@github/copilot-sdk 1.0.29-preview.1 → 1.0.29-preview.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.
@@ -336,8 +336,10 @@ class CopilotClient {
336
336
  effectiveConnectionToken;
337
337
  onListModels;
338
338
  onGetTraceContext;
339
- modelsCache = null;
340
- modelsCacheLock = Promise.resolve();
339
+ modelsCache = {
340
+ models: null,
341
+ lock: Promise.resolve()
342
+ };
341
343
  sessionLifecycleHandlers = /* @__PURE__ */ new Set();
342
344
  typedLifecycleHandlers = /* @__PURE__ */ new Map();
343
345
  _rpc = null;
@@ -906,7 +908,7 @@ class CopilotClient {
906
908
  this._rpc = null;
907
909
  this._internalRpc = null;
908
910
  }
909
- this.modelsCache = null;
911
+ this.modelsCache = { models: null, lock: Promise.resolve() };
910
912
  if (this.socket) {
911
913
  const socket = this.socket;
912
914
  this.socket = null;
@@ -1068,7 +1070,7 @@ class CopilotClient {
1068
1070
  this._rpc = null;
1069
1071
  this._internalRpc = null;
1070
1072
  }
1071
- this.modelsCache = null;
1073
+ this.modelsCache = { models: null, lock: Promise.resolve() };
1072
1074
  if (this.socket) {
1073
1075
  try {
1074
1076
  this.socket.destroy();
@@ -2087,14 +2089,16 @@ class CopilotClient {
2087
2089
  * @throws Error if not connected (when no custom handler is set)
2088
2090
  */
2089
2091
  async listModels() {
2090
- await this.modelsCacheLock;
2092
+ const cache = this.modelsCache;
2093
+ const previousLock = cache.lock;
2091
2094
  let resolveLock;
2092
- this.modelsCacheLock = new Promise((resolve2) => {
2095
+ cache.lock = new Promise((resolve2) => {
2093
2096
  resolveLock = resolve2;
2094
2097
  });
2098
+ await previousLock;
2095
2099
  try {
2096
- if (this.modelsCache !== null) {
2097
- return [...this.modelsCache];
2100
+ if (cache.models !== null) {
2101
+ return [...cache.models];
2098
2102
  }
2099
2103
  let models;
2100
2104
  if (this.onListModels) {
@@ -2123,7 +2127,7 @@ class CopilotClient {
2123
2127
  }
2124
2128
  }
2125
2129
  }
2126
- this.modelsCache = [...models];
2130
+ cache.models = [...models];
2127
2131
  return [...models];
2128
2132
  } finally {
2129
2133
  resolveLock();
@@ -2888,6 +2892,14 @@ stderr: ${stderrOutput}` : ""}`
2888
2892
  "skillProvider.list",
2889
2893
  async (params, token) => await this.resolveSkillProviderSession(params)._handleSkillProviderList(token)
2890
2894
  );
2895
+ this.connection.onRequest(
2896
+ "sessionManagement.create",
2897
+ async (params, token) => {
2898
+ const session = this.sessions.get(params.sessionId);
2899
+ if (!session) throw new Error("Session creation owner is unavailable");
2900
+ return await session._handleSessionManagementCreate(params, token);
2901
+ }
2902
+ );
2891
2903
  this.connection.onRequest(
2892
2904
  "skillProvider.read",
2893
2905
  async (params, token) => {
@@ -2925,7 +2937,7 @@ stderr: ${stderrOutput}` : ""}`
2925
2937
  this.messageWriter = null;
2926
2938
  this._rpc = null;
2927
2939
  this._internalRpc = null;
2928
- this.modelsCache = null;
2940
+ this.modelsCache = { models: null, lock: Promise.resolve() };
2929
2941
  };
2930
2942
  const disconnectAfterDrain = () => {
2931
2943
  if (exitFallback !== void 0) {
@@ -3021,7 +3033,8 @@ stderr: ${stderrOutput}` : ""}`
3021
3033
  const result = await session._handleUserInputRequest({
3022
3034
  question: params.question,
3023
3035
  choices: params.choices,
3024
- allowFreeform: params.allowFreeform
3036
+ allowFreeform: params.allowFreeform,
3037
+ requestId: params.requestId
3025
3038
  });
3026
3039
  return result;
3027
3040
  }
@@ -3037,7 +3050,8 @@ stderr: ${stderrOutput}` : ""}`
3037
3050
  summary: params.summary,
3038
3051
  planContent: params.planContent,
3039
3052
  actions: params.actions,
3040
- recommendedAction: params.recommendedAction
3053
+ recommendedAction: params.recommendedAction,
3054
+ requestId: params.requestId
3041
3055
  });
3042
3056
  }
3043
3057
  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 (this.#upstream?.readyState === WebSocket.OPEN || this.#upstream?.readyState === WebSocket.CONNECTING) {
154
- this.#upstream?.close();
158
+ if (upstream?.readyState === WebSocket.OPEN || upstream?.readyState === WebSocket.CONNECTING) {
159
+ upstream.close();
155
160
  }
156
161
  } catch {
157
162
  }
158
- await super.close(status);
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
- async function buildFetchRequest(exchange) {
502
+ function forwardedRequestHeaders(source) {
498
503
  const headers = new Headers();
499
- for (const [name, values] of Object.entries(exchange.headers)) {
504
+ for (const [name, values] of Object.entries(source)) {
500
505
  if (!values) {
501
506
  continue;
502
507
  }
503
- if (FORBIDDEN_REQUEST_HEADERS.has(name.toLowerCase())) {
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;
@@ -841,9 +841,17 @@ function createServerRpc(connection) {
841
841
  *
842
842
  * @param params A bounded catalog search. Both the query length and the result count are capped by the schema so a caller cannot request an unbounded scan.
843
843
  *
844
- * @returns Outcome of a catalog.search call: either bounded inert candidates, or one typed refusal. Never a partial success.
844
+ * @returns Outcome of catalog.search or catalog.recommend: either bounded inert candidates, or one typed refusal. Never a partial success.
845
845
  */
846
846
  search: async (params) => connection.sendRequest("catalog.search", params),
847
+ /**
848
+ * Requests the initial page of at most ten official-product recommendations for a complete task and product, bound to an existing attached local session. Requires catalog-recommend, catalog-search-session-bound and catalog-search-credential-required. Uses the selected GitHub.com user credential on the fixed Agent Finder recommendations route; successful capability negotiation does not establish service acceptance. Returns the existing inert catalog candidates and retained install references with the typed catalog refusal union. No anonymous retry, unfiltered search fallback or continuation is performed. Server-owned product approvals establish provenance, not task suitability. Read-only: nothing is installed, configured or persisted.
849
+ *
850
+ * @param params Initial-page official-product recommendations for a complete task. The runtime fixes recommendationPolicy to official-product and pageSize to 10; callers cannot override the policy, request pagination or fall back to search.
851
+ *
852
+ * @returns Outcome of catalog.search or catalog.recommend: either bounded inert candidates, or one typed refusal. Never a partial success.
853
+ */
854
+ recommend: async (params) => connection.sendRequest("catalog.recommend", params),
847
855
  /**
848
856
  * Retrieves one saved feed by its opaque ID, optionally filtered by trust tier. Requires catalog-feeds and catalog-search-credential-required, and uses the same authority, credential policy, optional session binding and candidate projection as catalog.search, so available entries are the same inert candidates, with the same installability, handles and display metadata, and install through the same confirmed flows. Entries keep the feed's order; an entry the runtime cannot present is returned as unavailable with a reason rather than failing the feed. No anonymous retry. Read-only: nothing is installed, configured, or persisted.
849
857
  *
@@ -2174,6 +2182,49 @@ function createInternalServerRpc(connection) {
2174
2182
  }
2175
2183
  function createSessionRpc(connection, sessionId) {
2176
2184
  return {
2185
+ /** @experimental */
2186
+ management: {
2187
+ /**
2188
+ * Updates opt-in session management policy live. Enabling the master enables all tools before applying individual overrides. Opt-in is scoped to the session's configuration directory.
2189
+ *
2190
+ * @param params Updates the session's live policy without changing persistent host settings.
2191
+ *
2192
+ * @returns Opt-in policy for local session management.
2193
+ */
2194
+ configure: async (params) => connection.sendRequest("session.management.configure", { ...params, sessionId }),
2195
+ /**
2196
+ * Lists other live, opted-in sessions in the same configuration directory. Requires the list tool to be enabled.
2197
+ *
2198
+ * @param params Lists opted-in local peers. The caller is omitted.
2199
+ *
2200
+ * @returns Eligible live peers reported by their owning runtimes.
2201
+ */
2202
+ list: async (params) => connection.sendRequest("session.management.list", { ...params, sessionId }),
2203
+ /**
2204
+ * Injects peer text into an opted-in session. Wakes an idle agent and safely queues delivery to a busy agent. Acceptance does not imply an answer or completed task.
2205
+ *
2206
+ * @param params Sends peer text, never human approval, to another opted-in session.
2207
+ *
2208
+ * @returns Acceptance is not a completed turn or a reply.
2209
+ */
2210
+ message: async (params) => connection.sendRequest("session.management.message", { ...params, sessionId }),
2211
+ /**
2212
+ * Asks the owning host to create and supervise a separate session, preserving permissions and model restrictions. Omitted model, reasoning effort, context tier and cwd inherit the caller's current values; explicit overrides use normal model validation and workspace trust. Records durable creation lineage before delivering the initial task as peer text. Returns the new session ID, not task completion. Requires the create tool and a host creation handler.
2213
+ *
2214
+ * @param params Requests a separate session. Omitted launch options inherit the caller's current configuration.
2215
+ *
2216
+ * @returns Identifies a session owned and supervised by the creating host.
2217
+ */
2218
+ create: async (params) => connection.sendRequest("session.management.create", { ...params, sessionId }),
2219
+ /**
2220
+ * Closes a live opted-in session created by the caller or one of its descendants, preserving saved history and leaving descendants open. Requires the close capability to be enabled. Self, ancestors, siblings and unrelated sessions cannot be closed.
2221
+ *
2222
+ * @param params Closes one live opted-in descendant without deleting its saved history or closing its children.
2223
+ *
2224
+ * @returns Completion of the existing non-deleting close lifecycle.
2225
+ */
2226
+ close: async (params) => connection.sendRequest("session.management.close", { ...params, sessionId })
2227
+ },
2177
2228
  /** @experimental */
2178
2229
  providers: {
2179
2230
  /**
@@ -2811,7 +2862,13 @@ function createSessionRpc(connection, sessionId) {
2811
2862
  *
2812
2863
  * @returns Canonical runtime state for the session's current autopilot objective.
2813
2864
  */
2814
- getState: async () => connection.sendRequest("session.autopilotObjective.getState", { sessionId })
2865
+ getState: async () => connection.sendRequest("session.autopilotObjective.getState", { sessionId }),
2866
+ /**
2867
+ * Sets whether completing or pausing this session's objective preserves autopilot mode. This in-memory preference defaults to false and must be reapplied when attaching a client.
2868
+ *
2869
+ * @param params Client-resolved preference for retaining autopilot mode after objective completion or pause.
2870
+ */
2871
+ setStayInAutopilot: async (params) => connection.sendRequest("session.autopilotObjective.setStayInAutopilot", { ...params, sessionId })
2815
2872
  },
2816
2873
  /** @experimental */
2817
2874
  completions: {
@@ -3551,6 +3608,12 @@ function createSessionRpc(connection, sessionId) {
3551
3608
  * @returns The selectable model entries and selection ids synthesized for the synchronized BYOK models.
3552
3609
  */
3553
3610
  sync: async (params) => connection.sendRequest("session.provider.sync", { ...params, sessionId }),
3611
+ /**
3612
+ * Returns credential-free provider configuration status for the selected model. This does not validate provider credentials or contact the provider.
3613
+ *
3614
+ * @returns Credential-free provider configuration status for the selected model.
3615
+ */
3616
+ getStatus: async () => connection.sendRequest("session.provider.getStatus", { sessionId }),
3554
3617
  /**
3555
3618
  * Withdraws named host-managed models from the session's BYOK registry, leaving every other entry untouched. The scoped counterpart to `provider.sync`: a snapshot can only describe entries the caller knows about, so using it to remove one model silently withdraws rows registered by another source, such as a plugin calling `provider.add` at runtime. Naming what to remove leaves unrelated entries alone. Selection ids that are not registered are ignored, so withdrawal is idempotent. A provider is removed only when one of the withdrawn models was the last entry referencing it; a provider that simply has no models, which is the normal state while its rows are supplied by catalog discovery, is left in place.
3556
3619
  *
@@ -3891,6 +3954,12 @@ function createSessionRpc(connection, sessionId) {
3891
3954
  * @returns Current permission mode.
3892
3955
  */
3893
3956
  getMode: async () => connection.sendRequest("session.permissions.getMode", { sessionId }),
3957
+ /**
3958
+ * Returns the approval modes currently available under the session's feature and managed permission policy. This read does not change permissions; mutations must still check their authoritative result because policy may change.
3959
+ *
3960
+ * @returns Available permission modes, derived from current session policy.
3961
+ */
3962
+ getCapabilities: async () => connection.sendRequest("session.permissions.getCapabilities", { sessionId }),
3894
3963
  /**
3895
3964
  * Adds or removes session-scoped or location-scoped permission rules.
3896
3965
  *
@@ -170,6 +170,11 @@ class WorkflowProgressBuffer {
170
170
  if (this.closed) {
171
171
  throw new Error("Cannot log after the workflow run has settled");
172
172
  }
173
+ if (typeof text !== "string") {
174
+ throw new TypeError(
175
+ `${kind}() expects a string; got ${text === null ? "null" : typeof text}.`
176
+ );
177
+ }
173
178
  this.pending.push({ seq: this.nextSeq++, kind, text });
174
179
  this.scheduleFlush();
175
180
  }
@@ -297,6 +302,7 @@ class CopilotSession {
297
302
  /** Settles once every earlier `setTools` call has finished. */
298
303
  setToolsQueue = Promise.resolve();
299
304
  pendingExternalTools = /* @__PURE__ */ new Map();
305
+ pendingSessionCreations = /* @__PURE__ */ new Set();
300
306
  canvases = /* @__PURE__ */ new Map();
301
307
  bearerTokenProviders = /* @__PURE__ */ new Map();
302
308
  commandHandlers = /* @__PURE__ */ new Map();
@@ -309,6 +315,7 @@ class CopilotSession {
309
315
  exitPlanModeHandler;
310
316
  autoModeSwitchHandler;
311
317
  skillProvider;
318
+ sessionCreationHandler;
312
319
  hooks;
313
320
  transformCallbacks;
314
321
  _rpc = null;
@@ -896,6 +903,10 @@ class CopilotSession {
896
903
  controller.abort();
897
904
  }
898
905
  this.pendingExternalTools.clear();
906
+ for (const controller of this.pendingSessionCreations) {
907
+ controller.abort();
908
+ }
909
+ this.pendingSessionCreations.clear();
899
910
  this.eventHandlers.clear();
900
911
  this.typedEventHandlers.clear();
901
912
  this.toolHandlers.clear();
@@ -906,6 +917,7 @@ class CopilotSession {
906
917
  this.exitPlanModeHandler = void 0;
907
918
  this.autoModeSwitchHandler = void 0;
908
919
  this.skillProvider = void 0;
920
+ this.sessionCreationHandler = void 0;
909
921
  this.hooks = void 0;
910
922
  this.traceContextProvider = void 0;
911
923
  this.bearerTokenProviders.clear();
@@ -1575,6 +1587,7 @@ class CopilotSession {
1575
1587
  assertWorkflowResult(result);
1576
1588
  return { result };
1577
1589
  } finally {
1590
+ controller.abort(new DOMException("Workflow run has settled", "AbortError"));
1578
1591
  try {
1579
1592
  await progress.close();
1580
1593
  } finally {
@@ -1869,6 +1882,41 @@ class CopilotSession {
1869
1882
  this.assertConnected();
1870
1883
  this.skillProvider = provider;
1871
1884
  }
1885
+ /**
1886
+ * Registers the host's creation callback without enabling the tool group.
1887
+ * Enable it separately with `session.rpc.management.configure({ enabled: true })`.
1888
+ */
1889
+ async setSessionCreationHandler(handler) {
1890
+ this.assertConnected();
1891
+ const previous = this.sessionCreationHandler;
1892
+ this.sessionCreationHandler = handler;
1893
+ try {
1894
+ return await this.rpc.management.configure({ hasCreateHandler: handler !== void 0 });
1895
+ } catch (error) {
1896
+ if (this.sessionCreationHandler === handler) this.sessionCreationHandler = previous;
1897
+ throw error;
1898
+ }
1899
+ }
1900
+ /** @internal */
1901
+ async _handleSessionManagementCreate(request, token) {
1902
+ this.assertConnected();
1903
+ const handler = this.sessionCreationHandler;
1904
+ if (!handler || request.parentSessionId !== this.sessionId || request.sessionId !== this.sessionId) {
1905
+ throw new Error("Session creation handler is unavailable");
1906
+ }
1907
+ const controller = new AbortController();
1908
+ this.pendingSessionCreations.add(controller);
1909
+ const subscription = token.onCancellationRequested(() => controller.abort());
1910
+ if (token.isCancellationRequested) controller.abort();
1911
+ try {
1912
+ return await handler(request, { signal: controller.signal });
1913
+ } catch {
1914
+ throw new Error("Session creation callback failed");
1915
+ } finally {
1916
+ subscription.dispose();
1917
+ this.pendingSessionCreations.delete(controller);
1918
+ }
1919
+ }
1872
1920
  /**
1873
1921
  * Handles a `skillProvider.list` request from the runtime.
1874
1922
  *
@@ -1,2 +1,2 @@
1
- export declare const COPILOT_CLI_VERSION = "1.0.106-2";
1
+ export declare const COPILOT_CLI_VERSION = "1.0.106-4";
2
2
  export declare const COPILOT_CLI_USE_NPM_PACKAGE = false;
@@ -1,4 +1,4 @@
1
- const COPILOT_CLI_VERSION = "1.0.106-2";
1
+ const COPILOT_CLI_VERSION = "1.0.106-4";
2
2
  const COPILOT_CLI_USE_NPM_PACKAGE = false;
3
3
  export {
4
4
  COPILOT_CLI_USE_NPM_PACKAGE,
package/dist/client.d.ts CHANGED
@@ -38,7 +38,6 @@ export declare class CopilotClient {
38
38
  private onListModels?;
39
39
  private onGetTraceContext?;
40
40
  private modelsCache;
41
- private modelsCacheLock;
42
41
  private sessionLifecycleHandlers;
43
42
  private typedLifecycleHandlers;
44
43
  private _rpc;
package/dist/client.js CHANGED
@@ -318,8 +318,10 @@ class CopilotClient {
318
318
  effectiveConnectionToken;
319
319
  onListModels;
320
320
  onGetTraceContext;
321
- modelsCache = null;
322
- modelsCacheLock = Promise.resolve();
321
+ modelsCache = {
322
+ models: null,
323
+ lock: Promise.resolve()
324
+ };
323
325
  sessionLifecycleHandlers = /* @__PURE__ */ new Set();
324
326
  typedLifecycleHandlers = /* @__PURE__ */ new Map();
325
327
  _rpc = null;
@@ -888,7 +890,7 @@ class CopilotClient {
888
890
  this._rpc = null;
889
891
  this._internalRpc = null;
890
892
  }
891
- this.modelsCache = null;
893
+ this.modelsCache = { models: null, lock: Promise.resolve() };
892
894
  if (this.socket) {
893
895
  const socket = this.socket;
894
896
  this.socket = null;
@@ -1050,7 +1052,7 @@ class CopilotClient {
1050
1052
  this._rpc = null;
1051
1053
  this._internalRpc = null;
1052
1054
  }
1053
- this.modelsCache = null;
1055
+ this.modelsCache = { models: null, lock: Promise.resolve() };
1054
1056
  if (this.socket) {
1055
1057
  try {
1056
1058
  this.socket.destroy();
@@ -2069,14 +2071,16 @@ class CopilotClient {
2069
2071
  * @throws Error if not connected (when no custom handler is set)
2070
2072
  */
2071
2073
  async listModels() {
2072
- await this.modelsCacheLock;
2074
+ const cache = this.modelsCache;
2075
+ const previousLock = cache.lock;
2073
2076
  let resolveLock;
2074
- this.modelsCacheLock = new Promise((resolve2) => {
2077
+ cache.lock = new Promise((resolve2) => {
2075
2078
  resolveLock = resolve2;
2076
2079
  });
2080
+ await previousLock;
2077
2081
  try {
2078
- if (this.modelsCache !== null) {
2079
- return [...this.modelsCache];
2082
+ if (cache.models !== null) {
2083
+ return [...cache.models];
2080
2084
  }
2081
2085
  let models;
2082
2086
  if (this.onListModels) {
@@ -2105,7 +2109,7 @@ class CopilotClient {
2105
2109
  }
2106
2110
  }
2107
2111
  }
2108
- this.modelsCache = [...models];
2112
+ cache.models = [...models];
2109
2113
  return [...models];
2110
2114
  } finally {
2111
2115
  resolveLock();
@@ -2870,6 +2874,14 @@ stderr: ${stderrOutput}` : ""}`
2870
2874
  "skillProvider.list",
2871
2875
  async (params, token) => await this.resolveSkillProviderSession(params)._handleSkillProviderList(token)
2872
2876
  );
2877
+ this.connection.onRequest(
2878
+ "sessionManagement.create",
2879
+ async (params, token) => {
2880
+ const session = this.sessions.get(params.sessionId);
2881
+ if (!session) throw new Error("Session creation owner is unavailable");
2882
+ return await session._handleSessionManagementCreate(params, token);
2883
+ }
2884
+ );
2873
2885
  this.connection.onRequest(
2874
2886
  "skillProvider.read",
2875
2887
  async (params, token) => {
@@ -2907,7 +2919,7 @@ stderr: ${stderrOutput}` : ""}`
2907
2919
  this.messageWriter = null;
2908
2920
  this._rpc = null;
2909
2921
  this._internalRpc = null;
2910
- this.modelsCache = null;
2922
+ this.modelsCache = { models: null, lock: Promise.resolve() };
2911
2923
  };
2912
2924
  const disconnectAfterDrain = () => {
2913
2925
  if (exitFallback !== void 0) {
@@ -3003,7 +3015,8 @@ stderr: ${stderrOutput}` : ""}`
3003
3015
  const result = await session._handleUserInputRequest({
3004
3016
  question: params.question,
3005
3017
  choices: params.choices,
3006
- allowFreeform: params.allowFreeform
3018
+ allowFreeform: params.allowFreeform,
3019
+ requestId: params.requestId
3007
3020
  });
3008
3021
  return result;
3009
3022
  }
@@ -3019,7 +3032,8 @@ stderr: ${stderrOutput}` : ""}`
3019
3032
  summary: params.summary,
3020
3033
  planContent: params.planContent,
3021
3034
  actions: params.actions,
3022
- recommendedAction: params.recommendedAction
3035
+ recommendedAction: params.recommendedAction,
3036
+ requestId: params.requestId
3023
3037
  });
3024
3038
  }
3025
3039
  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 (this.#upstream?.readyState === WebSocket.OPEN || this.#upstream?.readyState === WebSocket.CONNECTING) {
127
- this.#upstream?.close();
131
+ if (upstream?.readyState === WebSocket.OPEN || upstream?.readyState === WebSocket.CONNECTING) {
132
+ upstream.close();
128
133
  }
129
134
  } catch {
130
135
  }
131
- await super.close(status);
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
- async function buildFetchRequest(exchange) {
475
+ function forwardedRequestHeaders(source) {
471
476
  const headers = new Headers();
472
- for (const [name, values] of Object.entries(exchange.headers)) {
477
+ for (const [name, values] of Object.entries(source)) {
473
478
  if (!values) {
474
479
  continue;
475
480
  }
476
- if (FORBIDDEN_REQUEST_HEADERS.has(name.toLowerCase())) {
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;