@github/copilot-sdk 1.0.29-preview.0 → 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 +671 -15
- 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 +52 -17
- 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 +19 -16
package/dist/client.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import { randomUUID } from "node:crypto";
|
|
3
|
-
import { existsSync } from "node:fs";
|
|
3
|
+
import { accessSync, constants, existsSync, statSync } from "node:fs";
|
|
4
4
|
import { isIPv6, Socket } from "node:net";
|
|
5
|
-
import { dirname, isAbsolute, join, resolve } from "node:path";
|
|
5
|
+
import { dirname, isAbsolute, join, resolve, win32 } from "node:path";
|
|
6
6
|
import {
|
|
7
7
|
createMessageConnection as createRpcMessageConnection,
|
|
8
8
|
ErrorCodes,
|
|
@@ -36,6 +36,19 @@ import { defaultJoinSessionPermissionHandler } from "./types.js";
|
|
|
36
36
|
const MIN_PROTOCOL_VERSION = 3;
|
|
37
37
|
const RUNTIME_SHUTDOWN_TIMEOUT_MS = 1e4;
|
|
38
38
|
const CLOUD_SESSION_CLEANUP_TIMEOUT_MS = 1e4;
|
|
39
|
+
const messageEncodeErrors = /* @__PURE__ */ new WeakSet();
|
|
40
|
+
const jsonMessageEncoder = {
|
|
41
|
+
name: "application/json",
|
|
42
|
+
encode: (msg, options) => {
|
|
43
|
+
try {
|
|
44
|
+
return Promise.resolve(Buffer.from(JSON.stringify(msg), options.charset));
|
|
45
|
+
} catch (thrown) {
|
|
46
|
+
const error = thrown instanceof Error ? thrown : new Error(String(thrown));
|
|
47
|
+
messageEncodeErrors.add(error);
|
|
48
|
+
return Promise.reject(error);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
39
52
|
function createMessageConnection(reader, writer) {
|
|
40
53
|
let dispatch;
|
|
41
54
|
let finishDrain;
|
|
@@ -270,8 +283,15 @@ function getNodeExecPath() {
|
|
|
270
283
|
function getBundledRuntimePath() {
|
|
271
284
|
return ensureRuntimeBundle(COPILOT_CLI_VERSION);
|
|
272
285
|
}
|
|
286
|
+
async function getSpawnableBundledRuntimePath() {
|
|
287
|
+
const runtimePath = await getBundledRuntimePath();
|
|
288
|
+
return process.platform === "win32" ? win32.toNamespacedPath(runtimePath) : runtimePath;
|
|
289
|
+
}
|
|
273
290
|
class TeardownResilientStreamMessageWriter extends StreamMessageWriter {
|
|
274
291
|
suppressWriteErrors = false;
|
|
292
|
+
constructor(writable) {
|
|
293
|
+
super(writable, { contentTypeEncoder: jsonMessageEncoder });
|
|
294
|
+
}
|
|
275
295
|
async write(msg) {
|
|
276
296
|
try {
|
|
277
297
|
await super.write(msg);
|
|
@@ -318,8 +338,10 @@ class CopilotClient {
|
|
|
318
338
|
effectiveConnectionToken;
|
|
319
339
|
onListModels;
|
|
320
340
|
onGetTraceContext;
|
|
321
|
-
modelsCache =
|
|
322
|
-
|
|
341
|
+
modelsCache = {
|
|
342
|
+
models: null,
|
|
343
|
+
lock: Promise.resolve()
|
|
344
|
+
};
|
|
323
345
|
sessionLifecycleHandlers = /* @__PURE__ */ new Set();
|
|
324
346
|
typedLifecycleHandlers = /* @__PURE__ */ new Map();
|
|
325
347
|
_rpc = null;
|
|
@@ -430,19 +452,22 @@ class CopilotClient {
|
|
|
430
452
|
*/
|
|
431
453
|
constructor(options = {}) {
|
|
432
454
|
const conn = options._internalConnection ?? options.connection ?? CopilotClient.resolveDefaultConnection();
|
|
455
|
+
if ((conn.kind === "uri" || conn.kind === "parent-process") && options.internalTestOptions !== void 0) {
|
|
456
|
+
throw new Error("internalTestOptions requires a runtime owned by this client");
|
|
457
|
+
}
|
|
433
458
|
if (conn.kind === "uri" && (options.gitHubToken !== void 0 || options.useLoggedInUser !== void 0)) {
|
|
434
459
|
throw new Error(
|
|
435
460
|
"gitHubToken and useLoggedInUser cannot be used with RuntimeConnection.forUri (external server manages its own auth)"
|
|
436
461
|
);
|
|
437
462
|
}
|
|
438
|
-
if (conn.kind === "inprocess" && options.
|
|
463
|
+
if (conn.kind === "inprocess" && options.env !== void 0) {
|
|
439
464
|
throw new Error(
|
|
440
|
-
"
|
|
465
|
+
"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."
|
|
441
466
|
);
|
|
442
467
|
}
|
|
443
|
-
if (conn.kind === "inprocess" && options.
|
|
468
|
+
if (conn.kind === "inprocess" && options.workingDirectory !== void 0) {
|
|
444
469
|
throw new Error(
|
|
445
|
-
"
|
|
470
|
+
"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."
|
|
446
471
|
);
|
|
447
472
|
}
|
|
448
473
|
if (conn.kind === "inprocess" && options.telemetry !== void 0) {
|
|
@@ -508,6 +533,7 @@ class CopilotClient {
|
|
|
508
533
|
const connArgs = conn.kind === "stdio" || conn.kind === "tcp" ? conn.args ?? [] : [];
|
|
509
534
|
this.connectionExtraArgs = [...connArgs];
|
|
510
535
|
this.options = {
|
|
536
|
+
internalTestOptions: options.internalTestOptions,
|
|
511
537
|
entra: options.entra ? { ...options.entra, tokenProvider: options.entra.tokenProvider } : void 0,
|
|
512
538
|
workingDirectory: options.workingDirectory ?? process.cwd(),
|
|
513
539
|
logLevel: options.logLevel,
|
|
@@ -779,12 +805,17 @@ class CopilotClient {
|
|
|
779
805
|
* Stops the CLI server and closes all active sessions.
|
|
780
806
|
*
|
|
781
807
|
* This method performs graceful cleanup:
|
|
782
|
-
* 1. Closes all active sessions (releases in-memory resources)
|
|
808
|
+
* 1. Closes all active sessions (releases in-memory resources); detach
|
|
809
|
+
* requests share one shutdown timeout
|
|
783
810
|
* 2. Requests runtime shutdown for SDK-owned CLI processes
|
|
784
811
|
* 3. Closes the JSON-RPC connection
|
|
785
812
|
* 4. Signals EOF to an owned stdio process and waits for host cleanup, then
|
|
786
813
|
* terminates the process if it does not exit within the shutdown timeout
|
|
787
814
|
*
|
|
815
|
+
* In-process connections first abort active turns without a timeout. That
|
|
816
|
+
* step is outside the detach deadline, so in-process stop() can still wait
|
|
817
|
+
* indefinitely if the runtime stops answering abort requests.
|
|
818
|
+
*
|
|
788
819
|
* Note: session data on disk is preserved, so sessions can be resumed later.
|
|
789
820
|
* To permanently remove session data before stopping, call
|
|
790
821
|
* {@link deleteSession} for each session first.
|
|
@@ -806,16 +837,31 @@ class CopilotClient {
|
|
|
806
837
|
if (this.connectionConfig.kind === "inprocess") {
|
|
807
838
|
await Promise.allSettled(activeSessions.map((session) => session.abort()));
|
|
808
839
|
}
|
|
840
|
+
let detachDeadlineTimer;
|
|
841
|
+
const detachDeadline = new Promise((resolve2) => {
|
|
842
|
+
detachDeadlineTimer = setTimeout(() => resolve2("timeout"), RUNTIME_SHUTDOWN_TIMEOUT_MS);
|
|
843
|
+
});
|
|
809
844
|
for (const session of activeSessions) {
|
|
810
845
|
const sessionId = session.sessionId;
|
|
811
846
|
let lastError = null;
|
|
847
|
+
let attempts = 0;
|
|
812
848
|
for (let attempt = 1; attempt <= 3; attempt++) {
|
|
813
849
|
if (this.connectionClosed) {
|
|
814
850
|
lastError = null;
|
|
815
851
|
break;
|
|
816
852
|
}
|
|
853
|
+
attempts = attempt;
|
|
817
854
|
try {
|
|
818
|
-
await
|
|
855
|
+
const outcome = await Promise.race([
|
|
856
|
+
session.disconnect().then(() => "disconnected"),
|
|
857
|
+
detachDeadline
|
|
858
|
+
]);
|
|
859
|
+
if (outcome === "timeout") {
|
|
860
|
+
lastError = new Error(
|
|
861
|
+
`session.detach did not complete within the ${RUNTIME_SHUTDOWN_TIMEOUT_MS}ms shutdown timeout`
|
|
862
|
+
);
|
|
863
|
+
break;
|
|
864
|
+
}
|
|
819
865
|
lastError = null;
|
|
820
866
|
break;
|
|
821
867
|
} catch (error) {
|
|
@@ -833,11 +879,12 @@ class CopilotClient {
|
|
|
833
879
|
if (lastError) {
|
|
834
880
|
errors.push(
|
|
835
881
|
new Error(
|
|
836
|
-
`Failed to disconnect session ${sessionId} after
|
|
882
|
+
`Failed to disconnect session ${sessionId} after ${attempts} ${attempts === 1 ? "attempt" : "attempts"}: ${lastError.message}`
|
|
837
883
|
)
|
|
838
884
|
);
|
|
839
885
|
}
|
|
840
886
|
}
|
|
887
|
+
clearTimeout(detachDeadlineTimer);
|
|
841
888
|
for (const session of activeSessions) {
|
|
842
889
|
session._markDisconnected();
|
|
843
890
|
}
|
|
@@ -888,7 +935,7 @@ class CopilotClient {
|
|
|
888
935
|
this._rpc = null;
|
|
889
936
|
this._internalRpc = null;
|
|
890
937
|
}
|
|
891
|
-
this.modelsCache = null;
|
|
938
|
+
this.modelsCache = { models: null, lock: Promise.resolve() };
|
|
892
939
|
if (this.socket) {
|
|
893
940
|
const socket = this.socket;
|
|
894
941
|
this.socket = null;
|
|
@@ -1050,7 +1097,7 @@ class CopilotClient {
|
|
|
1050
1097
|
this._rpc = null;
|
|
1051
1098
|
this._internalRpc = null;
|
|
1052
1099
|
}
|
|
1053
|
-
this.modelsCache = null;
|
|
1100
|
+
this.modelsCache = { models: null, lock: Promise.resolve() };
|
|
1054
1101
|
if (this.socket) {
|
|
1055
1102
|
try {
|
|
1056
1103
|
this.socket.destroy();
|
|
@@ -1107,7 +1154,7 @@ class CopilotClient {
|
|
|
1107
1154
|
* // Session with model and tools
|
|
1108
1155
|
* const session = await client.createSession({
|
|
1109
1156
|
* onPermissionRequest: approveAll,
|
|
1110
|
-
* model: "gpt-4",
|
|
1157
|
+
* model: "gpt-5.4",
|
|
1111
1158
|
* tools: [{
|
|
1112
1159
|
* name: "get_weather",
|
|
1113
1160
|
* description: "Get weather for a location",
|
|
@@ -2069,14 +2116,16 @@ class CopilotClient {
|
|
|
2069
2116
|
* @throws Error if not connected (when no custom handler is set)
|
|
2070
2117
|
*/
|
|
2071
2118
|
async listModels() {
|
|
2072
|
-
|
|
2119
|
+
const cache = this.modelsCache;
|
|
2120
|
+
const previousLock = cache.lock;
|
|
2073
2121
|
let resolveLock;
|
|
2074
|
-
|
|
2122
|
+
cache.lock = new Promise((resolve2) => {
|
|
2075
2123
|
resolveLock = resolve2;
|
|
2076
2124
|
});
|
|
2125
|
+
await previousLock;
|
|
2077
2126
|
try {
|
|
2078
|
-
if (
|
|
2079
|
-
return [...
|
|
2127
|
+
if (cache.models !== null) {
|
|
2128
|
+
return [...cache.models];
|
|
2080
2129
|
}
|
|
2081
2130
|
let models;
|
|
2082
2131
|
if (this.onListModels) {
|
|
@@ -2105,7 +2154,7 @@ class CopilotClient {
|
|
|
2105
2154
|
}
|
|
2106
2155
|
}
|
|
2107
2156
|
}
|
|
2108
|
-
|
|
2157
|
+
cache.models = [...models];
|
|
2109
2158
|
return [...models];
|
|
2110
2159
|
} finally {
|
|
2111
2160
|
resolveLock();
|
|
@@ -2293,7 +2342,9 @@ class CopilotClient {
|
|
|
2293
2342
|
gitRoot: context.gitRoot,
|
|
2294
2343
|
repository: context.repository,
|
|
2295
2344
|
branch: context.branch
|
|
2296
|
-
} : void 0
|
|
2345
|
+
} : void 0,
|
|
2346
|
+
clientName: raw.clientName,
|
|
2347
|
+
lastClientName: raw.lastClientName
|
|
2297
2348
|
};
|
|
2298
2349
|
}
|
|
2299
2350
|
/**
|
|
@@ -2368,15 +2419,40 @@ class CopilotClient {
|
|
|
2368
2419
|
};
|
|
2369
2420
|
}
|
|
2370
2421
|
/**
|
|
2371
|
-
* Builds
|
|
2372
|
-
*
|
|
2373
|
-
* variables on top of the effective env. Not used by the in-process (FFI) transport,
|
|
2374
|
-
* whose worker inherits the host process's ambient environment
|
|
2375
|
-
* (see {@link CopilotClient.startInProcessFfi}).
|
|
2422
|
+
* Builds runtime configuration for child processes and the per-host FFI snapshot.
|
|
2423
|
+
* Typed auth, home, keychain, and telemetry options override the effective env.
|
|
2376
2424
|
*/
|
|
2377
2425
|
buildRuntimeEnv() {
|
|
2378
|
-
const env =
|
|
2426
|
+
const env = process.platform === "win32" ? Object.fromEntries(
|
|
2427
|
+
Object.entries(this.resolvedEnv).map(([key, value]) => [
|
|
2428
|
+
key.toUpperCase(),
|
|
2429
|
+
value
|
|
2430
|
+
])
|
|
2431
|
+
) : { ...this.resolvedEnv };
|
|
2379
2432
|
delete env.NODE_DEBUG;
|
|
2433
|
+
const test = this.options.internalTestOptions;
|
|
2434
|
+
if (test) {
|
|
2435
|
+
const overrides = {
|
|
2436
|
+
COPILOT_API_URL: test.copilotApiUrl,
|
|
2437
|
+
COPILOT_DEBUG_GITHUB_API_URL: test.githubApiUrl,
|
|
2438
|
+
HTTP_PROXY: test.proxyUrl,
|
|
2439
|
+
HTTPS_PROXY: test.proxyUrl,
|
|
2440
|
+
http_proxy: test.proxyUrl,
|
|
2441
|
+
https_proxy: test.proxyUrl,
|
|
2442
|
+
NO_PROXY: test.noProxy,
|
|
2443
|
+
no_proxy: test.noProxy,
|
|
2444
|
+
NODE_EXTRA_CA_CERTS: test.caCertificatePath,
|
|
2445
|
+
SSL_CERT_FILE: test.caCertificatePath,
|
|
2446
|
+
REQUESTS_CA_BUNDLE: test.caCertificatePath,
|
|
2447
|
+
CURL_CA_BUNDLE: test.caCertificatePath,
|
|
2448
|
+
GIT_SSL_CAINFO: test.caCertificatePath
|
|
2449
|
+
};
|
|
2450
|
+
for (const [key, value] of Object.entries(overrides)) {
|
|
2451
|
+
if (value !== void 0) {
|
|
2452
|
+
env[process.platform === "win32" ? key.toUpperCase() : key] = value;
|
|
2453
|
+
}
|
|
2454
|
+
}
|
|
2455
|
+
}
|
|
2380
2456
|
if (this.options.gitHubToken) {
|
|
2381
2457
|
env.COPILOT_SDK_AUTH_TOKEN = this.options.gitHubToken;
|
|
2382
2458
|
}
|
|
@@ -2385,6 +2461,9 @@ class CopilotClient {
|
|
|
2385
2461
|
}
|
|
2386
2462
|
if (this.options.baseDirectory) {
|
|
2387
2463
|
env.COPILOT_HOME = this.options.baseDirectory;
|
|
2464
|
+
if (this.connectionConfig.kind === "inprocess") {
|
|
2465
|
+
env.COPILOT_CACHE_HOME = join(this.options.baseDirectory, "cache");
|
|
2466
|
+
}
|
|
2388
2467
|
}
|
|
2389
2468
|
if (this.options.mode === "empty") {
|
|
2390
2469
|
env.COPILOT_DISABLE_KEYTAR = "1";
|
|
@@ -2411,7 +2490,7 @@ class CopilotClient {
|
|
|
2411
2490
|
* Start the CLI server process
|
|
2412
2491
|
*/
|
|
2413
2492
|
async startCLIServer() {
|
|
2414
|
-
this.resolvedCliPath ??= await
|
|
2493
|
+
this.resolvedCliPath ??= await getSpawnableBundledRuntimePath();
|
|
2415
2494
|
return new Promise((resolve2, reject) => {
|
|
2416
2495
|
this.stderrBuffer = "";
|
|
2417
2496
|
const args = [...this.connectionExtraArgs, "--headless", "--no-auto-update"];
|
|
@@ -2449,6 +2528,22 @@ class CopilotClient {
|
|
|
2449
2528
|
`Copilot CLI not found at ${this.resolvedCliPath}. Set COPILOT_CLI_PATH to use a custom installation.`
|
|
2450
2529
|
);
|
|
2451
2530
|
}
|
|
2531
|
+
const workingDirectory = this.options.workingDirectory;
|
|
2532
|
+
let workingDirectoryProblem;
|
|
2533
|
+
try {
|
|
2534
|
+
workingDirectoryProblem = statSync(workingDirectory).isDirectory() ? void 0 : "does not exist or is not a directory";
|
|
2535
|
+
if (!workingDirectoryProblem) {
|
|
2536
|
+
accessSync(workingDirectory, constants.X_OK);
|
|
2537
|
+
}
|
|
2538
|
+
} catch (error) {
|
|
2539
|
+
const code = error.code;
|
|
2540
|
+
workingDirectoryProblem = code === "ENOENT" || code === "ENOTDIR" ? "does not exist or is not a directory" : `cannot be entered: ${error instanceof Error ? error.message : String(error)}`;
|
|
2541
|
+
}
|
|
2542
|
+
if (workingDirectoryProblem) {
|
|
2543
|
+
throw new Error(
|
|
2544
|
+
`Working directory ${workingDirectory} ${workingDirectoryProblem}.`
|
|
2545
|
+
);
|
|
2546
|
+
}
|
|
2452
2547
|
const stdioConfig = this.connectionConfig.kind === "stdio" ? ["pipe", "pipe", "pipe"] : ["ignore", "pipe", "pipe"];
|
|
2453
2548
|
const isJsFile = this.resolvedCliPath.endsWith(".js");
|
|
2454
2549
|
if (isJsFile) {
|
|
@@ -2600,16 +2695,7 @@ stderr: ${this.stderrBuffer.trim().slice(-8192) || "(empty)"}`
|
|
|
2600
2695
|
runtimeLibrary = join(dirname(bundledRuntimePath), "runtime.node");
|
|
2601
2696
|
}
|
|
2602
2697
|
const { FfiRuntimeHost } = await import("./ffiRuntimeHost.js");
|
|
2603
|
-
const environment =
|
|
2604
|
-
if (this.options.gitHubToken) {
|
|
2605
|
-
environment.COPILOT_SDK_AUTH_TOKEN = this.options.gitHubToken;
|
|
2606
|
-
}
|
|
2607
|
-
if (this.options.baseDirectory) {
|
|
2608
|
-
environment.COPILOT_HOME = this.options.baseDirectory;
|
|
2609
|
-
}
|
|
2610
|
-
if (this.options.mode === "empty") {
|
|
2611
|
-
environment.COPILOT_DISABLE_KEYTAR = "1";
|
|
2612
|
-
}
|
|
2698
|
+
const environment = this.buildRuntimeEnv();
|
|
2613
2699
|
const args = [];
|
|
2614
2700
|
if (this.options.logLevel) {
|
|
2615
2701
|
args.push("--log-level", this.options.logLevel);
|
|
@@ -2870,6 +2956,14 @@ stderr: ${stderrOutput}` : ""}`
|
|
|
2870
2956
|
"skillProvider.list",
|
|
2871
2957
|
async (params, token) => await this.resolveSkillProviderSession(params)._handleSkillProviderList(token)
|
|
2872
2958
|
);
|
|
2959
|
+
this.connection.onRequest(
|
|
2960
|
+
"sessionManagement.create",
|
|
2961
|
+
async (params, token) => {
|
|
2962
|
+
const session = this.sessions.get(params.sessionId);
|
|
2963
|
+
if (!session) throw new Error("Session creation owner is unavailable");
|
|
2964
|
+
return await session._handleSessionManagementCreate(params, token);
|
|
2965
|
+
}
|
|
2966
|
+
);
|
|
2873
2967
|
this.connection.onRequest(
|
|
2874
2968
|
"skillProvider.read",
|
|
2875
2969
|
async (params, token) => {
|
|
@@ -2907,7 +3001,7 @@ stderr: ${stderrOutput}` : ""}`
|
|
|
2907
3001
|
this.messageWriter = null;
|
|
2908
3002
|
this._rpc = null;
|
|
2909
3003
|
this._internalRpc = null;
|
|
2910
|
-
this.modelsCache = null;
|
|
3004
|
+
this.modelsCache = { models: null, lock: Promise.resolve() };
|
|
2911
3005
|
};
|
|
2912
3006
|
const disconnectAfterDrain = () => {
|
|
2913
3007
|
if (exitFallback !== void 0) {
|
|
@@ -2942,7 +3036,10 @@ stderr: ${stderrOutput}` : ""}`
|
|
|
2942
3036
|
exitFallback.unref?.();
|
|
2943
3037
|
});
|
|
2944
3038
|
}
|
|
2945
|
-
this.connection.onError(() => {
|
|
3039
|
+
this.connection.onError(([error]) => {
|
|
3040
|
+
if (messageEncodeErrors.has(error)) {
|
|
3041
|
+
return;
|
|
3042
|
+
}
|
|
2946
3043
|
if (connectionEpoch === this.connectionEpoch && this.connection === connection) {
|
|
2947
3044
|
this.state = "disconnected";
|
|
2948
3045
|
}
|
|
@@ -3003,7 +3100,8 @@ stderr: ${stderrOutput}` : ""}`
|
|
|
3003
3100
|
const result = await session._handleUserInputRequest({
|
|
3004
3101
|
question: params.question,
|
|
3005
3102
|
choices: params.choices,
|
|
3006
|
-
allowFreeform: params.allowFreeform
|
|
3103
|
+
allowFreeform: params.allowFreeform,
|
|
3104
|
+
requestId: params.requestId
|
|
3007
3105
|
});
|
|
3008
3106
|
return result;
|
|
3009
3107
|
}
|
|
@@ -3019,7 +3117,8 @@ stderr: ${stderrOutput}` : ""}`
|
|
|
3019
3117
|
summary: params.summary,
|
|
3020
3118
|
planContent: params.planContent,
|
|
3021
3119
|
actions: params.actions,
|
|
3022
|
-
recommendedAction: params.recommendedAction
|
|
3120
|
+
recommendedAction: params.recommendedAction,
|
|
3121
|
+
requestId: params.requestId
|
|
3023
3122
|
});
|
|
3024
3123
|
}
|
|
3025
3124
|
async handleAutoModeSwitchRequest(params) {
|
|
@@ -82,7 +82,9 @@ class CopilotWebSocketForwarder extends CopilotWebSocketHandler {
|
|
|
82
82
|
if (this.#upstream) {
|
|
83
83
|
return;
|
|
84
84
|
}
|
|
85
|
-
const upstream = new WebSocket(this.context.url
|
|
85
|
+
const upstream = new WebSocket(this.context.url, {
|
|
86
|
+
headers: forwardedRequestHeaders(this.context.headers)
|
|
87
|
+
});
|
|
86
88
|
upstream.binaryType = "arraybuffer";
|
|
87
89
|
this.#upstream = upstream;
|
|
88
90
|
upstream.addEventListener("message", (event) => {
|
|
@@ -122,13 +124,16 @@ class CopilotWebSocketForwarder extends CopilotWebSocketHandler {
|
|
|
122
124
|
});
|
|
123
125
|
}
|
|
124
126
|
async close(status = CopilotWebSocketCloseStatus.normalClosure) {
|
|
127
|
+
const upstream = this.#upstream;
|
|
128
|
+
this.#upstream = null;
|
|
129
|
+
const completion = super.close(status);
|
|
125
130
|
try {
|
|
126
|
-
if (
|
|
127
|
-
|
|
131
|
+
if (upstream?.readyState === WebSocket.OPEN || upstream?.readyState === WebSocket.CONNECTING) {
|
|
132
|
+
upstream.close();
|
|
128
133
|
}
|
|
129
134
|
} catch {
|
|
130
135
|
}
|
|
131
|
-
await
|
|
136
|
+
await completion;
|
|
132
137
|
}
|
|
133
138
|
async [Symbol.asyncDispose]() {
|
|
134
139
|
try {
|
|
@@ -467,19 +472,24 @@ const FORBIDDEN_REQUEST_HEADERS = /* @__PURE__ */ new Set([
|
|
|
467
472
|
"te",
|
|
468
473
|
"trailer"
|
|
469
474
|
]);
|
|
470
|
-
|
|
475
|
+
function forwardedRequestHeaders(source) {
|
|
471
476
|
const headers = new Headers();
|
|
472
|
-
for (const [name, values] of Object.entries(
|
|
477
|
+
for (const [name, values] of Object.entries(source)) {
|
|
473
478
|
if (!values) {
|
|
474
479
|
continue;
|
|
475
480
|
}
|
|
476
|
-
|
|
481
|
+
const lower = name.toLowerCase();
|
|
482
|
+
if (FORBIDDEN_REQUEST_HEADERS.has(lower) || lower.startsWith("sec-websocket-")) {
|
|
477
483
|
continue;
|
|
478
484
|
}
|
|
479
485
|
for (const value of values) {
|
|
480
486
|
headers.append(name, value);
|
|
481
487
|
}
|
|
482
488
|
}
|
|
489
|
+
return headers;
|
|
490
|
+
}
|
|
491
|
+
async function buildFetchRequest(exchange) {
|
|
492
|
+
const headers = forwardedRequestHeaders(exchange.headers);
|
|
483
493
|
const method = exchange.method.toUpperCase();
|
|
484
494
|
const hasBody = method !== "GET" && method !== "HEAD";
|
|
485
495
|
let body;
|
|
@@ -116,6 +116,10 @@ export interface RuntimeLifecycleProgressStateDiagnosticEvent {
|
|
|
116
116
|
* Experimental, observational-only state of runtime progress and the lifecycle operations that block it. Observation and delivery never control runtime or model behavior. Process death, diagnostic projection loss, transport loss, or serialization failure can prevent external delivery.
|
|
117
117
|
*/
|
|
118
118
|
export interface RuntimeLifecycleProgressStateData {
|
|
119
|
+
/**
|
|
120
|
+
* Agent that owns this progress. Absent for the primary agent and progress without an agent owner.
|
|
121
|
+
*/
|
|
122
|
+
agentId?: string;
|
|
119
123
|
/**
|
|
120
124
|
* Complete dependency blocker set after this progress change.
|
|
121
125
|
*/
|