@github/copilot-language-server 1.551.2 → 1.551.3
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/dist/main.js +657 -657
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/cliVersion.js +1 -1
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/client.js +499 -36
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/copilotRequestHandler.js +156 -20
- package/dist/node_modules/@github/copilot-sdk/dist/cjs/extension.js +11 -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 +1305 -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,
|
|
@@ -1236,6 +1355,7 @@ class CopilotClient {
|
|
|
1236
1355
|
includeSubAgentStreamingEvents: config.includeSubAgentStreamingEvents ?? true,
|
|
1237
1356
|
...this.onGitHubTelemetry != null ? { enableGitHubTelemetryForwarding: true } : {},
|
|
1238
1357
|
mcpServers: toWireMcpServers(config.mcpServers),
|
|
1358
|
+
...config.diagnostics !== void 0 ? { diagnostics: config.diagnostics } : {},
|
|
1239
1359
|
mcpOAuthTokenStorage: config.mcpOAuthTokenStorage,
|
|
1240
1360
|
authClientIdMetadataUrl: config.authClientIdMetadataUrl,
|
|
1241
1361
|
envValueMode: "direct",
|
|
@@ -1245,6 +1365,7 @@ class CopilotClient {
|
|
|
1245
1365
|
agent: config.agent,
|
|
1246
1366
|
configDir: config.configDirectory,
|
|
1247
1367
|
enableConfigDiscovery: config.enableConfigDiscovery,
|
|
1368
|
+
refreshCustomInstructions: config.refreshCustomInstructions,
|
|
1248
1369
|
skipEmbeddingRetrieval: config.skipEmbeddingRetrieval,
|
|
1249
1370
|
embeddingCacheStorage: config.embeddingCacheStorage,
|
|
1250
1371
|
organizationCustomInstructions: config.organizationCustomInstructions,
|
|
@@ -1254,6 +1375,7 @@ class CopilotClient {
|
|
|
1254
1375
|
enableSessionStore: config.enableSessionStore,
|
|
1255
1376
|
enableSkills: config.enableSkills,
|
|
1256
1377
|
skillDirectories: config.skillDirectories,
|
|
1378
|
+
...config.skillProvider ? { hasSkillProvider: true } : {},
|
|
1257
1379
|
pluginDirectories: config.pluginDirectories,
|
|
1258
1380
|
instructionDirectories: config.instructionDirectories,
|
|
1259
1381
|
disabledSkills: config.disabledSkills,
|
|
@@ -1267,6 +1389,7 @@ class CopilotClient {
|
|
|
1267
1389
|
featureFlags: config.featureFlags,
|
|
1268
1390
|
expAssignments: config.expAssignments,
|
|
1269
1391
|
enableManagedSettings: config.enableManagedSettings,
|
|
1392
|
+
enforceManagedModelDefaults: config.enforceManagedModelDefaults,
|
|
1270
1393
|
managedSettings: config.managedSettings
|
|
1271
1394
|
});
|
|
1272
1395
|
const {
|
|
@@ -1283,7 +1406,9 @@ class CopilotClient {
|
|
|
1283
1406
|
);
|
|
1284
1407
|
}
|
|
1285
1408
|
if (session === void 0) {
|
|
1409
|
+
uninitializedCloudSessionId = returnedSessionId;
|
|
1286
1410
|
session = initializeSession(returnedSessionId);
|
|
1411
|
+
uninitializedCloudSessionId = void 0;
|
|
1287
1412
|
registeredId = returnedSessionId;
|
|
1288
1413
|
}
|
|
1289
1414
|
this.assignGitHubTokenProvider(gitHubTokenProviderRegistrationId, returnedSessionId);
|
|
@@ -1305,8 +1430,27 @@ class CopilotClient {
|
|
|
1305
1430
|
if (gitHubTokenProviderRegistrationId !== void 0) {
|
|
1306
1431
|
this.githubTokenProviders.delete(gitHubTokenProviderRegistrationId);
|
|
1307
1432
|
}
|
|
1433
|
+
if (uninitializedCloudSessionId !== void 0) {
|
|
1434
|
+
try {
|
|
1435
|
+
await withTimeout(
|
|
1436
|
+
this.deleteSession(uninitializedCloudSessionId),
|
|
1437
|
+
CLOUD_SESSION_CLEANUP_TIMEOUT_MS,
|
|
1438
|
+
`session.delete timed out after ${CLOUD_SESSION_CLEANUP_TIMEOUT_MS}ms`
|
|
1439
|
+
);
|
|
1440
|
+
} catch (cleanupError) {
|
|
1441
|
+
throw new AggregateError(
|
|
1442
|
+
[e, cleanupError],
|
|
1443
|
+
"Failed to initialize and delete cloud session"
|
|
1444
|
+
);
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1308
1447
|
throw e;
|
|
1309
1448
|
}
|
|
1449
|
+
for (const entry of this.hostHandoffs.values()) {
|
|
1450
|
+
if (entry.requestedSessionId === session.sessionId) {
|
|
1451
|
+
entry.configs.set(session, config);
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1310
1454
|
return session;
|
|
1311
1455
|
}
|
|
1312
1456
|
/**
|
|
@@ -1337,10 +1481,11 @@ class CopilotClient {
|
|
|
1337
1481
|
return this.resumeSessionInternal(sessionId, config);
|
|
1338
1482
|
}
|
|
1339
1483
|
/** @internal */
|
|
1340
|
-
async resumeSessionForExtension(sessionId, config,
|
|
1341
|
-
return this.resumeSessionInternal(sessionId, config,
|
|
1484
|
+
async resumeSessionForExtension(sessionId, config, contributions = {}, extensionOptions) {
|
|
1485
|
+
return this.resumeSessionInternal(sessionId, config, contributions, extensionOptions);
|
|
1342
1486
|
}
|
|
1343
|
-
async resumeSessionInternal(sessionId, config,
|
|
1487
|
+
async resumeSessionInternal(sessionId, config, contributions = {}, extensionOptions) {
|
|
1488
|
+
const { workflows } = contributions;
|
|
1344
1489
|
if (config.gitHubToken !== void 0 && config.gitHubTokenProvider !== void 0) {
|
|
1345
1490
|
throw new Error("gitHubToken and gitHubTokenProvider are mutually exclusive");
|
|
1346
1491
|
}
|
|
@@ -1360,7 +1505,7 @@ class CopilotClient {
|
|
|
1360
1505
|
session.registerTools(config.tools);
|
|
1361
1506
|
session.registerCanvases(config.canvases);
|
|
1362
1507
|
session.registerCommands(config.commands);
|
|
1363
|
-
session.
|
|
1508
|
+
session.registerWorkflows(workflows);
|
|
1364
1509
|
const {
|
|
1365
1510
|
wireProvider: bearerWireProvider,
|
|
1366
1511
|
wireProviders: bearerWireProviders,
|
|
@@ -1385,6 +1530,9 @@ class CopilotClient {
|
|
|
1385
1530
|
if (config.hooks) {
|
|
1386
1531
|
session.registerHooks(config.hooks);
|
|
1387
1532
|
}
|
|
1533
|
+
if (config.skillProvider) {
|
|
1534
|
+
session.registerSkillProvider(config.skillProvider);
|
|
1535
|
+
}
|
|
1388
1536
|
const modeDefaults = this.configDefaultsForMode();
|
|
1389
1537
|
config = { ...modeDefaults, ...config };
|
|
1390
1538
|
config.customAgentsLocalOnly ??= modeDefaults.customAgentsLocalOnly;
|
|
@@ -1398,9 +1546,10 @@ class CopilotClient {
|
|
|
1398
1546
|
if (config.onEvent) {
|
|
1399
1547
|
session.on(config.onEvent);
|
|
1400
1548
|
}
|
|
1401
|
-
this.sessions.set(sessionId, session);
|
|
1402
|
-
this.setupSessionFs(session, config);
|
|
1403
1549
|
const toolFilterOptions = this.resolveToolFilterOptions(config);
|
|
1550
|
+
this.setupSessionFs(session, config);
|
|
1551
|
+
const replacedSession = this.sessions.get(sessionId);
|
|
1552
|
+
this.sessions.set(sessionId, session);
|
|
1404
1553
|
const gitHubTokenProviderRegistrationId = this.registerGitHubTokenProvider(
|
|
1405
1554
|
config.gitHubTokenProvider,
|
|
1406
1555
|
sessionId
|
|
@@ -1412,8 +1561,10 @@ class CopilotClient {
|
|
|
1412
1561
|
}
|
|
1413
1562
|
try {
|
|
1414
1563
|
const response = await this.connection.sendRequest("session.resume", {
|
|
1564
|
+
allowedModels: config.allowedModels,
|
|
1415
1565
|
...await (0, import_telemetry.getTraceContext)(this.onGetTraceContext),
|
|
1416
1566
|
sessionId,
|
|
1567
|
+
allowTranscriptRecovery: config.allowTranscriptRecovery,
|
|
1417
1568
|
clientName: config.clientName,
|
|
1418
1569
|
model: config.model,
|
|
1419
1570
|
reasoningEffort: config.reasoningEffort,
|
|
@@ -1432,7 +1583,7 @@ class CopilotClient {
|
|
|
1432
1583
|
tools: config.tools?.map((tool) => ({
|
|
1433
1584
|
name: tool.name,
|
|
1434
1585
|
description: tool.description,
|
|
1435
|
-
parameters: toJsonSchema(tool.parameters),
|
|
1586
|
+
parameters: (0, import_schema.toJsonSchema)(tool.parameters),
|
|
1436
1587
|
overridesBuiltInTool: tool.overridesBuiltInTool,
|
|
1437
1588
|
skipPermission: tool.skipPermission,
|
|
1438
1589
|
defer: tool.defer,
|
|
@@ -1441,7 +1592,7 @@ class CopilotClient {
|
|
|
1441
1592
|
})),
|
|
1442
1593
|
toolSearch: config.toolSearch,
|
|
1443
1594
|
canvases: config.canvases?.map((canvas) => canvas.declaration),
|
|
1444
|
-
|
|
1595
|
+
workflows: workflows?.map((workflow) => workflow.meta),
|
|
1445
1596
|
requestCanvasRenderer: config.requestCanvasRenderer,
|
|
1446
1597
|
requestExtensions: config.requestExtensions,
|
|
1447
1598
|
extensionSdkPath: config.extensionSdkPath,
|
|
@@ -1482,6 +1633,7 @@ class CopilotClient {
|
|
|
1482
1633
|
includeSubAgentStreamingEvents: config.includeSubAgentStreamingEvents ?? true,
|
|
1483
1634
|
...this.onGitHubTelemetry != null ? { enableGitHubTelemetryForwarding: true } : {},
|
|
1484
1635
|
mcpServers: toWireMcpServers(config.mcpServers),
|
|
1636
|
+
...config.diagnostics !== void 0 ? { diagnostics: config.diagnostics } : {},
|
|
1485
1637
|
mcpOAuthTokenStorage: config.mcpOAuthTokenStorage,
|
|
1486
1638
|
authClientIdMetadataUrl: config.authClientIdMetadataUrl,
|
|
1487
1639
|
envValueMode: "direct",
|
|
@@ -1490,6 +1642,7 @@ class CopilotClient {
|
|
|
1490
1642
|
defaultAgent: config.defaultAgent,
|
|
1491
1643
|
agent: config.agent,
|
|
1492
1644
|
skillDirectories: config.skillDirectories,
|
|
1645
|
+
...config.skillProvider ? { hasSkillProvider: true } : {},
|
|
1493
1646
|
pluginDirectories: config.pluginDirectories,
|
|
1494
1647
|
instructionDirectories: config.instructionDirectories,
|
|
1495
1648
|
disabledSkills: config.disabledSkills,
|
|
@@ -1505,6 +1658,7 @@ class CopilotClient {
|
|
|
1505
1658
|
featureFlags: config.featureFlags,
|
|
1506
1659
|
expAssignments: config.expAssignments,
|
|
1507
1660
|
enableManagedSettings: config.enableManagedSettings,
|
|
1661
|
+
enforceManagedModelDefaults: config.enforceManagedModelDefaults,
|
|
1508
1662
|
managedSettings: config.managedSettings,
|
|
1509
1663
|
...extensionOptions?.requestedEnvironmentVariables ? {
|
|
1510
1664
|
requestedEnvironmentVariables: extensionOptions.requestedEnvironmentVariables
|
|
@@ -1519,10 +1673,11 @@ class CopilotClient {
|
|
|
1519
1673
|
}
|
|
1520
1674
|
}
|
|
1521
1675
|
}
|
|
1522
|
-
const { workspacePath, capabilities, openCanvases } = response;
|
|
1676
|
+
const { workspacePath, capabilities, openCanvases, transcriptRecovery } = response;
|
|
1523
1677
|
session["_workspacePath"] = workspacePath;
|
|
1524
1678
|
session.setCapabilities(capabilities);
|
|
1525
1679
|
session.setOpenCanvases(openCanvases ?? []);
|
|
1680
|
+
session.setTranscriptRecovery(transcriptRecovery);
|
|
1526
1681
|
if (config.onMcpAuthRequest) {
|
|
1527
1682
|
await this.connection.sendRequest("session.eventLog.registerInterest", {
|
|
1528
1683
|
sessionId,
|
|
@@ -1533,14 +1688,220 @@ class CopilotClient {
|
|
|
1533
1688
|
this.commitGitHubTokenProvider(sessionId, gitHubTokenProviderRegistrationId);
|
|
1534
1689
|
} catch (e) {
|
|
1535
1690
|
session._markDisconnected();
|
|
1536
|
-
this.sessions.
|
|
1691
|
+
if (this.sessions.get(sessionId) === session) {
|
|
1692
|
+
if (replacedSession) {
|
|
1693
|
+
this.sessions.set(sessionId, replacedSession);
|
|
1694
|
+
} else {
|
|
1695
|
+
this.sessions.delete(sessionId);
|
|
1696
|
+
}
|
|
1697
|
+
}
|
|
1537
1698
|
if (gitHubTokenProviderRegistrationId !== void 0) {
|
|
1538
1699
|
this.githubTokenProviders.delete(gitHubTokenProviderRegistrationId);
|
|
1539
1700
|
}
|
|
1540
1701
|
throw e;
|
|
1541
1702
|
}
|
|
1703
|
+
for (const entry of this.hostHandoffs.values()) {
|
|
1704
|
+
if (entry.requestedSessionId === session.sessionId) {
|
|
1705
|
+
entry.configs.set(session, { ...config, sessionId });
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1542
1708
|
return session;
|
|
1543
1709
|
}
|
|
1710
|
+
/**
|
|
1711
|
+
* Start a complete AHP listener hosted in-process by this runtime.
|
|
1712
|
+
*
|
|
1713
|
+
* This connection owns the host. Disposing it or disconnecting the client
|
|
1714
|
+
* stops the listener without deleting sessions. The host uses a separate
|
|
1715
|
+
* SDK connection to the same runtime, not another runtime process.
|
|
1716
|
+
* Only one AHP host may own a compute-scoped catalog in an effective Copilot home
|
|
1717
|
+
* at a time. Other SDK clients and sessions remain usable in that home.
|
|
1718
|
+
* Select localServer, githubEnvironment, or both. An empty localServer
|
|
1719
|
+
* selects 127.0.0.1 on an available port. `onExit` runs at most once; owner disconnection cannot
|
|
1720
|
+
* acknowledge listener cleanup over the disconnected transport.
|
|
1721
|
+
*
|
|
1722
|
+
* @experimental
|
|
1723
|
+
*/
|
|
1724
|
+
async startAhpHost(options) {
|
|
1725
|
+
if (!options?.localServer && !options?.githubEnvironment) {
|
|
1726
|
+
throw new Error("At least one of localServer or githubEnvironment is required");
|
|
1727
|
+
}
|
|
1728
|
+
if (this.state !== "connected") {
|
|
1729
|
+
await this.start();
|
|
1730
|
+
}
|
|
1731
|
+
const rpc = this.rpc;
|
|
1732
|
+
const hostId = (0, import_node_crypto.randomUUID)();
|
|
1733
|
+
const {
|
|
1734
|
+
computeId,
|
|
1735
|
+
localServer,
|
|
1736
|
+
githubEnvironment,
|
|
1737
|
+
onExit,
|
|
1738
|
+
createSession,
|
|
1739
|
+
resumeSession,
|
|
1740
|
+
onSessionReleased
|
|
1741
|
+
} = options;
|
|
1742
|
+
if (createSession || resumeSession) {
|
|
1743
|
+
this.hostSessionFactories.set(hostId, {
|
|
1744
|
+
createSession,
|
|
1745
|
+
resumeSession,
|
|
1746
|
+
onSessionReleased
|
|
1747
|
+
});
|
|
1748
|
+
}
|
|
1749
|
+
if (onExit) {
|
|
1750
|
+
this.hostExitCallbacks.set(hostId, onExit);
|
|
1751
|
+
}
|
|
1752
|
+
try {
|
|
1753
|
+
const info = await rpc.host.start({
|
|
1754
|
+
hostId,
|
|
1755
|
+
computeId,
|
|
1756
|
+
localServer,
|
|
1757
|
+
githubEnvironment,
|
|
1758
|
+
sessionFactory: createSession ? true : void 0,
|
|
1759
|
+
resumeFactory: resumeSession ? true : void 0
|
|
1760
|
+
});
|
|
1761
|
+
return new import_host.AhpHost(
|
|
1762
|
+
info,
|
|
1763
|
+
async () => {
|
|
1764
|
+
await rpc.host.dispose({ hostId });
|
|
1765
|
+
},
|
|
1766
|
+
(sessionId) => rpc.host.publishSession({ hostId, sessionId }),
|
|
1767
|
+
() => rpc.host.listSessions({ hostId })
|
|
1768
|
+
);
|
|
1769
|
+
} catch (error) {
|
|
1770
|
+
this.hostExitCallbacks.delete(hostId);
|
|
1771
|
+
this.releaseHostSessions(hostId);
|
|
1772
|
+
throw error;
|
|
1773
|
+
}
|
|
1774
|
+
}
|
|
1775
|
+
handleHostExit(exit) {
|
|
1776
|
+
this.releaseHostSessions(exit.hostId);
|
|
1777
|
+
const onExit = this.hostExitCallbacks.get(exit.hostId);
|
|
1778
|
+
if (onExit) {
|
|
1779
|
+
this.hostExitCallbacks.delete(exit.hostId);
|
|
1780
|
+
const reportError = (error) => {
|
|
1781
|
+
console.error("AHP host exit callback failed", { hostId: exit.hostId, error });
|
|
1782
|
+
};
|
|
1783
|
+
try {
|
|
1784
|
+
void Promise.resolve(onExit(exit)).catch(reportError);
|
|
1785
|
+
} catch (error) {
|
|
1786
|
+
reportError(error);
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
}
|
|
1790
|
+
disconnectHosts() {
|
|
1791
|
+
for (const hostId of /* @__PURE__ */ new Set([
|
|
1792
|
+
...this.hostExitCallbacks.keys(),
|
|
1793
|
+
...this.hostSessionFactories.keys()
|
|
1794
|
+
])) {
|
|
1795
|
+
this.handleHostExit({
|
|
1796
|
+
hostId,
|
|
1797
|
+
reason: "ownerDisconnected",
|
|
1798
|
+
error: "Owner connection closed; runtime cleanup cannot be acknowledged on this connection."
|
|
1799
|
+
});
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
releaseHostSessions(hostId) {
|
|
1803
|
+
this.hostSessionFactories.delete(hostId);
|
|
1804
|
+
for (const [handoffId, entry] of this.hostHandoffs) {
|
|
1805
|
+
if (entry.hostId === hostId) this.releaseHostSession(handoffId);
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
releaseHostSession(handoffId) {
|
|
1809
|
+
const entry = this.hostHandoffs.get(handoffId);
|
|
1810
|
+
if (!entry) return;
|
|
1811
|
+
this.hostHandoffs.delete(handoffId);
|
|
1812
|
+
entry.released = true;
|
|
1813
|
+
entry.configs.clear();
|
|
1814
|
+
entry.controller.abort();
|
|
1815
|
+
if (entry.session) this.notifyHostSessionReleased(entry);
|
|
1816
|
+
}
|
|
1817
|
+
notifyHostSessionReleased(entry) {
|
|
1818
|
+
const { session, onReleased } = entry;
|
|
1819
|
+
entry.session = void 0;
|
|
1820
|
+
if (session && onReleased) {
|
|
1821
|
+
try {
|
|
1822
|
+
void Promise.resolve(onReleased(session)).catch(() => {
|
|
1823
|
+
console.error("AHP session release callback failed");
|
|
1824
|
+
});
|
|
1825
|
+
} catch {
|
|
1826
|
+
console.error("AHP session release callback failed");
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
async createHostSession(params) {
|
|
1831
|
+
const options = this.hostSessionFactories.get(params.hostId);
|
|
1832
|
+
if (!(params.resume ? options?.resumeSession : options?.createSession) || this.hostHandoffs.has(params.handoffId)) {
|
|
1833
|
+
throw new Error("AHP session factory is unavailable or handoff already exists");
|
|
1834
|
+
}
|
|
1835
|
+
const entry = {
|
|
1836
|
+
hostId: params.hostId,
|
|
1837
|
+
requestedSessionId: params.config.sessionId,
|
|
1838
|
+
controller: new AbortController(),
|
|
1839
|
+
configs: /* @__PURE__ */ new Map(),
|
|
1840
|
+
released: false,
|
|
1841
|
+
onReleased: options.onSessionReleased
|
|
1842
|
+
};
|
|
1843
|
+
this.hostHandoffs.set(params.handoffId, entry);
|
|
1844
|
+
const { configDir, ...config } = params.config;
|
|
1845
|
+
if (configDir !== void 0) config.configDirectory = configDir;
|
|
1846
|
+
const expected = structuredClone(config);
|
|
1847
|
+
const cancelled = new Promise((_resolve, reject) => {
|
|
1848
|
+
entry.controller.signal.addEventListener(
|
|
1849
|
+
"abort",
|
|
1850
|
+
() => {
|
|
1851
|
+
reject(new Error("AHP session handoff ended"));
|
|
1852
|
+
},
|
|
1853
|
+
{ once: true }
|
|
1854
|
+
);
|
|
1855
|
+
});
|
|
1856
|
+
const materialized = Promise.resolve().then(() => {
|
|
1857
|
+
if (params.resume) {
|
|
1858
|
+
const { sessionId, ...resumeConfig } = config;
|
|
1859
|
+
if (!sessionId) throw new Error("AHP resume requires sessionId");
|
|
1860
|
+
return options.resumeSession({
|
|
1861
|
+
sessionId,
|
|
1862
|
+
config: resumeConfig,
|
|
1863
|
+
signal: entry.controller.signal
|
|
1864
|
+
});
|
|
1865
|
+
}
|
|
1866
|
+
return options.createSession({
|
|
1867
|
+
config,
|
|
1868
|
+
signal: entry.controller.signal
|
|
1869
|
+
});
|
|
1870
|
+
}).then((session) => {
|
|
1871
|
+
entry.session = session;
|
|
1872
|
+
const actual = entry.configs.get(session);
|
|
1873
|
+
entry.configs.clear();
|
|
1874
|
+
if (entry.released) {
|
|
1875
|
+
this.notifyHostSessionReleased(entry);
|
|
1876
|
+
throw new Error("AHP session handoff ended");
|
|
1877
|
+
}
|
|
1878
|
+
if (this.sessions.get(session.sessionId) !== session || session.sessionId !== expected.sessionId) {
|
|
1879
|
+
throw new Error(
|
|
1880
|
+
"AHP callback must return the requested session from this client"
|
|
1881
|
+
);
|
|
1882
|
+
}
|
|
1883
|
+
const contains = (value, required) => {
|
|
1884
|
+
if (required && typeof required === "object" && !Array.isArray(required)) {
|
|
1885
|
+
return !!value && typeof value === "object" && Object.entries(required).every(
|
|
1886
|
+
([key, child]) => contains(value[key], child)
|
|
1887
|
+
);
|
|
1888
|
+
}
|
|
1889
|
+
return JSON.stringify(value) === JSON.stringify(required);
|
|
1890
|
+
};
|
|
1891
|
+
if ((!params.resume || actual !== void 0) && !contains(actual, expected)) {
|
|
1892
|
+
throw new Error(
|
|
1893
|
+
"AHP callback must preserve the supplied session configuration"
|
|
1894
|
+
);
|
|
1895
|
+
}
|
|
1896
|
+
return { sessionId: session.sessionId };
|
|
1897
|
+
});
|
|
1898
|
+
try {
|
|
1899
|
+
return await Promise.race([materialized, cancelled]);
|
|
1900
|
+
} catch (error) {
|
|
1901
|
+
this.releaseHostSession(params.handoffId);
|
|
1902
|
+
throw error;
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1544
1905
|
/**
|
|
1545
1906
|
* Sends a ping request to the server to verify connectivity.
|
|
1546
1907
|
*
|
|
@@ -1902,6 +2263,9 @@ class CopilotClient {
|
|
|
1902
2263
|
if (this.options.mode === "empty") {
|
|
1903
2264
|
env.COPILOT_DISABLE_KEYTAR = "1";
|
|
1904
2265
|
}
|
|
2266
|
+
if (this.options.mode !== "empty") {
|
|
2267
|
+
env.COPILOT_RUNTIME_PROCESS_FILE_LOGGING = "1";
|
|
2268
|
+
}
|
|
1905
2269
|
if (this.options.telemetry) {
|
|
1906
2270
|
const t = this.options.telemetry;
|
|
1907
2271
|
env.COPILOT_OTEL_ENABLED = "true";
|
|
@@ -2077,8 +2441,13 @@ stderr: ${stderrOutput}`
|
|
|
2077
2441
|
case "inprocess":
|
|
2078
2442
|
return this.connectViaFfi();
|
|
2079
2443
|
case "tcp":
|
|
2080
|
-
case "uri":
|
|
2081
2444
|
return this.connectViaTcp();
|
|
2445
|
+
case "uri": {
|
|
2446
|
+
const { host, port } = this.parseCliUrl(this.connectionConfig.url);
|
|
2447
|
+
this.actualHost = host;
|
|
2448
|
+
this.runtimePort = port;
|
|
2449
|
+
return this.connectViaTcp();
|
|
2450
|
+
}
|
|
2082
2451
|
}
|
|
2083
2452
|
}
|
|
2084
2453
|
/** Starts the in-process FFI runtime with SDK-managed typed options. */
|
|
@@ -2137,7 +2506,7 @@ stderr: ${stderrOutput}`
|
|
|
2137
2506
|
throw new Error("In-process FFI runtime host not started");
|
|
2138
2507
|
}
|
|
2139
2508
|
this.messageWriter = new TeardownResilientStreamMessageWriter(this.ffiHost.sendStream);
|
|
2140
|
-
this.connection =
|
|
2509
|
+
this.connection = createMessageConnection(
|
|
2141
2510
|
new import_node.StreamMessageReader(this.ffiHost.receiveStream),
|
|
2142
2511
|
this.messageWriter
|
|
2143
2512
|
);
|
|
@@ -2191,7 +2560,7 @@ stderr: ${stderrOutput}` : ""}`
|
|
|
2191
2560
|
}
|
|
2192
2561
|
});
|
|
2193
2562
|
this.messageWriter = new TeardownResilientStreamMessageWriter(this.cliProcess.stdin);
|
|
2194
|
-
this.connection =
|
|
2563
|
+
this.connection = createMessageConnection(
|
|
2195
2564
|
new import_node.StreamMessageReader(this.cliProcess.stdout),
|
|
2196
2565
|
this.messageWriter
|
|
2197
2566
|
);
|
|
@@ -2206,7 +2575,7 @@ stderr: ${stderrOutput}` : ""}`
|
|
|
2206
2575
|
throw new Error("CLI child process was unexpectedly started in parent process mode");
|
|
2207
2576
|
}
|
|
2208
2577
|
this.messageWriter = new TeardownResilientStreamMessageWriter(process.stdout);
|
|
2209
|
-
this.connection =
|
|
2578
|
+
this.connection = createMessageConnection(
|
|
2210
2579
|
new import_node.StreamMessageReader(process.stdin),
|
|
2211
2580
|
this.messageWriter
|
|
2212
2581
|
);
|
|
@@ -2229,7 +2598,7 @@ stderr: ${stderrOutput}` : ""}`
|
|
|
2229
2598
|
this.socket.connect(this.runtimePort, this.actualHost, () => {
|
|
2230
2599
|
clearTimeout(connectionTimeout);
|
|
2231
2600
|
this.messageWriter = new TeardownResilientStreamMessageWriter(this.socket);
|
|
2232
|
-
this.connection =
|
|
2601
|
+
this.connection = createMessageConnection(
|
|
2233
2602
|
new import_node.StreamMessageReader(this.socket),
|
|
2234
2603
|
this.messageWriter
|
|
2235
2604
|
);
|
|
@@ -2276,28 +2645,112 @@ stderr: ${stderrOutput}` : ""}`
|
|
|
2276
2645
|
return session.clientSessionApis;
|
|
2277
2646
|
});
|
|
2278
2647
|
(0, import_rpc.registerClientGlobalApiHandlers)(this.connection, this.clientGlobalHandlers);
|
|
2648
|
+
if (this.installationConfirmationHandler) {
|
|
2649
|
+
const confirmation = (0, import_installationConfirmation.createInstallationConfirmationAdapter)(
|
|
2650
|
+
this.connection,
|
|
2651
|
+
this.installationConfirmationHandler
|
|
2652
|
+
);
|
|
2653
|
+
this.connection.onRequest(
|
|
2654
|
+
"installations.confirm",
|
|
2655
|
+
(params, token) => confirmation.confirm(params, token)
|
|
2656
|
+
);
|
|
2657
|
+
}
|
|
2658
|
+
this.connection.onRequest(
|
|
2659
|
+
"host.materializeSession",
|
|
2660
|
+
(params) => this.createHostSession(params)
|
|
2661
|
+
);
|
|
2662
|
+
this.connection.onNotification(
|
|
2663
|
+
"host.sessionReleased",
|
|
2664
|
+
(params) => {
|
|
2665
|
+
if (this.hostHandoffs.get(params.handoffId)?.hostId === params.hostId) {
|
|
2666
|
+
this.releaseHostSession(params.handoffId);
|
|
2667
|
+
}
|
|
2668
|
+
}
|
|
2669
|
+
);
|
|
2279
2670
|
this.connection.onRequest(
|
|
2280
2671
|
"hooks.invoke",
|
|
2281
2672
|
async (params) => {
|
|
2282
2673
|
return await this.handleHooksInvoke(params);
|
|
2283
2674
|
}
|
|
2284
2675
|
);
|
|
2676
|
+
this.connection.onRequest(
|
|
2677
|
+
"skillProvider.list",
|
|
2678
|
+
async (params, token) => await this.resolveSkillProviderSession(params)._handleSkillProviderList(token)
|
|
2679
|
+
);
|
|
2680
|
+
this.connection.onRequest(
|
|
2681
|
+
"skillProvider.read",
|
|
2682
|
+
async (params, token) => {
|
|
2683
|
+
const session = this.resolveSkillProviderSession(params);
|
|
2684
|
+
if (typeof params.name !== "string") {
|
|
2685
|
+
throw new Error("Invalid skillProvider.read payload");
|
|
2686
|
+
}
|
|
2687
|
+
return await session._handleSkillProviderRead(params.name, token);
|
|
2688
|
+
}
|
|
2689
|
+
);
|
|
2285
2690
|
const connection = this.connection;
|
|
2691
|
+
const messageWriter = this.messageWriter;
|
|
2692
|
+
const cliProcess = this.isExternalServer ? null : this.cliProcess;
|
|
2693
|
+
const connectionEpoch = this.connectionEpoch;
|
|
2694
|
+
const isCurrentConnection = () => connectionEpoch === this.connectionEpoch && this.connection === connection && (cliProcess === null || this.cliProcess === cliProcess);
|
|
2695
|
+
let disconnecting = false;
|
|
2696
|
+
let exitFallback;
|
|
2286
2697
|
const markDisconnected = () => {
|
|
2287
|
-
if (
|
|
2698
|
+
if (!isCurrentConnection()) {
|
|
2699
|
+
connection.dispose();
|
|
2288
2700
|
return;
|
|
2289
2701
|
}
|
|
2290
2702
|
this.connectionClosed = true;
|
|
2291
2703
|
this.state = "disconnected";
|
|
2704
|
+
this.disconnectHosts();
|
|
2292
2705
|
for (const session of this.sessions.values()) {
|
|
2293
2706
|
session._markDisconnected();
|
|
2294
2707
|
}
|
|
2295
2708
|
this.sessions.clear();
|
|
2296
2709
|
this.githubTokenProviders.clear();
|
|
2710
|
+
this.requestAdapter?.cancelPending();
|
|
2711
|
+
connection.dispose();
|
|
2712
|
+
this.socket?.destroy();
|
|
2713
|
+
this.connection = null;
|
|
2714
|
+
this.messageWriter = null;
|
|
2715
|
+
this._rpc = null;
|
|
2716
|
+
this._internalRpc = null;
|
|
2717
|
+
this.modelsCache = null;
|
|
2297
2718
|
};
|
|
2298
|
-
|
|
2719
|
+
const disconnectAfterDrain = () => {
|
|
2720
|
+
if (exitFallback !== void 0) {
|
|
2721
|
+
clearTimeout(exitFallback);
|
|
2722
|
+
exitFallback = void 0;
|
|
2723
|
+
}
|
|
2724
|
+
if (!isCurrentConnection()) {
|
|
2725
|
+
connection.dispose();
|
|
2726
|
+
return;
|
|
2727
|
+
}
|
|
2728
|
+
if (disconnecting) return;
|
|
2729
|
+
disconnecting = true;
|
|
2730
|
+
this.connectionClosed = true;
|
|
2731
|
+
if (messageWriter) messageWriter.suppressWriteErrors = true;
|
|
2732
|
+
void connection.drain().then(markDisconnected);
|
|
2733
|
+
};
|
|
2734
|
+
this.connection.onClose(disconnectAfterDrain);
|
|
2735
|
+
if (cliProcess) {
|
|
2736
|
+
cliProcess.once("exit", () => {
|
|
2737
|
+
if (!isCurrentConnection()) {
|
|
2738
|
+
connection.dispose();
|
|
2739
|
+
return;
|
|
2740
|
+
}
|
|
2741
|
+
if (disconnecting) return;
|
|
2742
|
+
if (this.socket) {
|
|
2743
|
+
disconnectAfterDrain();
|
|
2744
|
+
return;
|
|
2745
|
+
}
|
|
2746
|
+
this.connectionClosed = true;
|
|
2747
|
+
if (messageWriter) messageWriter.suppressWriteErrors = true;
|
|
2748
|
+
exitFallback = setTimeout(disconnectAfterDrain, RUNTIME_SHUTDOWN_TIMEOUT_MS);
|
|
2749
|
+
exitFallback.unref();
|
|
2750
|
+
});
|
|
2751
|
+
}
|
|
2299
2752
|
this.connection.onError(() => {
|
|
2300
|
-
if (this.connection === connection) {
|
|
2753
|
+
if (connectionEpoch === this.connectionEpoch && this.connection === connection) {
|
|
2301
2754
|
this.state = "disconnected";
|
|
2302
2755
|
}
|
|
2303
2756
|
});
|
|
@@ -2390,6 +2843,16 @@ stderr: ${stderrOutput}` : ""}`
|
|
|
2390
2843
|
});
|
|
2391
2844
|
return { response };
|
|
2392
2845
|
}
|
|
2846
|
+
resolveSkillProviderSession(params) {
|
|
2847
|
+
if (!params || typeof params.sessionId !== "string") {
|
|
2848
|
+
throw new Error("Invalid skillProvider payload");
|
|
2849
|
+
}
|
|
2850
|
+
const session = this.sessions.get(params.sessionId);
|
|
2851
|
+
if (!session) {
|
|
2852
|
+
throw new Error(`Session not found: ${params.sessionId}`);
|
|
2853
|
+
}
|
|
2854
|
+
return session;
|
|
2855
|
+
}
|
|
2393
2856
|
async handleHooksInvoke(params) {
|
|
2394
2857
|
if (!params || typeof params.sessionId !== "string" || typeof params.hookType !== "string") {
|
|
2395
2858
|
throw new Error("Invalid hooks invoke payload");
|