@github/copilot-language-server 1.543.0 → 1.545.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +32 -0
- package/dist/contextWorker.js +1 -1
- package/dist/main.js +985 -979
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/cliVersion.js +31 -0
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/client.js +244 -108
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/extension.js +3 -1
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/ffiRuntimeHost.js +14 -19
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/generated/rpc.js +293 -22
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/index.js +2 -0
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/runtimeArtifacts.js +254 -0
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/session.js +144 -16
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/types.js +8 -0
- package/dist/node_modules/@github/copilot-sdk/package.json +103 -88
- package/package.json +14 -14
|
@@ -34,20 +34,19 @@ module.exports = __toCommonJS(client_exports);
|
|
|
34
34
|
var import_node_child_process = require("node:child_process");
|
|
35
35
|
var import_node_crypto = require("node:crypto");
|
|
36
36
|
var import_node_fs = require("node:fs");
|
|
37
|
-
var import_node_module = require("node:module");
|
|
38
37
|
var import_node_net = require("node:net");
|
|
39
38
|
var import_node_path = require("node:path");
|
|
40
|
-
var import_node_url = require("node:url");
|
|
41
39
|
var import_node = require("vscode-jsonrpc/node.js");
|
|
42
40
|
var import_rpc = require("./generated/rpc.js");
|
|
43
41
|
var import_sdkProtocolVersion = require("./sdkProtocolVersion.js");
|
|
44
42
|
var import_session = require("./session.js");
|
|
43
|
+
var import_runtimeArtifacts = require("./runtimeArtifacts.js");
|
|
44
|
+
var import_cliVersion = require("./cliVersion.js");
|
|
45
45
|
var import_sessionFsProvider = require("./sessionFsProvider.js");
|
|
46
46
|
var import_copilotRequestHandler = require("./copilotRequestHandler.js");
|
|
47
47
|
var import_telemetry = require("./telemetry.js");
|
|
48
48
|
var import_toolSet = require("./toolSet.js");
|
|
49
49
|
var import_types = require("./types.js");
|
|
50
|
-
const import_meta = {};
|
|
51
50
|
const MIN_PROTOCOL_VERSION = 3;
|
|
52
51
|
const RUNTIME_SHUTDOWN_TIMEOUT_MS = 1e4;
|
|
53
52
|
function isZodSchema(value) {
|
|
@@ -72,7 +71,7 @@ async function waitForChildExit(child, timeoutMs) {
|
|
|
72
71
|
if (child.exitCode != null || child.signalCode != null) {
|
|
73
72
|
return true;
|
|
74
73
|
}
|
|
75
|
-
return new Promise((
|
|
74
|
+
return new Promise((resolve2) => {
|
|
76
75
|
let timeout;
|
|
77
76
|
let settled = false;
|
|
78
77
|
const onExit = () => {
|
|
@@ -81,7 +80,7 @@ async function waitForChildExit(child, timeoutMs) {
|
|
|
81
80
|
}
|
|
82
81
|
settled = true;
|
|
83
82
|
clearTimeout(timeout);
|
|
84
|
-
|
|
83
|
+
resolve2(true);
|
|
85
84
|
};
|
|
86
85
|
timeout = setTimeout(() => {
|
|
87
86
|
if (settled) {
|
|
@@ -89,7 +88,7 @@ async function waitForChildExit(child, timeoutMs) {
|
|
|
89
88
|
}
|
|
90
89
|
settled = true;
|
|
91
90
|
child.off("exit", onExit);
|
|
92
|
-
|
|
91
|
+
resolve2(false);
|
|
93
92
|
}, timeoutMs);
|
|
94
93
|
child.once("exit", onExit);
|
|
95
94
|
if (child.exitCode != null || child.signalCode != null) {
|
|
@@ -150,6 +149,15 @@ function toWireCustomAgents(agents) {
|
|
|
150
149
|
return { ...rest, mcpServers: toWireMcpServers(mcpServers) };
|
|
151
150
|
});
|
|
152
151
|
}
|
|
152
|
+
function clientInfoToWire(info) {
|
|
153
|
+
if (info == null) return void 0;
|
|
154
|
+
const wire = {};
|
|
155
|
+
if (info.applicationName) wire.editorName = info.applicationName;
|
|
156
|
+
if (info.applicationVersion) wire.editorVersion = info.applicationVersion;
|
|
157
|
+
if (info.integrationName) wire.extensionName = info.integrationName;
|
|
158
|
+
if (info.integrationVersion) wire.extensionVersion = info.integrationVersion;
|
|
159
|
+
return Object.keys(wire).length > 0 ? wire : void 0;
|
|
160
|
+
}
|
|
153
161
|
function toWireLargeOutput(config) {
|
|
154
162
|
if (!config) return void 0;
|
|
155
163
|
const { outputDirectory, ...rest } = config;
|
|
@@ -207,39 +215,8 @@ function getNodeExecPath() {
|
|
|
207
215
|
}
|
|
208
216
|
return process.execPath;
|
|
209
217
|
}
|
|
210
|
-
function
|
|
211
|
-
|
|
212
|
-
const variants = process.platform === "linux" ? ["linux", "linuxmusl"] : [process.platform];
|
|
213
|
-
return variants.map((variant) => `@github/copilot-${variant}-${arch}`);
|
|
214
|
-
}
|
|
215
|
-
function getBundledCliPath() {
|
|
216
|
-
const packageNames = getCliPlatformPackageNames();
|
|
217
|
-
if (typeof import_meta.resolve === "function") {
|
|
218
|
-
for (const packageName of packageNames) {
|
|
219
|
-
try {
|
|
220
|
-
const sdkUrl = import_meta.resolve(`${packageName}/sdk`);
|
|
221
|
-
const sdkPath = (0, import_node_url.fileURLToPath)(sdkUrl);
|
|
222
|
-
return (0, import_node_path.join)((0, import_node_path.dirname)((0, import_node_path.dirname)(sdkPath)), "index.js");
|
|
223
|
-
} catch {
|
|
224
|
-
}
|
|
225
|
-
}
|
|
226
|
-
throw new Error(
|
|
227
|
-
`Could not resolve a @github/copilot platform package (tried ${packageNames.join(", ")}). Ensure @github/copilot is installed, or pass cliPath/cliUrl to CopilotClient.`
|
|
228
|
-
);
|
|
229
|
-
}
|
|
230
|
-
const req = (0, import_node_module.createRequire)(__filename);
|
|
231
|
-
const searchPaths = req.resolve.paths("@github/copilot") ?? [];
|
|
232
|
-
for (const base of searchPaths) {
|
|
233
|
-
for (const packageName of packageNames) {
|
|
234
|
-
const candidate = (0, import_node_path.join)(base, ...packageName.split("/"), "index.js");
|
|
235
|
-
if ((0, import_node_fs.existsSync)(candidate)) {
|
|
236
|
-
return candidate;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
throw new Error(
|
|
241
|
-
`Could not find a @github/copilot platform package (tried ${packageNames.join(", ")}). Searched ${searchPaths.length} paths. Ensure @github/copilot is installed, or pass cliPath/cliUrl to CopilotClient.`
|
|
242
|
-
);
|
|
218
|
+
function getBundledRuntimePath() {
|
|
219
|
+
return (0, import_runtimeArtifacts.ensureRuntimeBundle)(import_cliVersion.COPILOT_CLI_VERSION);
|
|
243
220
|
}
|
|
244
221
|
class TeardownResilientStreamMessageWriter extends import_node.StreamMessageWriter {
|
|
245
222
|
suppressWriteErrors = false;
|
|
@@ -259,6 +236,7 @@ class CopilotClient {
|
|
|
259
236
|
ffiHost = null;
|
|
260
237
|
connection = null;
|
|
261
238
|
messageWriter = null;
|
|
239
|
+
connectionClosed = false;
|
|
262
240
|
socket = null;
|
|
263
241
|
runtimePort = null;
|
|
264
242
|
actualHost = "localhost";
|
|
@@ -287,6 +265,7 @@ class CopilotClient {
|
|
|
287
265
|
_internalRpc = null;
|
|
288
266
|
processExitPromise = null;
|
|
289
267
|
// Rejects when CLI process exits
|
|
268
|
+
processTransportError = null;
|
|
290
269
|
negotiatedProtocolVersion = null;
|
|
291
270
|
/** Connection-level session filesystem config, set via constructor option. */
|
|
292
271
|
sessionFsConfig = null;
|
|
@@ -294,6 +273,7 @@ class CopilotClient {
|
|
|
294
273
|
builtinPluginDirectories = [];
|
|
295
274
|
onGitHubTelemetry;
|
|
296
275
|
clientGlobalHandlers = {};
|
|
276
|
+
githubTokenProviders = /* @__PURE__ */ new Map();
|
|
297
277
|
/**
|
|
298
278
|
* Typed server-scoped RPC methods.
|
|
299
279
|
* @throws Error if the client is not connected
|
|
@@ -452,7 +432,12 @@ class CopilotClient {
|
|
|
452
432
|
const connEnv = conn.kind === "stdio" || conn.kind === "tcp" ? conn.env : void 0;
|
|
453
433
|
const effectiveEnv = connEnv ?? options.env ?? process.env;
|
|
454
434
|
this.resolvedEnv = effectiveEnv;
|
|
455
|
-
|
|
435
|
+
if (conn.kind === "stdio" || conn.kind === "tcp") {
|
|
436
|
+
const explicitCliPath = conn.path ?? effectiveEnv.COPILOT_CLI_PATH;
|
|
437
|
+
if (explicitCliPath) {
|
|
438
|
+
this.resolvedCliPath = explicitCliPath;
|
|
439
|
+
}
|
|
440
|
+
}
|
|
456
441
|
const connArgs = conn.kind === "stdio" || conn.kind === "tcp" ? conn.args ?? [] : [];
|
|
457
442
|
this.connectionExtraArgs = [...connArgs];
|
|
458
443
|
this.options = {
|
|
@@ -465,7 +450,8 @@ class CopilotClient {
|
|
|
465
450
|
baseDirectory: options.baseDirectory,
|
|
466
451
|
sessionIdleTimeoutSeconds: options.sessionIdleTimeoutSeconds ?? 0,
|
|
467
452
|
enableRemoteSessions: options.enableRemoteSessions ?? false,
|
|
468
|
-
mode: options.mode ?? "copilot-cli"
|
|
453
|
+
mode: options.mode ?? "copilot-cli",
|
|
454
|
+
clientInfo: options.clientInfo
|
|
469
455
|
};
|
|
470
456
|
if (this.options.mode === "empty") {
|
|
471
457
|
const hasPersistence = this.options.baseDirectory !== void 0 || this.sessionFsConfig !== null || // External runtimes manage their own persistence layer; the SDK
|
|
@@ -481,17 +467,29 @@ class CopilotClient {
|
|
|
481
467
|
connectionExtraArgs = [];
|
|
482
468
|
/**
|
|
483
469
|
* Parse CLI URL into host and port
|
|
484
|
-
* Supports formats: "host:port", "http://host:port", "https://host:port", or just "port"
|
|
470
|
+
* Supports formats: "host:port", "[ipv6]:port", "http://host:port", "https://host:port", or just "port"
|
|
485
471
|
*/
|
|
486
472
|
parseCliUrl(url) {
|
|
487
|
-
|
|
473
|
+
const cleanUrl = url.replace(/^https?:\/\//, "");
|
|
488
474
|
if (/^\d+$/.test(cleanUrl)) {
|
|
489
475
|
return { host: "localhost", port: parseInt(cleanUrl, 10) };
|
|
490
476
|
}
|
|
477
|
+
const ipv6Match = cleanUrl.match(/^\[([^\]]+)\]:(\d+)$/);
|
|
478
|
+
if (ipv6Match) {
|
|
479
|
+
const host2 = ipv6Match[1];
|
|
480
|
+
if (!(0, import_node_net.isIPv6)(host2)) {
|
|
481
|
+
throw new Error(`Invalid cliUrl format: ${url}`);
|
|
482
|
+
}
|
|
483
|
+
const port2 = parseInt(ipv6Match[2], 10);
|
|
484
|
+
if (isNaN(port2) || port2 <= 0 || port2 > 65535) {
|
|
485
|
+
throw new Error(`Invalid port in cliUrl: ${url}`);
|
|
486
|
+
}
|
|
487
|
+
return { host: host2, port: port2 };
|
|
488
|
+
}
|
|
491
489
|
const parts = cleanUrl.split(":");
|
|
492
490
|
if (parts.length !== 2) {
|
|
493
491
|
throw new Error(
|
|
494
|
-
`Invalid cliUrl format: ${url}. Expected "host:port", "http://host:port", or "port"`
|
|
492
|
+
`Invalid cliUrl format: ${url}. Expected "host:port", "[ipv6]:port", "http://host:port", or "port"`
|
|
495
493
|
);
|
|
496
494
|
}
|
|
497
495
|
const host = parts[0] || "localhost";
|
|
@@ -551,8 +549,53 @@ class CopilotClient {
|
|
|
551
549
|
}
|
|
552
550
|
};
|
|
553
551
|
}
|
|
552
|
+
handlers.gitHubToken = {
|
|
553
|
+
getToken: (params) => this.acquireGitHubToken(params)
|
|
554
|
+
};
|
|
554
555
|
this.clientGlobalHandlers = handlers;
|
|
555
556
|
}
|
|
557
|
+
async acquireGitHubToken(params) {
|
|
558
|
+
const registration = this.githubTokenProviders.get(params.registrationId);
|
|
559
|
+
if (!registration) {
|
|
560
|
+
throw new Error(
|
|
561
|
+
`No GitHub token provider registered for registration ID "${params.registrationId}"`
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
return await registration.provider({
|
|
565
|
+
host: params.host,
|
|
566
|
+
sessionId: params.sessionId ?? registration.sessionId,
|
|
567
|
+
reason: params.reason
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
registerGitHubTokenProvider(provider, sessionId) {
|
|
571
|
+
if (!provider) {
|
|
572
|
+
return void 0;
|
|
573
|
+
}
|
|
574
|
+
const registrationId = (0, import_node_crypto.randomUUID)();
|
|
575
|
+
this.githubTokenProviders.set(registrationId, { provider, sessionId, committed: false });
|
|
576
|
+
return registrationId;
|
|
577
|
+
}
|
|
578
|
+
assignGitHubTokenProvider(registrationId, sessionId) {
|
|
579
|
+
if (!registrationId) {
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
const registration = this.githubTokenProviders.get(registrationId);
|
|
583
|
+
if (registration) {
|
|
584
|
+
registration.sessionId = sessionId;
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
commitGitHubTokenProvider(sessionId, registrationId) {
|
|
588
|
+
for (const [candidateId, registration2] of this.githubTokenProviders) {
|
|
589
|
+
if (registration2.sessionId === sessionId && registration2.committed) {
|
|
590
|
+
this.githubTokenProviders.delete(candidateId);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
const registration = registrationId ? this.githubTokenProviders.get(registrationId) : void 0;
|
|
594
|
+
if (registration) {
|
|
595
|
+
registration.sessionId = sessionId;
|
|
596
|
+
registration.committed = true;
|
|
597
|
+
}
|
|
598
|
+
}
|
|
556
599
|
/**
|
|
557
600
|
* Starts the CLI server and establishes a connection.
|
|
558
601
|
*
|
|
@@ -575,6 +618,9 @@ class CopilotClient {
|
|
|
575
618
|
if (this.state === "connected") {
|
|
576
619
|
return;
|
|
577
620
|
}
|
|
621
|
+
this.forceStopping = false;
|
|
622
|
+
this.connectionClosed = false;
|
|
623
|
+
this.processTransportError = null;
|
|
578
624
|
this.state = "connecting";
|
|
579
625
|
try {
|
|
580
626
|
if (this.connectionConfig.kind === "inprocess") {
|
|
@@ -607,8 +653,10 @@ class CopilotClient {
|
|
|
607
653
|
}
|
|
608
654
|
this.state = "connected";
|
|
609
655
|
} catch (error) {
|
|
656
|
+
const startupError = this.processTransportError ?? error;
|
|
657
|
+
await this.forceStop();
|
|
610
658
|
this.state = "error";
|
|
611
|
-
throw
|
|
659
|
+
throw startupError;
|
|
612
660
|
}
|
|
613
661
|
}
|
|
614
662
|
/**
|
|
@@ -653,7 +701,7 @@ class CopilotClient {
|
|
|
653
701
|
lastError = error instanceof Error ? error : new Error(String(error));
|
|
654
702
|
if (attempt < 3) {
|
|
655
703
|
const delay = 100 * Math.pow(2, attempt - 1);
|
|
656
|
-
await new Promise((
|
|
704
|
+
await new Promise((resolve2) => setTimeout(resolve2, delay));
|
|
657
705
|
}
|
|
658
706
|
}
|
|
659
707
|
}
|
|
@@ -669,7 +717,8 @@ class CopilotClient {
|
|
|
669
717
|
session._markDisconnected();
|
|
670
718
|
}
|
|
671
719
|
this.sessions.clear();
|
|
672
|
-
|
|
720
|
+
this.githubTokenProviders.clear();
|
|
721
|
+
if (this.connection && !this.connectionClosed && (this.cliProcess || this.ffiHost) && !this.isExternalServer) {
|
|
673
722
|
const runtimeShutdownStart = Date.now();
|
|
674
723
|
const shutdownPromise = this.rpc.runtime.shutdown();
|
|
675
724
|
void shutdownPromise.catch(() => void 0);
|
|
@@ -719,8 +768,8 @@ class CopilotClient {
|
|
|
719
768
|
this.socket = null;
|
|
720
769
|
try {
|
|
721
770
|
if (!socket.destroyed) {
|
|
722
|
-
await new Promise((
|
|
723
|
-
socket.once("close", () =>
|
|
771
|
+
await new Promise((resolve2) => {
|
|
772
|
+
socket.once("close", () => resolve2());
|
|
724
773
|
socket.end();
|
|
725
774
|
});
|
|
726
775
|
}
|
|
@@ -823,6 +872,7 @@ class CopilotClient {
|
|
|
823
872
|
session._markDisconnected();
|
|
824
873
|
}
|
|
825
874
|
this.sessions.clear();
|
|
875
|
+
this.githubTokenProviders.clear();
|
|
826
876
|
if (this.messageWriter) {
|
|
827
877
|
this.messageWriter.suppressWriteErrors = true;
|
|
828
878
|
}
|
|
@@ -988,7 +1038,8 @@ class CopilotClient {
|
|
|
988
1038
|
*
|
|
989
1039
|
* In empty mode, defaults the four overridable feature flags to safe values
|
|
990
1040
|
* (caller values from `config` win). `installedPlugins=[]` is unconditional
|
|
991
|
-
* in empty mode
|
|
1041
|
+
* in empty mode. `includedBuiltinSkills` defaults to `[]`, but callers can
|
|
1042
|
+
* explicitly allow selected runtime-bundled skills.
|
|
992
1043
|
*/
|
|
993
1044
|
async updateSessionOptionsForMode(session, config) {
|
|
994
1045
|
const patch = {};
|
|
@@ -998,6 +1049,7 @@ class CopilotClient {
|
|
|
998
1049
|
patch.coauthorEnabled = config.coauthorEnabled ?? false;
|
|
999
1050
|
patch.manageScheduleEnabled = config.manageScheduleEnabled ?? false;
|
|
1000
1051
|
patch.installedPlugins = [];
|
|
1052
|
+
patch.includedBuiltinSkills = config.includedBuiltinSkills ?? [];
|
|
1001
1053
|
} else {
|
|
1002
1054
|
if (config.skipCustomInstructions !== void 0)
|
|
1003
1055
|
patch.skipCustomInstructions = config.skipCustomInstructions;
|
|
@@ -1007,6 +1059,8 @@ class CopilotClient {
|
|
|
1007
1059
|
patch.coauthorEnabled = config.coauthorEnabled;
|
|
1008
1060
|
if (config.manageScheduleEnabled !== void 0)
|
|
1009
1061
|
patch.manageScheduleEnabled = config.manageScheduleEnabled;
|
|
1062
|
+
if (config.includedBuiltinSkills !== void 0)
|
|
1063
|
+
patch.includedBuiltinSkills = config.includedBuiltinSkills;
|
|
1010
1064
|
}
|
|
1011
1065
|
if (Object.keys(patch).length === 0) {
|
|
1012
1066
|
return;
|
|
@@ -1022,6 +1076,9 @@ class CopilotClient {
|
|
|
1022
1076
|
}
|
|
1023
1077
|
}
|
|
1024
1078
|
async createSession(config) {
|
|
1079
|
+
if (config.gitHubToken !== void 0 && config.gitHubTokenProvider !== void 0) {
|
|
1080
|
+
throw new Error("gitHubToken and gitHubTokenProvider are mutually exclusive");
|
|
1081
|
+
}
|
|
1025
1082
|
if (!this.connection) {
|
|
1026
1083
|
await this.start();
|
|
1027
1084
|
}
|
|
@@ -1032,6 +1089,11 @@ class CopilotClient {
|
|
|
1032
1089
|
const callerSessionId = config.sessionId;
|
|
1033
1090
|
const useServerGeneratedId = config.cloud != null && callerSessionId == null;
|
|
1034
1091
|
const localSessionId = useServerGeneratedId ? void 0 : callerSessionId ?? (0, import_node_crypto.randomUUID)();
|
|
1092
|
+
const toolFilterOptions = this.resolveToolFilterOptions(config);
|
|
1093
|
+
const gitHubTokenProviderRegistrationId = this.registerGitHubTokenProvider(
|
|
1094
|
+
config.gitHubTokenProvider,
|
|
1095
|
+
localSessionId
|
|
1096
|
+
);
|
|
1035
1097
|
const {
|
|
1036
1098
|
wireProvider: bearerWireProvider,
|
|
1037
1099
|
wireProviders: bearerWireProviders,
|
|
@@ -1048,7 +1110,10 @@ class CopilotClient {
|
|
|
1048
1110
|
this.onGetTraceContext,
|
|
1049
1111
|
{
|
|
1050
1112
|
mcpAuthHandler: config.onMcpAuthRequest,
|
|
1051
|
-
managedSettingsEnabled: config.enableManagedSettings === true || config.managedSettings !== void 0
|
|
1113
|
+
managedSettingsEnabled: config.enableManagedSettings === true || config.managedSettings !== void 0,
|
|
1114
|
+
onDisconnected: gitHubTokenProviderRegistrationId === void 0 ? void 0 : () => this.githubTokenProviders.delete(
|
|
1115
|
+
gitHubTokenProviderRegistrationId
|
|
1116
|
+
)
|
|
1052
1117
|
}
|
|
1053
1118
|
);
|
|
1054
1119
|
s.registerTools(config.tools);
|
|
@@ -1086,10 +1151,16 @@ class CopilotClient {
|
|
|
1086
1151
|
let session;
|
|
1087
1152
|
let registeredId;
|
|
1088
1153
|
if (localSessionId !== void 0) {
|
|
1089
|
-
|
|
1090
|
-
|
|
1154
|
+
try {
|
|
1155
|
+
session = initializeSession(localSessionId);
|
|
1156
|
+
registeredId = localSessionId;
|
|
1157
|
+
} catch (error) {
|
|
1158
|
+
if (gitHubTokenProviderRegistrationId !== void 0) {
|
|
1159
|
+
this.githubTokenProviders.delete(gitHubTokenProviderRegistrationId);
|
|
1160
|
+
}
|
|
1161
|
+
throw error;
|
|
1162
|
+
}
|
|
1091
1163
|
}
|
|
1092
|
-
const toolFilterOptions = this.resolveToolFilterOptions(config);
|
|
1093
1164
|
try {
|
|
1094
1165
|
const response = await this.connection.sendRequest("session.create", {
|
|
1095
1166
|
...await (0, import_telemetry.getTraceContext)(this.onGetTraceContext),
|
|
@@ -1119,7 +1190,7 @@ class CopilotClient {
|
|
|
1119
1190
|
canvasProvider: config.canvasProvider,
|
|
1120
1191
|
commands: config.commands?.map((cmd) => ({
|
|
1121
1192
|
name: cmd.name,
|
|
1122
|
-
description: cmd.description
|
|
1193
|
+
description: cmd.description ?? ""
|
|
1123
1194
|
})),
|
|
1124
1195
|
systemMessage: wireSystemMessage,
|
|
1125
1196
|
availableTools: toolFilterOptions.availableTools,
|
|
@@ -1139,6 +1210,7 @@ class CopilotClient {
|
|
|
1139
1210
|
requestPermission: !!config.onPermissionRequest,
|
|
1140
1211
|
requestUserInput: !!config.onUserInputRequest,
|
|
1141
1212
|
requestElicitation: !!config.onElicitationRequest,
|
|
1213
|
+
askUserVariant: config.askUserVariant,
|
|
1142
1214
|
...config.enableMcpApps ? { requestMcpApps: true } : {},
|
|
1143
1215
|
...config.githubMcpToolConfig != null ? { githubMcpToolConfig: config.githubMcpToolConfig } : {},
|
|
1144
1216
|
requestExitPlanMode: !!config.onExitPlanModeRequest,
|
|
@@ -1174,8 +1246,10 @@ class CopilotClient {
|
|
|
1174
1246
|
infiniteSessions: config.infiniteSessions,
|
|
1175
1247
|
memory: config.memory,
|
|
1176
1248
|
gitHubToken: config.gitHubToken,
|
|
1249
|
+
gitHubTokenProviderRegistrationId,
|
|
1177
1250
|
remoteSession: config.remoteSession,
|
|
1178
1251
|
cloud: config.cloud,
|
|
1252
|
+
featureFlags: config.featureFlags,
|
|
1179
1253
|
expAssignments: config.expAssignments,
|
|
1180
1254
|
enableManagedSettings: config.enableManagedSettings,
|
|
1181
1255
|
managedSettings: config.managedSettings
|
|
@@ -1197,6 +1271,7 @@ class CopilotClient {
|
|
|
1197
1271
|
session = initializeSession(returnedSessionId);
|
|
1198
1272
|
registeredId = returnedSessionId;
|
|
1199
1273
|
}
|
|
1274
|
+
this.assignGitHubTokenProvider(gitHubTokenProviderRegistrationId, returnedSessionId);
|
|
1200
1275
|
if (config.onMcpAuthRequest) {
|
|
1201
1276
|
await this.connection.sendRequest("session.eventLog.registerInterest", {
|
|
1202
1277
|
sessionId: returnedSessionId,
|
|
@@ -1206,10 +1281,15 @@ class CopilotClient {
|
|
|
1206
1281
|
session["_workspacePath"] = workspacePath;
|
|
1207
1282
|
session.setCapabilities(capabilities);
|
|
1208
1283
|
await this.updateSessionOptionsForMode(session, config);
|
|
1284
|
+
this.commitGitHubTokenProvider(returnedSessionId, gitHubTokenProviderRegistrationId);
|
|
1209
1285
|
} catch (e) {
|
|
1286
|
+
session?._markDisconnected();
|
|
1210
1287
|
if (registeredId !== void 0) {
|
|
1211
1288
|
this.sessions.delete(registeredId);
|
|
1212
1289
|
}
|
|
1290
|
+
if (gitHubTokenProviderRegistrationId !== void 0) {
|
|
1291
|
+
this.githubTokenProviders.delete(gitHubTokenProviderRegistrationId);
|
|
1292
|
+
}
|
|
1213
1293
|
throw e;
|
|
1214
1294
|
}
|
|
1215
1295
|
return session;
|
|
@@ -1242,10 +1322,13 @@ class CopilotClient {
|
|
|
1242
1322
|
return this.resumeSessionInternal(sessionId, config);
|
|
1243
1323
|
}
|
|
1244
1324
|
/** @internal */
|
|
1245
|
-
async resumeSessionForExtension(sessionId, config, factories) {
|
|
1246
|
-
return this.resumeSessionInternal(sessionId, config, factories);
|
|
1325
|
+
async resumeSessionForExtension(sessionId, config, factories, extensionOptions) {
|
|
1326
|
+
return this.resumeSessionInternal(sessionId, config, factories, extensionOptions);
|
|
1247
1327
|
}
|
|
1248
|
-
async resumeSessionInternal(sessionId, config, factories) {
|
|
1328
|
+
async resumeSessionInternal(sessionId, config, factories, extensionOptions) {
|
|
1329
|
+
if (config.gitHubToken !== void 0 && config.gitHubTokenProvider !== void 0) {
|
|
1330
|
+
throw new Error("gitHubToken and gitHubTokenProvider are mutually exclusive");
|
|
1331
|
+
}
|
|
1249
1332
|
if (!this.connection) {
|
|
1250
1333
|
await this.start();
|
|
1251
1334
|
}
|
|
@@ -1303,6 +1386,15 @@ class CopilotClient {
|
|
|
1303
1386
|
this.sessions.set(sessionId, session);
|
|
1304
1387
|
this.setupSessionFs(session, config);
|
|
1305
1388
|
const toolFilterOptions = this.resolveToolFilterOptions(config);
|
|
1389
|
+
const gitHubTokenProviderRegistrationId = this.registerGitHubTokenProvider(
|
|
1390
|
+
config.gitHubTokenProvider,
|
|
1391
|
+
sessionId
|
|
1392
|
+
);
|
|
1393
|
+
if (gitHubTokenProviderRegistrationId !== void 0) {
|
|
1394
|
+
session._setOnDisconnected(
|
|
1395
|
+
() => this.githubTokenProviders.delete(gitHubTokenProviderRegistrationId)
|
|
1396
|
+
);
|
|
1397
|
+
}
|
|
1306
1398
|
try {
|
|
1307
1399
|
const response = await this.connection.sendRequest("session.resume", {
|
|
1308
1400
|
...await (0, import_telemetry.getTraceContext)(this.onGetTraceContext),
|
|
@@ -1342,7 +1434,7 @@ class CopilotClient {
|
|
|
1342
1434
|
canvasProvider: config.canvasProvider,
|
|
1343
1435
|
commands: config.commands?.map((cmd) => ({
|
|
1344
1436
|
name: cmd.name,
|
|
1345
|
-
description: cmd.description
|
|
1437
|
+
description: cmd.description ?? ""
|
|
1346
1438
|
})),
|
|
1347
1439
|
provider: bearerWireProvider,
|
|
1348
1440
|
capi: config.capi,
|
|
@@ -1353,6 +1445,7 @@ class CopilotClient {
|
|
|
1353
1445
|
requestPermission: config.onPermissionRequest !== import_types.defaultJoinSessionPermissionHandler,
|
|
1354
1446
|
requestUserInput: !!config.onUserInputRequest,
|
|
1355
1447
|
requestElicitation: !!config.onElicitationRequest,
|
|
1448
|
+
askUserVariant: config.askUserVariant,
|
|
1356
1449
|
...config.enableMcpApps ? { requestMcpApps: true } : {},
|
|
1357
1450
|
...config.githubMcpToolConfig != null ? { githubMcpToolConfig: config.githubMcpToolConfig } : {},
|
|
1358
1451
|
requestExitPlanMode: !!config.onExitPlanModeRequest,
|
|
@@ -1390,12 +1483,26 @@ class CopilotClient {
|
|
|
1390
1483
|
disableResume: config.suppressResumeEvent,
|
|
1391
1484
|
continuePendingWork: config.continuePendingWork,
|
|
1392
1485
|
gitHubToken: config.gitHubToken,
|
|
1486
|
+
gitHubTokenProviderRegistrationId,
|
|
1393
1487
|
remoteSession: config.remoteSession,
|
|
1394
1488
|
openCanvases: config.openCanvases,
|
|
1489
|
+
featureFlags: config.featureFlags,
|
|
1395
1490
|
expAssignments: config.expAssignments,
|
|
1396
1491
|
enableManagedSettings: config.enableManagedSettings,
|
|
1397
|
-
managedSettings: config.managedSettings
|
|
1492
|
+
managedSettings: config.managedSettings,
|
|
1493
|
+
...extensionOptions?.requestedEnvironmentVariables ? {
|
|
1494
|
+
requestedEnvironmentVariables: extensionOptions.requestedEnvironmentVariables
|
|
1495
|
+
} : {}
|
|
1398
1496
|
});
|
|
1497
|
+
if (extensionOptions?.requestedEnvironmentVariables) {
|
|
1498
|
+
const requested = new Set(extensionOptions.requestedEnvironmentVariables);
|
|
1499
|
+
const { grantedEnvironmentVariables } = response;
|
|
1500
|
+
for (const [name, value] of Object.entries(grantedEnvironmentVariables ?? {})) {
|
|
1501
|
+
if (requested.has(name)) {
|
|
1502
|
+
process.env[name] = value;
|
|
1503
|
+
}
|
|
1504
|
+
}
|
|
1505
|
+
}
|
|
1399
1506
|
const { workspacePath, capabilities, openCanvases } = response;
|
|
1400
1507
|
session["_workspacePath"] = workspacePath;
|
|
1401
1508
|
session.setCapabilities(capabilities);
|
|
@@ -1407,8 +1514,13 @@ class CopilotClient {
|
|
|
1407
1514
|
});
|
|
1408
1515
|
}
|
|
1409
1516
|
await this.updateSessionOptionsForMode(session, config);
|
|
1517
|
+
this.commitGitHubTokenProvider(sessionId, gitHubTokenProviderRegistrationId);
|
|
1410
1518
|
} catch (e) {
|
|
1519
|
+
session._markDisconnected();
|
|
1411
1520
|
this.sessions.delete(sessionId);
|
|
1521
|
+
if (gitHubTokenProviderRegistrationId !== void 0) {
|
|
1522
|
+
this.githubTokenProviders.delete(gitHubTokenProviderRegistrationId);
|
|
1523
|
+
}
|
|
1412
1524
|
throw e;
|
|
1413
1525
|
}
|
|
1414
1526
|
return session;
|
|
@@ -1467,8 +1579,8 @@ class CopilotClient {
|
|
|
1467
1579
|
async listModels() {
|
|
1468
1580
|
await this.modelsCacheLock;
|
|
1469
1581
|
let resolveLock;
|
|
1470
|
-
this.modelsCacheLock = new Promise((
|
|
1471
|
-
resolveLock =
|
|
1582
|
+
this.modelsCacheLock = new Promise((resolve2) => {
|
|
1583
|
+
resolveLock = resolve2;
|
|
1472
1584
|
});
|
|
1473
1585
|
try {
|
|
1474
1586
|
if (this.modelsCache !== null) {
|
|
@@ -1520,10 +1632,17 @@ class CopilotClient {
|
|
|
1520
1632
|
const raceAgainstExit = (p) => this.processExitPromise ? Promise.race([p, this.processExitPromise]) : p;
|
|
1521
1633
|
let serverVersion;
|
|
1522
1634
|
try {
|
|
1523
|
-
const connectParams = {
|
|
1635
|
+
const connectParams = {
|
|
1636
|
+
token: this.effectiveConnectionToken,
|
|
1637
|
+
supportedTaskKinds: ["agent", "client", "shell"]
|
|
1638
|
+
};
|
|
1524
1639
|
if (this.onGitHubTelemetry != null) {
|
|
1525
1640
|
connectParams.enableGitHubTelemetryForwarding = true;
|
|
1526
1641
|
}
|
|
1642
|
+
const clientInfo = clientInfoToWire(this.options.clientInfo);
|
|
1643
|
+
if (clientInfo != null) {
|
|
1644
|
+
connectParams.clientInfo = clientInfo;
|
|
1645
|
+
}
|
|
1527
1646
|
const result = await raceAgainstExit(this.internalRpc.connect(connectParams));
|
|
1528
1647
|
serverVersion = result.protocolVersion;
|
|
1529
1648
|
} catch (err) {
|
|
@@ -1597,7 +1716,9 @@ class CopilotClient {
|
|
|
1597
1716
|
if (!success) {
|
|
1598
1717
|
throw new Error(`Failed to delete session ${sessionId}: ${error || "Unknown error"}`);
|
|
1599
1718
|
}
|
|
1719
|
+
const session = this.sessions.get(sessionId);
|
|
1600
1720
|
this.sessions.delete(sessionId);
|
|
1721
|
+
session?._runOnDisconnected();
|
|
1601
1722
|
}
|
|
1602
1723
|
/**
|
|
1603
1724
|
* List all available sessions.
|
|
@@ -1782,7 +1903,8 @@ class CopilotClient {
|
|
|
1782
1903
|
* Start the CLI server process
|
|
1783
1904
|
*/
|
|
1784
1905
|
async startCLIServer() {
|
|
1785
|
-
|
|
1906
|
+
this.resolvedCliPath ??= await getBundledRuntimePath();
|
|
1907
|
+
return new Promise((resolve2, reject) => {
|
|
1786
1908
|
this.stderrBuffer = "";
|
|
1787
1909
|
const args = [...this.connectionExtraArgs, "--headless", "--no-auto-update"];
|
|
1788
1910
|
if (this.options.logLevel) {
|
|
@@ -1819,7 +1941,7 @@ class CopilotClient {
|
|
|
1819
1941
|
}
|
|
1820
1942
|
if (!(0, import_node_fs.existsSync)(this.resolvedCliPath)) {
|
|
1821
1943
|
throw new Error(
|
|
1822
|
-
`Copilot CLI not found at ${this.resolvedCliPath}.
|
|
1944
|
+
`Copilot CLI not found at ${this.resolvedCliPath}. Set COPILOT_CLI_PATH to use a custom installation.`
|
|
1823
1945
|
);
|
|
1824
1946
|
}
|
|
1825
1947
|
const stdioConfig = this.connectionConfig.kind === "stdio" ? ["pipe", "pipe", "pipe"] : ["ignore", "pipe", "pipe"];
|
|
@@ -1843,7 +1965,7 @@ class CopilotClient {
|
|
|
1843
1965
|
let resolved = false;
|
|
1844
1966
|
if (this.connectionConfig.kind === "stdio") {
|
|
1845
1967
|
resolved = true;
|
|
1846
|
-
|
|
1968
|
+
resolve2();
|
|
1847
1969
|
} else {
|
|
1848
1970
|
this.cliProcess.stdout?.on("data", (data) => {
|
|
1849
1971
|
stdout += data.toString();
|
|
@@ -1851,7 +1973,7 @@ class CopilotClient {
|
|
|
1851
1973
|
if (match && !resolved) {
|
|
1852
1974
|
this.runtimePort = parseInt(match[1], 10);
|
|
1853
1975
|
resolved = true;
|
|
1854
|
-
|
|
1976
|
+
resolve2();
|
|
1855
1977
|
}
|
|
1856
1978
|
});
|
|
1857
1979
|
}
|
|
@@ -1883,26 +2005,27 @@ stderr: ${stderrOutput}`
|
|
|
1883
2005
|
});
|
|
1884
2006
|
this.processExitPromise = new Promise((_, rejectProcessExit) => {
|
|
1885
2007
|
this.cliProcess.on("exit", (code) => {
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
2008
|
+
if (this.messageWriter) {
|
|
2009
|
+
this.messageWriter.suppressWriteErrors = true;
|
|
2010
|
+
}
|
|
2011
|
+
const stderrOutput = this.stderrBuffer.trim();
|
|
2012
|
+
if (stderrOutput) {
|
|
2013
|
+
rejectProcessExit(
|
|
2014
|
+
new Error(
|
|
2015
|
+
`CLI server exited with code ${code}
|
|
1892
2016
|
stderr: ${stderrOutput}`
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
}, 50);
|
|
2017
|
+
)
|
|
2018
|
+
);
|
|
2019
|
+
} else {
|
|
2020
|
+
rejectProcessExit(
|
|
2021
|
+
new Error(`CLI server exited unexpectedly with code ${code}`)
|
|
2022
|
+
);
|
|
2023
|
+
}
|
|
1901
2024
|
});
|
|
1902
2025
|
});
|
|
1903
2026
|
this.processExitPromise.catch(() => {
|
|
1904
2027
|
});
|
|
1905
|
-
this.cliProcess.on("
|
|
2028
|
+
this.cliProcess.on("close", (code) => {
|
|
1906
2029
|
if (!resolved) {
|
|
1907
2030
|
resolved = true;
|
|
1908
2031
|
const stderrOutput = this.stderrBuffer.trim();
|
|
@@ -1944,7 +2067,20 @@ stderr: ${stderrOutput}`
|
|
|
1944
2067
|
}
|
|
1945
2068
|
/** Starts the in-process FFI runtime with SDK-managed typed options. */
|
|
1946
2069
|
async startInProcessFfi() {
|
|
1947
|
-
const
|
|
2070
|
+
const explicitEntrypoint = this.resolvedEnv.COPILOT_CLI_PATH;
|
|
2071
|
+
let runtimeLibrary;
|
|
2072
|
+
if (explicitEntrypoint) {
|
|
2073
|
+
const entrypointDirectory = (0, import_node_path.dirname)((0, import_node_path.resolve)(explicitEntrypoint));
|
|
2074
|
+
const adjacentRuntime = (0, import_node_path.join)(entrypointDirectory, "runtime.node");
|
|
2075
|
+
runtimeLibrary = (0, import_node_fs.existsSync)(adjacentRuntime) ? adjacentRuntime : (0, import_node_path.join)(
|
|
2076
|
+
entrypointDirectory,
|
|
2077
|
+
"prebuilds",
|
|
2078
|
+
CopilotClient.getNapiPrebuildsFolder(explicitEntrypoint),
|
|
2079
|
+
"runtime.node"
|
|
2080
|
+
);
|
|
2081
|
+
} else {
|
|
2082
|
+
runtimeLibrary = (0, import_node_path.join)((0, import_node_path.dirname)(await getBundledRuntimePath()), "runtime.node");
|
|
2083
|
+
}
|
|
1948
2084
|
const { FfiRuntimeHost } = await import("./ffiRuntimeHost.js");
|
|
1949
2085
|
const environment = {};
|
|
1950
2086
|
if (this.options.gitHubToken) {
|
|
@@ -1972,12 +2108,7 @@ stderr: ${stderrOutput}`
|
|
|
1972
2108
|
if (this.options.enableRemoteSessions) {
|
|
1973
2109
|
args.push("--remote");
|
|
1974
2110
|
}
|
|
1975
|
-
const host = FfiRuntimeHost.create(
|
|
1976
|
-
entrypoint,
|
|
1977
|
-
CopilotClient.getNapiPrebuildsFolder(entrypoint),
|
|
1978
|
-
environment,
|
|
1979
|
-
args
|
|
1980
|
-
);
|
|
2111
|
+
const host = FfiRuntimeHost.create(runtimeLibrary, explicitEntrypoint, environment, args);
|
|
1981
2112
|
this.ffiHost = host;
|
|
1982
2113
|
await host.start();
|
|
1983
2114
|
}
|
|
@@ -1997,19 +2128,6 @@ stderr: ${stderrOutput}`
|
|
|
1997
2128
|
this.attachConnectionHandlers();
|
|
1998
2129
|
this.connection.listen();
|
|
1999
2130
|
}
|
|
2000
|
-
/**
|
|
2001
|
-
* Resolves the CLI entrypoint used for in-process FFI hosting: `COPILOT_CLI_PATH`
|
|
2002
|
-
* when set, otherwise the bundled platform-package entrypoint.
|
|
2003
|
-
*/
|
|
2004
|
-
resolveCliPathForFfi() {
|
|
2005
|
-
return this.resolvedEnv.COPILOT_CLI_PATH ?? getBundledCliPath();
|
|
2006
|
-
}
|
|
2007
|
-
/**
|
|
2008
|
-
* Returns the napi prebuilds folder name for the current host — the
|
|
2009
|
-
* `<node-platform>-<arch>` convention (e.g. `win32-x64`, `darwin-arm64`,
|
|
2010
|
-
* `linux-x64`, `linuxmusl-x64`) under which the runtime ships
|
|
2011
|
-
* `prebuilds/<folder>/runtime.node`.
|
|
2012
|
-
*/
|
|
2013
2131
|
static getNapiPrebuildsFolder(entrypoint) {
|
|
2014
2132
|
const arch = process.arch;
|
|
2015
2133
|
if (arch !== "x64" && arch !== "arm64") {
|
|
@@ -2045,6 +2163,11 @@ stderr: ${stderrOutput}`
|
|
|
2045
2163
|
}
|
|
2046
2164
|
this.state = "error";
|
|
2047
2165
|
const reason = err instanceof Error ? err.stack ?? err.message : String(err);
|
|
2166
|
+
const stderrOutput = this.stderrBuffer.trim();
|
|
2167
|
+
this.processTransportError = new Error(
|
|
2168
|
+
`CLI server connection failed: ${reason}${stderrOutput ? `
|
|
2169
|
+
stderr: ${stderrOutput}` : ""}`
|
|
2170
|
+
);
|
|
2048
2171
|
this.logDebug(`stdin pipe error: ${reason}`);
|
|
2049
2172
|
try {
|
|
2050
2173
|
this.connection?.dispose();
|
|
@@ -2081,7 +2204,7 @@ stderr: ${stderrOutput}`
|
|
|
2081
2204
|
if (!this.runtimePort) {
|
|
2082
2205
|
throw new Error("Server port not available");
|
|
2083
2206
|
}
|
|
2084
|
-
return new Promise((
|
|
2207
|
+
return new Promise((resolve2, reject) => {
|
|
2085
2208
|
this.socket = new import_node_net.Socket();
|
|
2086
2209
|
const connectionTimeout = setTimeout(() => {
|
|
2087
2210
|
this.socket?.destroy();
|
|
@@ -2096,7 +2219,7 @@ stderr: ${stderrOutput}`
|
|
|
2096
2219
|
);
|
|
2097
2220
|
this.attachConnectionHandlers();
|
|
2098
2221
|
this.connection.listen();
|
|
2099
|
-
|
|
2222
|
+
resolve2();
|
|
2100
2223
|
});
|
|
2101
2224
|
this.socket.on("error", (error) => {
|
|
2102
2225
|
clearTimeout(connectionTimeout);
|
|
@@ -2143,11 +2266,24 @@ stderr: ${stderrOutput}`
|
|
|
2143
2266
|
return await this.handleHooksInvoke(params);
|
|
2144
2267
|
}
|
|
2145
2268
|
);
|
|
2146
|
-
this.connection
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2269
|
+
const connection = this.connection;
|
|
2270
|
+
const markDisconnected = () => {
|
|
2271
|
+
if (this.connection !== connection) {
|
|
2272
|
+
return;
|
|
2273
|
+
}
|
|
2274
|
+
this.connectionClosed = true;
|
|
2150
2275
|
this.state = "disconnected";
|
|
2276
|
+
for (const session of this.sessions.values()) {
|
|
2277
|
+
session._markDisconnected();
|
|
2278
|
+
}
|
|
2279
|
+
this.sessions.clear();
|
|
2280
|
+
this.githubTokenProviders.clear();
|
|
2281
|
+
};
|
|
2282
|
+
this.connection.onClose(markDisconnected);
|
|
2283
|
+
this.connection.onError(() => {
|
|
2284
|
+
if (this.connection === connection) {
|
|
2285
|
+
this.state = "disconnected";
|
|
2286
|
+
}
|
|
2151
2287
|
});
|
|
2152
2288
|
}
|
|
2153
2289
|
handleSessionEventNotification(notification) {
|