@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.
- package/README.md +94 -1
- package/dist/contextWorker.js +14 -14
- package/dist/main.js +1030 -1020
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/cliVersion.js +1 -1
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/client.js +507 -37
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/copilotRequestHandler.js +156 -20
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/extension.js +13 -9
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/ffiRuntimeHost.js +14 -3
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/generated/rpc.js +1366 -304
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/host.js +67 -0
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/index.js +12 -7
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/installationConfirmation.js +98 -0
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/runtimeArtifacts.js +1 -0
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/schema.js +40 -0
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/session.js +474 -151
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/sessionFsProvider.js +57 -0
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/types.js +5 -2
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/{factory.js → workflow.js} +33 -33
- package/dist/node_modules/@github/copilot-sdk/package.json +27 -16
- package/dist/node_modules/zod/locales/package.json +1 -2
- package/dist/node_modules/zod/mini/package.json +1 -2
- package/dist/node_modules/zod/package.json +1 -1
- package/dist/node_modules/zod/v3/package.json +1 -2
- package/dist/node_modules/zod/v4/classic/errors.js +2 -2
- package/dist/node_modules/zod/v4/classic/from-json-schema.cjs +16 -31
- package/dist/node_modules/zod/v4/classic/from-json-schema.js +16 -31
- package/dist/node_modules/zod/v4/classic/package.json +1 -2
- package/dist/node_modules/zod/v4/classic/schemas.cjs +119 -358
- package/dist/node_modules/zod/v4/classic/schemas.js +118 -356
- package/dist/node_modules/zod/v4/core/api.cjs +2 -7
- package/dist/node_modules/zod/v4/core/api.js +2 -7
- package/dist/node_modules/zod/v4/core/core.cjs +1 -3
- package/dist/node_modules/zod/v4/core/core.js +2 -4
- package/dist/node_modules/zod/v4/core/errors.cjs +23 -26
- package/dist/node_modules/zod/v4/core/errors.js +23 -26
- package/dist/node_modules/zod/v4/core/json-schema-processors.cjs +20 -16
- package/dist/node_modules/zod/v4/core/json-schema-processors.js +20 -16
- package/dist/node_modules/zod/v4/core/package.json +1 -2
- package/dist/node_modules/zod/v4/core/parse.cjs +7 -7
- package/dist/node_modules/zod/v4/core/parse.js +7 -7
- package/dist/node_modules/zod/v4/core/regexes.cjs +3 -9
- package/dist/node_modules/zod/v4/core/regexes.js +1 -7
- package/dist/node_modules/zod/v4/core/schemas.cjs +65 -211
- package/dist/node_modules/zod/v4/core/schemas.js +64 -210
- package/dist/node_modules/zod/v4/core/to-json-schema.cjs +1 -12
- package/dist/node_modules/zod/v4/core/to-json-schema.js +1 -12
- package/dist/node_modules/zod/v4/core/util.cjs +30 -54
- package/dist/node_modules/zod/v4/core/util.js +32 -55
- package/dist/node_modules/zod/v4/core/versions.cjs +2 -2
- package/dist/node_modules/zod/v4/core/versions.js +2 -2
- package/dist/node_modules/zod/v4/locales/en.cjs +0 -4
- package/dist/node_modules/zod/v4/locales/en.js +0 -4
- package/dist/node_modules/zod/v4/locales/fr.cjs +7 -24
- package/dist/node_modules/zod/v4/locales/fr.js +7 -24
- package/dist/node_modules/zod/v4/locales/index.cjs +1 -8
- package/dist/node_modules/zod/v4/locales/index.js +0 -3
- package/dist/node_modules/zod/v4/locales/it.cjs +1 -1
- package/dist/node_modules/zod/v4/locales/it.js +1 -1
- package/dist/node_modules/zod/v4/locales/ka.cjs +8 -8
- package/dist/node_modules/zod/v4/locales/ka.js +8 -8
- package/dist/node_modules/zod/v4/locales/package.json +1 -2
- package/dist/node_modules/zod/v4/locales/uz.cjs +0 -1
- package/dist/node_modules/zod/v4/locales/uz.js +0 -1
- package/dist/node_modules/zod/v4/mini/package.json +1 -2
- package/dist/node_modules/zod/v4/mini/schemas.cjs +4 -41
- package/dist/node_modules/zod/v4/mini/schemas.js +4 -40
- package/dist/node_modules/zod/v4/package.json +1 -2
- package/dist/node_modules/zod/v4-mini/package.json +1 -2
- package/package.json +20 -20
- package/dist/node_modules/zod/v4/locales/el.cjs +0 -136
- package/dist/node_modules/zod/v4/locales/el.js +0 -109
- package/dist/node_modules/zod/v4/locales/hr.cjs +0 -149
- package/dist/node_modules/zod/v4/locales/hr.js +0 -122
- package/dist/node_modules/zod/v4/locales/ro.cjs +0 -146
- 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
|
-
|
|
53
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
785
|
-
|
|
786
|
-
|
|
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.
|
|
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,
|
|
1341
|
-
return this.resumeSessionInternal(sessionId, config,
|
|
1485
|
+
async resumeSessionForExtension(sessionId, config, contributions = {}, extensionOptions) {
|
|
1486
|
+
return this.resumeSessionInternal(sessionId, config, contributions, extensionOptions);
|
|
1342
1487
|
}
|
|
1343
|
-
async resumeSessionInternal(sessionId, config,
|
|
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.
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 (
|
|
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
|
-
|
|
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");
|