@github/copilot-sdk 1.0.29-preview.1 → 1.0.29-preview.10
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 +658 -11
- package/dist/cjs/cliVersion.js +1 -1
- package/dist/cjs/client.js +138 -39
- package/dist/cjs/copilotRequestHandler.js +17 -7
- package/dist/cjs/generated/rpc.js +321 -6
- package/dist/cjs/session.js +59 -1
- package/dist/cjs/types.js +3 -5
- package/dist/cliVersion.d.ts +1 -1
- package/dist/cliVersion.js +1 -1
- package/dist/client.d.ts +8 -7
- package/dist/client.js +140 -41
- package/dist/copilotRequestHandler.js +17 -7
- package/dist/generated/lifecycle-diagnostics.d.ts +4 -0
- package/dist/generated/rpc.d.ts +670 -14
- package/dist/generated/rpc.js +321 -6
- package/dist/generated/session-events.d.ts +48 -2
- package/dist/index.d.ts +1 -1
- package/dist/session.d.ts +11 -3
- package/dist/session.js +59 -1
- package/dist/types.d.ts +50 -16
- package/dist/types.js +3 -5
- package/docs/agent-author.md +53 -5
- package/docs/examples.md +21 -16
- package/docs/workflows.md +2 -3
- package/package.json +17 -14
package/dist/cjs/cliVersion.js
CHANGED
|
@@ -22,7 +22,7 @@ __export(cliVersion_exports, {
|
|
|
22
22
|
COPILOT_CLI_VERSION: () => COPILOT_CLI_VERSION
|
|
23
23
|
});
|
|
24
24
|
module.exports = __toCommonJS(cliVersion_exports);
|
|
25
|
-
const COPILOT_CLI_VERSION = "1.0.106-
|
|
25
|
+
const COPILOT_CLI_VERSION = "1.0.106-11";
|
|
26
26
|
const COPILOT_CLI_USE_NPM_PACKAGE = false;
|
|
27
27
|
// Annotate the CommonJS export names for ESM import in node:
|
|
28
28
|
0 && (module.exports = {
|
package/dist/cjs/client.js
CHANGED
|
@@ -54,6 +54,19 @@ var import_types = require("./types.js");
|
|
|
54
54
|
const MIN_PROTOCOL_VERSION = 3;
|
|
55
55
|
const RUNTIME_SHUTDOWN_TIMEOUT_MS = 1e4;
|
|
56
56
|
const CLOUD_SESSION_CLEANUP_TIMEOUT_MS = 1e4;
|
|
57
|
+
const messageEncodeErrors = /* @__PURE__ */ new WeakSet();
|
|
58
|
+
const jsonMessageEncoder = {
|
|
59
|
+
name: "application/json",
|
|
60
|
+
encode: (msg, options) => {
|
|
61
|
+
try {
|
|
62
|
+
return Promise.resolve(Buffer.from(JSON.stringify(msg), options.charset));
|
|
63
|
+
} catch (thrown) {
|
|
64
|
+
const error = thrown instanceof Error ? thrown : new Error(String(thrown));
|
|
65
|
+
messageEncodeErrors.add(error);
|
|
66
|
+
return Promise.reject(error);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
};
|
|
57
70
|
function createMessageConnection(reader, writer) {
|
|
58
71
|
let dispatch;
|
|
59
72
|
let finishDrain;
|
|
@@ -288,8 +301,15 @@ function getNodeExecPath() {
|
|
|
288
301
|
function getBundledRuntimePath() {
|
|
289
302
|
return (0, import_runtimeArtifacts.ensureRuntimeBundle)(import_cliVersion.COPILOT_CLI_VERSION);
|
|
290
303
|
}
|
|
304
|
+
async function getSpawnableBundledRuntimePath() {
|
|
305
|
+
const runtimePath = await getBundledRuntimePath();
|
|
306
|
+
return process.platform === "win32" ? import_node_path.win32.toNamespacedPath(runtimePath) : runtimePath;
|
|
307
|
+
}
|
|
291
308
|
class TeardownResilientStreamMessageWriter extends import_node.StreamMessageWriter {
|
|
292
309
|
suppressWriteErrors = false;
|
|
310
|
+
constructor(writable) {
|
|
311
|
+
super(writable, { contentTypeEncoder: jsonMessageEncoder });
|
|
312
|
+
}
|
|
293
313
|
async write(msg) {
|
|
294
314
|
try {
|
|
295
315
|
await super.write(msg);
|
|
@@ -336,8 +356,10 @@ class CopilotClient {
|
|
|
336
356
|
effectiveConnectionToken;
|
|
337
357
|
onListModels;
|
|
338
358
|
onGetTraceContext;
|
|
339
|
-
modelsCache =
|
|
340
|
-
|
|
359
|
+
modelsCache = {
|
|
360
|
+
models: null,
|
|
361
|
+
lock: Promise.resolve()
|
|
362
|
+
};
|
|
341
363
|
sessionLifecycleHandlers = /* @__PURE__ */ new Set();
|
|
342
364
|
typedLifecycleHandlers = /* @__PURE__ */ new Map();
|
|
343
365
|
_rpc = null;
|
|
@@ -448,19 +470,22 @@ class CopilotClient {
|
|
|
448
470
|
*/
|
|
449
471
|
constructor(options = {}) {
|
|
450
472
|
const conn = options._internalConnection ?? options.connection ?? CopilotClient.resolveDefaultConnection();
|
|
473
|
+
if ((conn.kind === "uri" || conn.kind === "parent-process") && options.internalTestOptions !== void 0) {
|
|
474
|
+
throw new Error("internalTestOptions requires a runtime owned by this client");
|
|
475
|
+
}
|
|
451
476
|
if (conn.kind === "uri" && (options.gitHubToken !== void 0 || options.useLoggedInUser !== void 0)) {
|
|
452
477
|
throw new Error(
|
|
453
478
|
"gitHubToken and useLoggedInUser cannot be used with RuntimeConnection.forUri (external server manages its own auth)"
|
|
454
479
|
);
|
|
455
480
|
}
|
|
456
|
-
if (conn.kind === "inprocess" && options.
|
|
481
|
+
if (conn.kind === "inprocess" && options.env !== void 0) {
|
|
457
482
|
throw new Error(
|
|
458
|
-
"
|
|
483
|
+
"env is not supported with RuntimeConnection.forInProcess(): environment variables belong to the shared host process. Use typed client/session options or a child-process transport."
|
|
459
484
|
);
|
|
460
485
|
}
|
|
461
|
-
if (conn.kind === "inprocess" && options.
|
|
486
|
+
if (conn.kind === "inprocess" && options.workingDirectory !== void 0) {
|
|
462
487
|
throw new Error(
|
|
463
|
-
"
|
|
488
|
+
"workingDirectory is not supported with RuntimeConnection.forInProcess(): the in-process transport hosts the runtime in this process, so honoring it would require mutating the shared process-global cwd. Set SessionConfig.workingDirectory instead."
|
|
464
489
|
);
|
|
465
490
|
}
|
|
466
491
|
if (conn.kind === "inprocess" && options.telemetry !== void 0) {
|
|
@@ -526,6 +551,7 @@ class CopilotClient {
|
|
|
526
551
|
const connArgs = conn.kind === "stdio" || conn.kind === "tcp" ? conn.args ?? [] : [];
|
|
527
552
|
this.connectionExtraArgs = [...connArgs];
|
|
528
553
|
this.options = {
|
|
554
|
+
internalTestOptions: options.internalTestOptions,
|
|
529
555
|
entra: options.entra ? { ...options.entra, tokenProvider: options.entra.tokenProvider } : void 0,
|
|
530
556
|
workingDirectory: options.workingDirectory ?? process.cwd(),
|
|
531
557
|
logLevel: options.logLevel,
|
|
@@ -797,12 +823,17 @@ class CopilotClient {
|
|
|
797
823
|
* Stops the CLI server and closes all active sessions.
|
|
798
824
|
*
|
|
799
825
|
* This method performs graceful cleanup:
|
|
800
|
-
* 1. Closes all active sessions (releases in-memory resources)
|
|
826
|
+
* 1. Closes all active sessions (releases in-memory resources); detach
|
|
827
|
+
* requests share one shutdown timeout
|
|
801
828
|
* 2. Requests runtime shutdown for SDK-owned CLI processes
|
|
802
829
|
* 3. Closes the JSON-RPC connection
|
|
803
830
|
* 4. Signals EOF to an owned stdio process and waits for host cleanup, then
|
|
804
831
|
* terminates the process if it does not exit within the shutdown timeout
|
|
805
832
|
*
|
|
833
|
+
* In-process connections first abort active turns without a timeout. That
|
|
834
|
+
* step is outside the detach deadline, so in-process stop() can still wait
|
|
835
|
+
* indefinitely if the runtime stops answering abort requests.
|
|
836
|
+
*
|
|
806
837
|
* Note: session data on disk is preserved, so sessions can be resumed later.
|
|
807
838
|
* To permanently remove session data before stopping, call
|
|
808
839
|
* {@link deleteSession} for each session first.
|
|
@@ -824,16 +855,31 @@ class CopilotClient {
|
|
|
824
855
|
if (this.connectionConfig.kind === "inprocess") {
|
|
825
856
|
await Promise.allSettled(activeSessions.map((session) => session.abort()));
|
|
826
857
|
}
|
|
858
|
+
let detachDeadlineTimer;
|
|
859
|
+
const detachDeadline = new Promise((resolve2) => {
|
|
860
|
+
detachDeadlineTimer = setTimeout(() => resolve2("timeout"), RUNTIME_SHUTDOWN_TIMEOUT_MS);
|
|
861
|
+
});
|
|
827
862
|
for (const session of activeSessions) {
|
|
828
863
|
const sessionId = session.sessionId;
|
|
829
864
|
let lastError = null;
|
|
865
|
+
let attempts = 0;
|
|
830
866
|
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
831
867
|
if (this.connectionClosed) {
|
|
832
868
|
lastError = null;
|
|
833
869
|
break;
|
|
834
870
|
}
|
|
871
|
+
attempts = attempt;
|
|
835
872
|
try {
|
|
836
|
-
await
|
|
873
|
+
const outcome = await Promise.race([
|
|
874
|
+
session.disconnect().then(() => "disconnected"),
|
|
875
|
+
detachDeadline
|
|
876
|
+
]);
|
|
877
|
+
if (outcome === "timeout") {
|
|
878
|
+
lastError = new Error(
|
|
879
|
+
`session.detach did not complete within the ${RUNTIME_SHUTDOWN_TIMEOUT_MS}ms shutdown timeout`
|
|
880
|
+
);
|
|
881
|
+
break;
|
|
882
|
+
}
|
|
837
883
|
lastError = null;
|
|
838
884
|
break;
|
|
839
885
|
} catch (error) {
|
|
@@ -851,11 +897,12 @@ class CopilotClient {
|
|
|
851
897
|
if (lastError) {
|
|
852
898
|
errors.push(
|
|
853
899
|
new Error(
|
|
854
|
-
`Failed to disconnect session ${sessionId} after
|
|
900
|
+
`Failed to disconnect session ${sessionId} after ${attempts} ${attempts === 1 ? "attempt" : "attempts"}: ${lastError.message}`
|
|
855
901
|
)
|
|
856
902
|
);
|
|
857
903
|
}
|
|
858
904
|
}
|
|
905
|
+
clearTimeout(detachDeadlineTimer);
|
|
859
906
|
for (const session of activeSessions) {
|
|
860
907
|
session._markDisconnected();
|
|
861
908
|
}
|
|
@@ -906,7 +953,7 @@ class CopilotClient {
|
|
|
906
953
|
this._rpc = null;
|
|
907
954
|
this._internalRpc = null;
|
|
908
955
|
}
|
|
909
|
-
this.modelsCache = null;
|
|
956
|
+
this.modelsCache = { models: null, lock: Promise.resolve() };
|
|
910
957
|
if (this.socket) {
|
|
911
958
|
const socket = this.socket;
|
|
912
959
|
this.socket = null;
|
|
@@ -1068,7 +1115,7 @@ class CopilotClient {
|
|
|
1068
1115
|
this._rpc = null;
|
|
1069
1116
|
this._internalRpc = null;
|
|
1070
1117
|
}
|
|
1071
|
-
this.modelsCache = null;
|
|
1118
|
+
this.modelsCache = { models: null, lock: Promise.resolve() };
|
|
1072
1119
|
if (this.socket) {
|
|
1073
1120
|
try {
|
|
1074
1121
|
this.socket.destroy();
|
|
@@ -1125,7 +1172,7 @@ class CopilotClient {
|
|
|
1125
1172
|
* // Session with model and tools
|
|
1126
1173
|
* const session = await client.createSession({
|
|
1127
1174
|
* onPermissionRequest: approveAll,
|
|
1128
|
-
* model: "gpt-4",
|
|
1175
|
+
* model: "gpt-5.4",
|
|
1129
1176
|
* tools: [{
|
|
1130
1177
|
* name: "get_weather",
|
|
1131
1178
|
* description: "Get weather for a location",
|
|
@@ -2087,14 +2134,16 @@ class CopilotClient {
|
|
|
2087
2134
|
* @throws Error if not connected (when no custom handler is set)
|
|
2088
2135
|
*/
|
|
2089
2136
|
async listModels() {
|
|
2090
|
-
|
|
2137
|
+
const cache = this.modelsCache;
|
|
2138
|
+
const previousLock = cache.lock;
|
|
2091
2139
|
let resolveLock;
|
|
2092
|
-
|
|
2140
|
+
cache.lock = new Promise((resolve2) => {
|
|
2093
2141
|
resolveLock = resolve2;
|
|
2094
2142
|
});
|
|
2143
|
+
await previousLock;
|
|
2095
2144
|
try {
|
|
2096
|
-
if (
|
|
2097
|
-
return [...
|
|
2145
|
+
if (cache.models !== null) {
|
|
2146
|
+
return [...cache.models];
|
|
2098
2147
|
}
|
|
2099
2148
|
let models;
|
|
2100
2149
|
if (this.onListModels) {
|
|
@@ -2123,7 +2172,7 @@ class CopilotClient {
|
|
|
2123
2172
|
}
|
|
2124
2173
|
}
|
|
2125
2174
|
}
|
|
2126
|
-
|
|
2175
|
+
cache.models = [...models];
|
|
2127
2176
|
return [...models];
|
|
2128
2177
|
} finally {
|
|
2129
2178
|
resolveLock();
|
|
@@ -2311,7 +2360,9 @@ class CopilotClient {
|
|
|
2311
2360
|
gitRoot: context.gitRoot,
|
|
2312
2361
|
repository: context.repository,
|
|
2313
2362
|
branch: context.branch
|
|
2314
|
-
} : void 0
|
|
2363
|
+
} : void 0,
|
|
2364
|
+
clientName: raw.clientName,
|
|
2365
|
+
lastClientName: raw.lastClientName
|
|
2315
2366
|
};
|
|
2316
2367
|
}
|
|
2317
2368
|
/**
|
|
@@ -2386,15 +2437,40 @@ class CopilotClient {
|
|
|
2386
2437
|
};
|
|
2387
2438
|
}
|
|
2388
2439
|
/**
|
|
2389
|
-
* Builds
|
|
2390
|
-
*
|
|
2391
|
-
* variables on top of the effective env. Not used by the in-process (FFI) transport,
|
|
2392
|
-
* whose worker inherits the host process's ambient environment
|
|
2393
|
-
* (see {@link CopilotClient.startInProcessFfi}).
|
|
2440
|
+
* Builds runtime configuration for child processes and the per-host FFI snapshot.
|
|
2441
|
+
* Typed auth, home, keychain, and telemetry options override the effective env.
|
|
2394
2442
|
*/
|
|
2395
2443
|
buildRuntimeEnv() {
|
|
2396
|
-
const env =
|
|
2444
|
+
const env = process.platform === "win32" ? Object.fromEntries(
|
|
2445
|
+
Object.entries(this.resolvedEnv).map(([key, value]) => [
|
|
2446
|
+
key.toUpperCase(),
|
|
2447
|
+
value
|
|
2448
|
+
])
|
|
2449
|
+
) : { ...this.resolvedEnv };
|
|
2397
2450
|
delete env.NODE_DEBUG;
|
|
2451
|
+
const test = this.options.internalTestOptions;
|
|
2452
|
+
if (test) {
|
|
2453
|
+
const overrides = {
|
|
2454
|
+
COPILOT_API_URL: test.copilotApiUrl,
|
|
2455
|
+
COPILOT_DEBUG_GITHUB_API_URL: test.githubApiUrl,
|
|
2456
|
+
HTTP_PROXY: test.proxyUrl,
|
|
2457
|
+
HTTPS_PROXY: test.proxyUrl,
|
|
2458
|
+
http_proxy: test.proxyUrl,
|
|
2459
|
+
https_proxy: test.proxyUrl,
|
|
2460
|
+
NO_PROXY: test.noProxy,
|
|
2461
|
+
no_proxy: test.noProxy,
|
|
2462
|
+
NODE_EXTRA_CA_CERTS: test.caCertificatePath,
|
|
2463
|
+
SSL_CERT_FILE: test.caCertificatePath,
|
|
2464
|
+
REQUESTS_CA_BUNDLE: test.caCertificatePath,
|
|
2465
|
+
CURL_CA_BUNDLE: test.caCertificatePath,
|
|
2466
|
+
GIT_SSL_CAINFO: test.caCertificatePath
|
|
2467
|
+
};
|
|
2468
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
2469
|
+
if (value !== void 0) {
|
|
2470
|
+
env[process.platform === "win32" ? key.toUpperCase() : key] = value;
|
|
2471
|
+
}
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2398
2474
|
if (this.options.gitHubToken) {
|
|
2399
2475
|
env.COPILOT_SDK_AUTH_TOKEN = this.options.gitHubToken;
|
|
2400
2476
|
}
|
|
@@ -2403,6 +2479,9 @@ class CopilotClient {
|
|
|
2403
2479
|
}
|
|
2404
2480
|
if (this.options.baseDirectory) {
|
|
2405
2481
|
env.COPILOT_HOME = this.options.baseDirectory;
|
|
2482
|
+
if (this.connectionConfig.kind === "inprocess") {
|
|
2483
|
+
env.COPILOT_CACHE_HOME = (0, import_node_path.join)(this.options.baseDirectory, "cache");
|
|
2484
|
+
}
|
|
2406
2485
|
}
|
|
2407
2486
|
if (this.options.mode === "empty") {
|
|
2408
2487
|
env.COPILOT_DISABLE_KEYTAR = "1";
|
|
@@ -2429,7 +2508,7 @@ class CopilotClient {
|
|
|
2429
2508
|
* Start the CLI server process
|
|
2430
2509
|
*/
|
|
2431
2510
|
async startCLIServer() {
|
|
2432
|
-
this.resolvedCliPath ??= await
|
|
2511
|
+
this.resolvedCliPath ??= await getSpawnableBundledRuntimePath();
|
|
2433
2512
|
return new Promise((resolve2, reject) => {
|
|
2434
2513
|
this.stderrBuffer = "";
|
|
2435
2514
|
const args = [...this.connectionExtraArgs, "--headless", "--no-auto-update"];
|
|
@@ -2467,6 +2546,22 @@ class CopilotClient {
|
|
|
2467
2546
|
`Copilot CLI not found at ${this.resolvedCliPath}. Set COPILOT_CLI_PATH to use a custom installation.`
|
|
2468
2547
|
);
|
|
2469
2548
|
}
|
|
2549
|
+
const workingDirectory = this.options.workingDirectory;
|
|
2550
|
+
let workingDirectoryProblem;
|
|
2551
|
+
try {
|
|
2552
|
+
workingDirectoryProblem = (0, import_node_fs.statSync)(workingDirectory).isDirectory() ? void 0 : "does not exist or is not a directory";
|
|
2553
|
+
if (!workingDirectoryProblem) {
|
|
2554
|
+
(0, import_node_fs.accessSync)(workingDirectory, import_node_fs.constants.X_OK);
|
|
2555
|
+
}
|
|
2556
|
+
} catch (error) {
|
|
2557
|
+
const code = error.code;
|
|
2558
|
+
workingDirectoryProblem = code === "ENOENT" || code === "ENOTDIR" ? "does not exist or is not a directory" : `cannot be entered: ${error instanceof Error ? error.message : String(error)}`;
|
|
2559
|
+
}
|
|
2560
|
+
if (workingDirectoryProblem) {
|
|
2561
|
+
throw new Error(
|
|
2562
|
+
`Working directory ${workingDirectory} ${workingDirectoryProblem}.`
|
|
2563
|
+
);
|
|
2564
|
+
}
|
|
2470
2565
|
const stdioConfig = this.connectionConfig.kind === "stdio" ? ["pipe", "pipe", "pipe"] : ["ignore", "pipe", "pipe"];
|
|
2471
2566
|
const isJsFile = this.resolvedCliPath.endsWith(".js");
|
|
2472
2567
|
if (isJsFile) {
|
|
@@ -2618,16 +2713,7 @@ stderr: ${this.stderrBuffer.trim().slice(-8192) || "(empty)"}`
|
|
|
2618
2713
|
runtimeLibrary = (0, import_node_path.join)((0, import_node_path.dirname)(bundledRuntimePath), "runtime.node");
|
|
2619
2714
|
}
|
|
2620
2715
|
const { FfiRuntimeHost } = await import("./ffiRuntimeHost.js");
|
|
2621
|
-
const environment =
|
|
2622
|
-
if (this.options.gitHubToken) {
|
|
2623
|
-
environment.COPILOT_SDK_AUTH_TOKEN = this.options.gitHubToken;
|
|
2624
|
-
}
|
|
2625
|
-
if (this.options.baseDirectory) {
|
|
2626
|
-
environment.COPILOT_HOME = this.options.baseDirectory;
|
|
2627
|
-
}
|
|
2628
|
-
if (this.options.mode === "empty") {
|
|
2629
|
-
environment.COPILOT_DISABLE_KEYTAR = "1";
|
|
2630
|
-
}
|
|
2716
|
+
const environment = this.buildRuntimeEnv();
|
|
2631
2717
|
const args = [];
|
|
2632
2718
|
if (this.options.logLevel) {
|
|
2633
2719
|
args.push("--log-level", this.options.logLevel);
|
|
@@ -2888,6 +2974,14 @@ stderr: ${stderrOutput}` : ""}`
|
|
|
2888
2974
|
"skillProvider.list",
|
|
2889
2975
|
async (params, token) => await this.resolveSkillProviderSession(params)._handleSkillProviderList(token)
|
|
2890
2976
|
);
|
|
2977
|
+
this.connection.onRequest(
|
|
2978
|
+
"sessionManagement.create",
|
|
2979
|
+
async (params, token) => {
|
|
2980
|
+
const session = this.sessions.get(params.sessionId);
|
|
2981
|
+
if (!session) throw new Error("Session creation owner is unavailable");
|
|
2982
|
+
return await session._handleSessionManagementCreate(params, token);
|
|
2983
|
+
}
|
|
2984
|
+
);
|
|
2891
2985
|
this.connection.onRequest(
|
|
2892
2986
|
"skillProvider.read",
|
|
2893
2987
|
async (params, token) => {
|
|
@@ -2925,7 +3019,7 @@ stderr: ${stderrOutput}` : ""}`
|
|
|
2925
3019
|
this.messageWriter = null;
|
|
2926
3020
|
this._rpc = null;
|
|
2927
3021
|
this._internalRpc = null;
|
|
2928
|
-
this.modelsCache = null;
|
|
3022
|
+
this.modelsCache = { models: null, lock: Promise.resolve() };
|
|
2929
3023
|
};
|
|
2930
3024
|
const disconnectAfterDrain = () => {
|
|
2931
3025
|
if (exitFallback !== void 0) {
|
|
@@ -2960,7 +3054,10 @@ stderr: ${stderrOutput}` : ""}`
|
|
|
2960
3054
|
exitFallback.unref?.();
|
|
2961
3055
|
});
|
|
2962
3056
|
}
|
|
2963
|
-
this.connection.onError(() => {
|
|
3057
|
+
this.connection.onError(([error]) => {
|
|
3058
|
+
if (messageEncodeErrors.has(error)) {
|
|
3059
|
+
return;
|
|
3060
|
+
}
|
|
2964
3061
|
if (connectionEpoch === this.connectionEpoch && this.connection === connection) {
|
|
2965
3062
|
this.state = "disconnected";
|
|
2966
3063
|
}
|
|
@@ -3021,7 +3118,8 @@ stderr: ${stderrOutput}` : ""}`
|
|
|
3021
3118
|
const result = await session._handleUserInputRequest({
|
|
3022
3119
|
question: params.question,
|
|
3023
3120
|
choices: params.choices,
|
|
3024
|
-
allowFreeform: params.allowFreeform
|
|
3121
|
+
allowFreeform: params.allowFreeform,
|
|
3122
|
+
requestId: params.requestId
|
|
3025
3123
|
});
|
|
3026
3124
|
return result;
|
|
3027
3125
|
}
|
|
@@ -3037,7 +3135,8 @@ stderr: ${stderrOutput}` : ""}`
|
|
|
3037
3135
|
summary: params.summary,
|
|
3038
3136
|
planContent: params.planContent,
|
|
3039
3137
|
actions: params.actions,
|
|
3040
|
-
recommendedAction: params.recommendedAction
|
|
3138
|
+
recommendedAction: params.recommendedAction,
|
|
3139
|
+
requestId: params.requestId
|
|
3041
3140
|
});
|
|
3042
3141
|
}
|
|
3043
3142
|
async handleAutoModeSwitchRequest(params) {
|
|
@@ -109,7 +109,9 @@ class CopilotWebSocketForwarder extends CopilotWebSocketHandler {
|
|
|
109
109
|
if (this.#upstream) {
|
|
110
110
|
return;
|
|
111
111
|
}
|
|
112
|
-
const upstream = new WebSocket(this.context.url
|
|
112
|
+
const upstream = new WebSocket(this.context.url, {
|
|
113
|
+
headers: forwardedRequestHeaders(this.context.headers)
|
|
114
|
+
});
|
|
113
115
|
upstream.binaryType = "arraybuffer";
|
|
114
116
|
this.#upstream = upstream;
|
|
115
117
|
upstream.addEventListener("message", (event) => {
|
|
@@ -149,13 +151,16 @@ class CopilotWebSocketForwarder extends CopilotWebSocketHandler {
|
|
|
149
151
|
});
|
|
150
152
|
}
|
|
151
153
|
async close(status = CopilotWebSocketCloseStatus.normalClosure) {
|
|
154
|
+
const upstream = this.#upstream;
|
|
155
|
+
this.#upstream = null;
|
|
156
|
+
const completion = super.close(status);
|
|
152
157
|
try {
|
|
153
|
-
if (
|
|
154
|
-
|
|
158
|
+
if (upstream?.readyState === WebSocket.OPEN || upstream?.readyState === WebSocket.CONNECTING) {
|
|
159
|
+
upstream.close();
|
|
155
160
|
}
|
|
156
161
|
} catch {
|
|
157
162
|
}
|
|
158
|
-
await
|
|
163
|
+
await completion;
|
|
159
164
|
}
|
|
160
165
|
async [Symbol.asyncDispose]() {
|
|
161
166
|
try {
|
|
@@ -494,19 +499,24 @@ const FORBIDDEN_REQUEST_HEADERS = /* @__PURE__ */ new Set([
|
|
|
494
499
|
"te",
|
|
495
500
|
"trailer"
|
|
496
501
|
]);
|
|
497
|
-
|
|
502
|
+
function forwardedRequestHeaders(source) {
|
|
498
503
|
const headers = new Headers();
|
|
499
|
-
for (const [name, values] of Object.entries(
|
|
504
|
+
for (const [name, values] of Object.entries(source)) {
|
|
500
505
|
if (!values) {
|
|
501
506
|
continue;
|
|
502
507
|
}
|
|
503
|
-
|
|
508
|
+
const lower = name.toLowerCase();
|
|
509
|
+
if (FORBIDDEN_REQUEST_HEADERS.has(lower) || lower.startsWith("sec-websocket-")) {
|
|
504
510
|
continue;
|
|
505
511
|
}
|
|
506
512
|
for (const value of values) {
|
|
507
513
|
headers.append(name, value);
|
|
508
514
|
}
|
|
509
515
|
}
|
|
516
|
+
return headers;
|
|
517
|
+
}
|
|
518
|
+
async function buildFetchRequest(exchange) {
|
|
519
|
+
const headers = forwardedRequestHeaders(exchange.headers);
|
|
510
520
|
const method = exchange.method.toUpperCase();
|
|
511
521
|
const hasBody = method !== "GET" && method !== "HEAD";
|
|
512
522
|
let body;
|