@github/copilot-sdk 1.0.29 → 1.0.30-preview.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/session.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { createSessionRpc } from "./generated/rpc.js";
2
- import type { ModelSwitchAutoTierResult, SessionManagementPolicy } from "./generated/rpc.js";
2
+ import type { ModelSwitchAutoTierResult, PermissionsSetModeResult, SessionManagementPolicy } from "./generated/rpc.js";
3
3
  import type { OpenCanvasInstance } from "./generated/rpc.js";
4
4
  import type { MessageOptions, ResponseSchema, ContextTier, ReasoningEffort, ReasoningSummary, AutoTier, ModelCapabilitiesOverride, SessionCapabilities, SessionConfig, SessionEvent, SessionEventHandler, SessionEventType, SessionUiApi, SessionCreationHandler, TranscriptRecovery, TypedSessionEventHandler } from "./types.js";
5
5
  import { type SessionWorkflowApi } from "./workflow.js";
@@ -38,10 +38,11 @@ export type AssistantMessageEvent = Extract<SessionEvent, {
38
38
  * `overridesBuiltInTool: true`.
39
39
  */
40
40
  export declare class CopilotSession {
41
- readonly sessionId: string;
41
+ private _sessionId;
42
42
  private connection;
43
43
  private _workspacePath?;
44
44
  private _transcriptRecovery?;
45
+ private _permissionModeResult?;
45
46
  private eventHandlers;
46
47
  private typedEventHandlers;
47
48
  private toolHandlers;
@@ -62,6 +63,7 @@ export declare class CopilotSession {
62
63
  private autoModeSwitchHandler?;
63
64
  private skillProvider?;
64
65
  private sessionCreationHandler?;
66
+ private memoryStoreHandler?;
65
67
  private hooks?;
66
68
  private transformCallbacks?;
67
69
  private _rpc;
@@ -95,6 +97,7 @@ export declare class CopilotSession {
95
97
  * dropped invalidation from leaving the wait pending forever.
96
98
  */
97
99
  private waitForWorkflowRun;
100
+ get sessionId(): string;
98
101
  /**
99
102
  * Typed session-scoped RPC methods.
100
103
  */
@@ -107,6 +110,12 @@ export declare class CopilotSession {
107
110
  get workspacePath(): string | undefined;
108
111
  /** Recovery observed while loading this session; undefined on a clean resume. */
109
112
  get transcriptRecovery(): TranscriptRecovery | undefined;
113
+ /**
114
+ * Outcome of the `permissionMode` requested when this session was created
115
+ * or resumed: whether it was applied, and the authoritative mode the
116
+ * session started in. Undefined when no mode was requested.
117
+ */
118
+ get permissionModeResult(): PermissionsSetModeResult | undefined;
110
119
  /**
111
120
  * Host capabilities reported when the session was created or resumed.
112
121
  * Use this to check feature support before calling capability-gated APIs.
package/dist/session.js CHANGED
@@ -270,8 +270,8 @@ class CopilotSession {
270
270
  * @param traceContextProvider - Optional callback to get W3C Trace Context for outbound RPCs
271
271
  * @internal This constructor is internal. Use {@link CopilotClient.createSession} to create sessions.
272
272
  */
273
- constructor(sessionId, connection, _workspacePath, traceContextProvider, options) {
274
- this.sessionId = sessionId;
273
+ constructor(_sessionId, connection, _workspacePath, traceContextProvider, options) {
274
+ this._sessionId = _sessionId;
275
275
  this.connection = connection;
276
276
  this._workspacePath = _workspacePath;
277
277
  this.traceContextProvider = traceContextProvider;
@@ -279,10 +279,11 @@ class CopilotSession {
279
279
  this.managedSettingsEnabled = options?.managedSettingsEnabled === true;
280
280
  this.onDisconnected = options?.onDisconnected;
281
281
  }
282
- sessionId;
282
+ _sessionId;
283
283
  connection;
284
284
  _workspacePath;
285
285
  _transcriptRecovery;
286
+ _permissionModeResult;
286
287
  eventHandlers = /* @__PURE__ */ new Set();
287
288
  typedEventHandlers = /* @__PURE__ */ new Map();
288
289
  toolHandlers = /* @__PURE__ */ new Map();
@@ -303,6 +304,7 @@ class CopilotSession {
303
304
  autoModeSwitchHandler;
304
305
  skillProvider;
305
306
  sessionCreationHandler;
307
+ memoryStoreHandler;
306
308
  hooks;
307
309
  transformCallbacks;
308
310
  _rpc = null;
@@ -455,6 +457,15 @@ class CopilotSession {
455
457
  void read();
456
458
  });
457
459
  }
460
+ get sessionId() {
461
+ return this._sessionId;
462
+ }
463
+ /** @internal */
464
+ _setSessionId(sessionId) {
465
+ this._sessionId = sessionId;
466
+ this._rpc = null;
467
+ this._internalRpc = null;
468
+ }
458
469
  /**
459
470
  * Typed session-scoped RPC methods.
460
471
  */
@@ -487,6 +498,18 @@ class CopilotSession {
487
498
  setTranscriptRecovery(recovery) {
488
499
  this._transcriptRecovery = recovery;
489
500
  }
501
+ /**
502
+ * Outcome of the `permissionMode` requested when this session was created
503
+ * or resumed: whether it was applied, and the authoritative mode the
504
+ * session started in. Undefined when no mode was requested.
505
+ */
506
+ get permissionModeResult() {
507
+ return this._permissionModeResult;
508
+ }
509
+ /** @internal */
510
+ setPermissionModeResult(result) {
511
+ this._permissionModeResult = result;
512
+ }
490
513
  /**
491
514
  * Host capabilities reported when the session was created or resumed.
492
515
  * Use this to check feature support before calling capability-gated APIs.
@@ -535,6 +558,7 @@ class CopilotSession {
535
558
  prompt: options.prompt,
536
559
  source: options.source,
537
560
  displayPrompt: options.displayPrompt,
561
+ clientTurnId: options.clientTurnId,
538
562
  attachments: options.attachments,
539
563
  mode: options.mode,
540
564
  agentMode: options.agentMode,
@@ -894,6 +918,7 @@ class CopilotSession {
894
918
  controller.abort();
895
919
  }
896
920
  this.pendingSessionCreations.clear();
921
+ this._clearMemoryStore();
897
922
  this.eventHandlers.clear();
898
923
  this.typedEventHandlers.clear();
899
924
  this.toolHandlers.clear();
@@ -905,6 +930,7 @@ class CopilotSession {
905
930
  this.autoModeSwitchHandler = void 0;
906
931
  this.skillProvider = void 0;
907
932
  this.sessionCreationHandler = void 0;
933
+ this.memoryStoreHandler = void 0;
908
934
  this.hooks = void 0;
909
935
  this.traceContextProvider = void 0;
910
936
  this.bearerTokenProviders.clear();
@@ -1122,6 +1148,7 @@ class CopilotSession {
1122
1148
  return;
1123
1149
  }
1124
1150
  this.pendingExternalTools.set(requestId, controller);
1151
+ let claimed = false;
1125
1152
  try {
1126
1153
  let availableTools;
1127
1154
  if (toolName === TOOL_SEARCH_TOOL_NAME) {
@@ -1178,20 +1205,45 @@ class CopilotSession {
1178
1205
  if (!this._claimExternalTool(requestId, controller)) {
1179
1206
  return;
1180
1207
  }
1208
+ claimed = true;
1181
1209
  await this.rpc.tools.handlePendingToolCall({ requestId, result });
1182
1210
  } catch (error) {
1183
- if (!this._claimExternalTool(requestId, controller)) {
1211
+ if (claimed) {
1212
+ if (this.disconnected) {
1213
+ return;
1214
+ }
1215
+ console.error("External tool result delivery failed", {
1216
+ sessionId: this.sessionId,
1217
+ requestId,
1218
+ toolName,
1219
+ error
1220
+ });
1221
+ } else if (!this._claimExternalTool(requestId, controller)) {
1184
1222
  return;
1185
1223
  }
1186
1224
  let message = error instanceof Error ? error.message : String(error);
1187
1225
  if (!message) {
1188
1226
  message = error instanceof Error && error.name || "Error";
1189
1227
  }
1190
- try {
1191
- await this.rpc.tools.handlePendingToolCall({ requestId, error: message });
1192
- } catch (rpcError) {
1193
- if (!(rpcError instanceof ConnectionError || rpcError instanceof ResponseError)) {
1194
- throw rpcError;
1228
+ for (let attempt = 0; attempt < (claimed ? 1 : 2); attempt++) {
1229
+ try {
1230
+ await this.rpc.tools.handlePendingToolCall({ requestId, error: message });
1231
+ return;
1232
+ } catch (rpcError) {
1233
+ const retryable = !claimed && attempt === 0 && !this.disconnected;
1234
+ if (!retryable) {
1235
+ if (!(rpcError instanceof ConnectionError || rpcError instanceof ResponseError)) {
1236
+ throw rpcError;
1237
+ }
1238
+ return;
1239
+ }
1240
+ console.error("External tool error delivery failed", {
1241
+ sessionId: this.sessionId,
1242
+ requestId,
1243
+ toolName,
1244
+ error: rpcError
1245
+ });
1246
+ message = (rpcError instanceof Error ? rpcError.message || rpcError.name : "") || String(rpcError) || "Error";
1195
1247
  }
1196
1248
  }
1197
1249
  } finally {
@@ -1894,6 +1946,43 @@ class CopilotSession {
1894
1946
  throw error;
1895
1947
  }
1896
1948
  }
1949
+ /**
1950
+ * Registers the callback the runtime calls through `memoryStore.store`.
1951
+ * Registered before create or resume declares `hasMemoryStore`.
1952
+ * @internal
1953
+ */
1954
+ _registerMemoryStore(handler) {
1955
+ this.assertConnected();
1956
+ this.memoryStoreHandler = handler;
1957
+ }
1958
+ /** @internal */
1959
+ _clearMemoryStore() {
1960
+ this.memoryStoreHandler = void 0;
1961
+ }
1962
+ /** @internal */
1963
+ async _handleMemoryStore(memory, signal) {
1964
+ this.assertConnected();
1965
+ const handler = this.memoryStoreHandler;
1966
+ if (!handler) throw new Error("No memory store is registered");
1967
+ try {
1968
+ await handler(
1969
+ {
1970
+ sessionId: memory.sessionId,
1971
+ subject: memory.subject,
1972
+ fact: memory.fact,
1973
+ citations: memory.citations,
1974
+ reason: memory.reason
1975
+ },
1976
+ { signal }
1977
+ );
1978
+ } catch (error) {
1979
+ if (signal.aborted) {
1980
+ throw new ResponseError(-32800, "Memory store call cancelled");
1981
+ }
1982
+ console.warn("Memory store callback failed", error);
1983
+ throw new Error("Memory store callback failed");
1984
+ }
1985
+ }
1897
1986
  /** @internal */
1898
1987
  async _handleSessionManagementCreate(request, token) {
1899
1988
  this.assertConnected();
package/dist/types.d.ts CHANGED
@@ -6,12 +6,12 @@ import type { SessionFsProvider } from "./sessionFsProvider.js";
6
6
  import type { CopilotRequestHandler } from "./copilotRequestHandler.js";
7
7
  import type { InstallationConfirmationHandler } from "./installationConfirmation.js";
8
8
  export type { InstallationConfirmationHandler } from "./installationConfirmation.js";
9
- import type { AgentModelPolicy, AttachmentExtensionContext as GeneratedExtensionContextAttachment, AutoTier, ModelProviderRef, PermissionRequest as GeneratedPermissionRequest, PermissionRequestedData as GeneratedPermissionRequestedData, PermissionRequestedEvent as GeneratedPermissionRequestedEvent, ReasoningSummary, SessionLimitsConfig, SessionEvent as GeneratedSessionEvent } from "./generated/session-events.js";
9
+ import type { AgentModelPolicy, AttachmentExtensionContext as GeneratedExtensionContextAttachment, AutoTier, ModelProviderRef, PermissionMode, PermissionRequest as GeneratedPermissionRequest, PermissionRequestedData as GeneratedPermissionRequestedData, PermissionRequestedEvent as GeneratedPermissionRequestedEvent, ReasoningSummary, SessionLimitsConfig, SessionEvent as GeneratedSessionEvent } from "./generated/session-events.js";
10
10
  import type { CopilotSession } from "./session.js";
11
11
  import type { JsonValue } from "./workflow.js";
12
- import type { ExtensionLaunchProviderHandler as GeneratedExtensionLaunchProvider, AutopilotOptions, GitHubTokenAcquireRequest, GitHubTokenAcquireResult, GitHubTelemetryNotification, HostDiagnostics, ModelBillingTokenPrices, DiagnosticsConfiguration, OpenCanvasInstance, ProtocolExternalToolNamespace, CurrentToolMetadata, KerberosAuthAcquireRequest, KerberosAuthMode, KerberosAuthScope, SandboxConfig, SessionManagementCreateHostRequest, SessionManagementCreateResult } from "./generated/rpc.js";
12
+ import type { ExtensionLaunchProviderHandler as GeneratedExtensionLaunchProvider, AutopilotOptions, GitHubTokenAcquireRequest, GitHubTokenAcquireResult, GitHubTelemetryNotification, HostDiagnostics, ModelBillingTokenPrices, DiagnosticsConfiguration, OpenCanvasInstance, ProtocolExternalToolNamespace, CurrentToolMetadata, NetworkAuthenticateNegotiateRequest, NetworkResolveProxyRequest, NetworkScope, SandboxConfig, SessionManagementCreateHostRequest, SessionManagementCreateResult } from "./generated/rpc.js";
13
13
  import type { ToolSet } from "./toolSet.js";
14
- export type { KerberosAuthAcquireRequest, KerberosAuthMode, KerberosAuthScope, } from "./generated/rpc.js";
14
+ export type { NetworkAuthenticateNegotiateRequest, NetworkResolveProxyRequest, NetworkScope, } from "./generated/rpc.js";
15
15
  export type { RemoteSessionMode } from "./generated/rpc.js";
16
16
  export type { AutopilotOptions } from "./generated/rpc.js";
17
17
  export type { CurrentToolMetadata } from "./generated/rpc.js";
@@ -341,9 +341,11 @@ export interface CopilotClientOptions {
341
341
  */
342
342
  extensionLaunchProvider?: ExtensionLaunchProvider;
343
343
  /**
344
- * Supplies Kerberos credentials from the SDK host. Registered before sessions start.
344
+ * Network callbacks through which the SDK host decides the runtime's proxies and supplies its
345
+ * Kerberos tokens. Registered before sessions start.
346
+ * @experimental
345
347
  */
346
- kerberosAuth?: KerberosAuthConfig;
348
+ network?: NetworkConfig;
347
349
  /**
348
350
  * Connection-global human review for experimental installation operations.
349
351
  * Does not register or enable installation capabilities on the runtime.
@@ -473,25 +475,49 @@ export interface CopilotClientOptions {
473
475
  }
474
476
  /** Resolves launch profiles for extension entrypoints discovered by the runtime. */
475
477
  export type ExtensionLaunchProvider = GeneratedExtensionLaunchProvider;
476
- /** Supplies Kerberos credentials from the SDK host for a requested service principal. */
477
- export interface KerberosAuthConfig {
478
- /** `fallback` uses the callback only when native lookup fails; `always` never attempts native lookup. */
479
- mode: KerberosAuthMode;
478
+ /**
479
+ * Network callbacks through which the SDK host takes over parts of the runtime's networking, for
480
+ * example when the runtime runs in WSL or a container and should use VS Code's proxy settings and
481
+ * the host's Kerberos tickets. Each registered callback replaces the runtime's own handling for the
482
+ * traffic in {@link NetworkConfig.scope}; without it, the runtime handles that part itself.
483
+ *
484
+ * Each callback receives a `signal` that aborts when the runtime stops waiting: it cancels the
485
+ * request (for example when its wait times out) or the connection closes. The SDK then stops
486
+ * awaiting the callback and ignores any later result.
487
+ * @experimental
488
+ */
489
+ export interface NetworkConfig {
480
490
  /**
481
- * `connection` (the default) authenticates only requests made for sessions this client creates or resumes;
482
- * `runtime` also authenticates the runtime's shared background traffic. Only one connection at a time can
491
+ * `connection` (the default) serves only requests made for sessions this client creates or resumes;
492
+ * `runtime` also serves the runtime's shared background traffic. Only one connection at a time can
483
493
  * hold `runtime` scope, so startup fails if another client holds it. The registration ends when the
484
494
  * connection closes.
485
495
  */
486
- scope?: KerberosAuthScope;
496
+ scope?: NetworkScope;
487
497
  /**
488
- * Returns a fresh, single-use base64 SPNEGO token without the `Negotiate ` prefix, or `undefined` when unavailable.
489
- * The SPN uses the platform-independent `service/host[@REALM]` form; validate it before issuing credentials.
490
- * `signal` aborts when the runtime stops waiting: it cancels the request (for example when its wait times
491
- * out) or the connection closes. The SDK then stops awaiting the callback and ignores any later result,
492
- * so pass `signal` to credential lookups that support cancellation.
498
+ * Returns the proxies for `request.url` as a PAC-style list such as
499
+ * `PROXY proxy.example:8080; HTTPS proxy.example:443; DIRECT` (the format of Electron's
500
+ * `session.resolveProxy`); return `DIRECT` for a direct connection, or `undefined` to let the runtime
501
+ * resolve the proxy itself, as if no callback were registered. Entry types the runtime doesn't
502
+ * support, such as SOCKS and QUIC, are skipped.
503
+ * `request.url` is the request target as a PAC script sees it: without credentials or fragment, and for
504
+ * `https` and `wss` also without path and query.
505
+ *
506
+ * The callback decides every request that would otherwise follow ambient proxy settings: proxy
507
+ * environment variables, `NO_PROXY` and operating-system proxy settings are not consulted unless it
508
+ * returns `undefined`, and a failing callback fails the request rather than connecting directly.
509
+ * Requests to loopback targets, and requests with an explicitly configured proxy, never consult it. The
510
+ * runtime caches answers, including `undefined`, for a few seconds.
511
+ */
512
+ resolveProxy?: (request: NetworkResolveProxyRequest, signal: AbortSignal) => Promise<string | undefined>;
513
+ /**
514
+ * Returns a fresh, single-use base64 SPNEGO token without the `Negotiate ` prefix for a proxy's Negotiate
515
+ * challenge, or `undefined` when unavailable. The runtime then does not look up Kerberos credentials
516
+ * itself, so a missing token or a failing callback fails the request.
517
+ * The SPN uses the platform-independent `service/host[@REALM]` form; validate it before issuing
518
+ * credentials. Never log or persist tokens.
493
519
  */
494
- callback: (request: KerberosAuthAcquireRequest, signal: AbortSignal) => Promise<string | undefined>;
520
+ authenticateNegotiate?: (request: NetworkAuthenticateNegotiateRequest, signal: AbortSignal) => Promise<string | undefined>;
495
521
  }
496
522
  /**
497
523
  * Configuration for creating a session
@@ -816,6 +842,8 @@ export interface SessionCapabilities {
816
842
  mcpApps?: boolean;
817
843
  /** Whether the host supports canvas rendering. */
818
844
  canvases?: boolean;
845
+ /** Whether the runtime acknowledges retained-request Auto recovery. Absence means unsupported. */
846
+ autoModeRecovery?: boolean;
819
847
  };
820
848
  }
821
849
  /**
@@ -1251,13 +1279,26 @@ export type ExitPlanModeHandler = (request: ExitPlanModeRequest, invocation: {
1251
1279
  sessionId: string;
1252
1280
  }) => Promise<ExitPlanModeResult> | ExitPlanModeResult;
1253
1281
  /**
1254
- * Request to switch to auto mode after an eligible rate limit.
1282
+ * Context for an offer to recover the active request using Auto.
1283
+ */
1284
+ export interface AutoModeRecoveryContext {
1285
+ requestId: string;
1286
+ originalModel: string;
1287
+ /** Whether this offer permits remembering consent. Currently true only for per-model rate limits. */
1288
+ allowAlways: boolean;
1289
+ interactionId?: string;
1290
+ originatingMessageId?: string;
1291
+ }
1292
+ /**
1293
+ * Request to switch to Auto after a rate limit or recover an eligible active request.
1255
1294
  */
1256
1295
  export interface AutoModeSwitchRequest {
1257
- /** The rate-limit error code that triggered the request. */
1296
+ /** The upstream error code that triggered the request. */
1258
1297
  errorCode?: string;
1259
1298
  /** Seconds until the rate limit resets, when known. */
1260
1299
  retryAfterSeconds?: number;
1300
+ /** Present only for retained-request recovery, not legacy future-message switching. */
1301
+ autoModeRecovery?: AutoModeRecoveryContext;
1261
1302
  }
1262
1303
  /**
1263
1304
  * Response to an auto-mode-switch request.
@@ -1334,7 +1375,9 @@ export interface PreMcpToolCallHookOutput {
1334
1375
  * Hook-controlled metadata to use for the outgoing MCP request.
1335
1376
  * - undefined/absent: preserve the current request `_meta`
1336
1377
  * - object: use this object as request `_meta`
1337
- * - null: omit `_meta`
1378
+ * - null: drop hook-controlled `_meta`; the runtime still sends its own keys,
1379
+ * `com.github.copilot/toolCallId` and `com.github.copilot/turnId` (present only
1380
+ * when the turn was started with a `clientTurnId`)
1338
1381
  */
1339
1382
  metaToUse?: Record<string, unknown> | null;
1340
1383
  }
@@ -1848,6 +1891,46 @@ export interface MemoryConfiguration {
1848
1891
  * @experimental This option may change or be removed in a future release.
1849
1892
  */
1850
1893
  mode?: "system-prompt" | "user-prompt";
1894
+ /**
1895
+ * Stores memories in the app instead of the built-in memory backends.
1896
+ *
1897
+ * The runtime validates the memory, filters secrets and requests approval,
1898
+ * then calls this callback. `store_memory` succeeds only after the returned
1899
+ * promise resolves; a rejection fails the tool call with a generic message
1900
+ * and nothing is stored elsewhere. `options.signal` aborts when the runtime
1901
+ * stops waiting, for example after its 60-second timeout. While set, the
1902
+ * runtime retrieves no memories and `vote_memory` is unavailable, so supply
1903
+ * recall yourself, for example through `systemMessage`.
1904
+ * @experimental This option may change or be removed in a future release.
1905
+ */
1906
+ storeMemory?: (memory: StoredMemory, options: MemoryStoreCallOptions) => Promise<void>;
1907
+ }
1908
+ /**
1909
+ * Per-call options passed to {@link MemoryConfiguration.storeMemory}.
1910
+ * @experimental This type may change or be removed in a future release.
1911
+ */
1912
+ export interface MemoryStoreCallOptions {
1913
+ /**
1914
+ * Aborted when the runtime cancels the call, for example when it times out
1915
+ * or the session disconnects. The runtime ignores any later result.
1916
+ */
1917
+ signal: AbortSignal;
1918
+ }
1919
+ /**
1920
+ * A memory the agent asked to store, after validation, secret filtering and approval.
1921
+ * @experimental This type may change or be removed in a future release.
1922
+ */
1923
+ export interface StoredMemory {
1924
+ /** ID of the session that stored the memory. */
1925
+ sessionId: string;
1926
+ /** Short topic the memory is about. */
1927
+ subject: string;
1928
+ /** The fact to remember. */
1929
+ fact: string;
1930
+ /** Sources supporting the fact, such as file paths with line numbers. */
1931
+ citations: string;
1932
+ /** Why the agent considers the fact worth remembering. */
1933
+ reason: string;
1851
1934
  }
1852
1935
  /**
1853
1936
  * Configuration for handling large tool outputs.
@@ -1864,7 +1947,8 @@ export interface LargeToolOutputConfig {
1864
1947
  enabled?: boolean;
1865
1948
  /**
1866
1949
  * Maximum size in bytes before output is written to a temp file.
1867
- * @default 51200
1950
+ * Uses the runtime's configured threshold when unset.
1951
+ * @default 20480
1868
1952
  */
1869
1953
  maxSizeBytes?: number;
1870
1954
  /**
@@ -2182,6 +2266,12 @@ export interface SkillProviderCallOptions {
2182
2266
  * an existing one).
2183
2267
  */
2184
2268
  export interface SessionConfigBase {
2269
+ /**
2270
+ * Whether the model may request multiple tool calls per response. Defaults to true.
2271
+ * False sends a provider hint, not an execution guarantee: all returned calls
2272
+ * are still handled and handlers may run concurrently. Re-supply on cold resume.
2273
+ */
2274
+ allowMultipleToolCalls?: boolean;
2185
2275
  /** Exact model IDs allowed by the host. Omission preserves runtime policy. */
2186
2276
  allowedModels?: string[];
2187
2277
  /**
@@ -2535,6 +2625,15 @@ export interface SessionConfigBase {
2535
2625
  * When provided, enables `autoModeSwitch.request` callbacks.
2536
2626
  */
2537
2627
  onAutoModeSwitchRequest?: AutoModeSwitchHandler;
2628
+ /**
2629
+ * Opt in to runtime-owned recovery of an eligible active request using Auto.
2630
+ * Requires `onAutoModeSwitchRequest` and a runtime acknowledgment in
2631
+ * `session.capabilities.ui.autoModeRecovery`; unsupported enrollment rejects
2632
+ * create/resume and detaches the session. Re-supply when resuming.
2633
+ * Consent has no runtime deadline. The handler must not resend or rewind.
2634
+ * @default false
2635
+ */
2636
+ autoModeRecovery?: boolean;
2538
2637
  /**
2539
2638
  * Hook handlers for intercepting session lifecycle events.
2540
2639
  * When provided, enables hooks callback allowing custom logic at various points.
@@ -2752,6 +2851,16 @@ export interface SessionConfigBase {
2752
2851
  * for cloud sessions and when resuming a session that is already active.
2753
2852
  */
2754
2853
  interactiveUser?: boolean;
2854
+ /**
2855
+ * Permission mode applied before the session's first turn: before
2856
+ * `createSession` returns, and before a resume continues pending work.
2857
+ * Uses the same checks as `session.rpc.permissions.setMode`; a mode that
2858
+ * managed policy or session capabilities refuse leaves the current mode in
2859
+ * place. `session.permissionModeResult` reports whether it was applied and
2860
+ * the authoritative mode. Omit to keep the default mode on create, or the
2861
+ * restored mode on resume. Not supported for cloud sessions.
2862
+ */
2863
+ permissionMode?: PermissionMode;
2755
2864
  /**
2756
2865
  * Host-injected enterprise managed settings for this session.
2757
2866
  *
@@ -3271,6 +3380,16 @@ export interface MessageOptions {
3271
3380
  * If provided, this is shown in the timeline instead of `prompt`.
3272
3381
  */
3273
3382
  displayPrompt?: string;
3383
+ /**
3384
+ * Opaque caller-chosen ID for the turn this message starts. The runtime copies it unchanged
3385
+ * into `_meta["com.github.copilot/turnId"]` on every MCP `tools/call` made while serving that
3386
+ * turn, including calls from sub-agents and Autopilot continuations. When a running turn
3387
+ * consumes the message as visible user steering, the value (or its absence) replaces the
3388
+ * turn's ID from the next model call onward. The `user.message` event for the message echoes
3389
+ * it as `clientTurnId` and is persisted like other events; the value stamped on MCP calls is
3390
+ * held in memory only and is not restored on resume.
3391
+ */
3392
+ clientTurnId?: string;
3274
3393
  /**
3275
3394
  * JSON Schema or a Zod schema for this run's output, including requests after tool calls.
3276
3395
  * Independent sends do not inherit it. Ordinary immediate steering retains the active
@@ -184,7 +184,7 @@ object sent with the request; to allow, deny or change the call itself, use
184
184
  **Output (all fields optional):**
185
185
  | Field | Type | Effect |
186
186
  |-------|------|--------|
187
- | `metaToUse` | `Record<string, unknown> \| null` | An object becomes the request's `_meta`; `null` omits it; leaving it out keeps the current `_meta` |
187
+ | `metaToUse` | `Record<string, unknown> \| null` | An object becomes the request's `_meta`; `null` omits it, except for the runtime's `com.github.copilot/toolCallId` and `com.github.copilot/turnId` keys; leaving it out keeps the current `_meta` |
188
188
 
189
189
  ### onPostToolUse
190
190
 
package/docs/examples.md CHANGED
@@ -25,7 +25,8 @@ const session = await joinSession({
25
25
  >
26
26
  > - Use `process.platform === "win32"` to detect Windows at runtime.
27
27
  > - Clipboard: `pbcopy` on macOS, `clip` on Windows.
28
- > - Use `exec()` instead of `execFile()` for `.cmd` scripts like `code`, `npx`, `npm` on Windows.
28
+ > - Never interpolate tool arguments into `exec()` or shell command strings. Use `execFile()` or `spawn()` with argument arrays.
29
+ > - On Windows, `.cmd` and `.bat` wrappers require a shell. Invoke the actual executable or use the library's Node.js API instead.
29
30
  > - PowerShell stderr redirection uses `*>&1` instead of `2>&1`.
30
31
 
31
32
  ---
@@ -257,16 +258,41 @@ hooks: {
257
258
 
258
259
  Use `onPostToolUse` to run side effects after a tool completes.
259
260
 
261
+ On macOS and Linux, install the `code` command in your `PATH`. On Windows,
262
+ set `VSCODE_EXECUTABLE` to the absolute path of your installed `Code.exe`
263
+ (for example, `C:\Users\you\AppData\Local\Programs\Microsoft VS Code\Code.exe`),
264
+ not the `code.cmd` wrapper. This is trusted extension configuration, not a
265
+ tool argument.
266
+
260
267
  ```js
261
- import { exec } from "node:child_process";
268
+ import { spawn } from "node:child_process";
269
+ import { isAbsolute, resolve } from "node:path";
270
+
271
+ const isWindows = process.platform === "win32";
272
+ const vscodeExecutable = isWindows ? process.env.VSCODE_EXECUTABLE : "code";
273
+ if (!vscodeExecutable || (isWindows && !isAbsolute(vscodeExecutable))) {
274
+ throw new Error("Set VSCODE_EXECUTABLE to the absolute path of Code.exe.");
275
+ }
276
+ const editorEnv = { ...process.env };
277
+ // The native editor must open the file, not run it as a Node.js script.
278
+ delete editorEnv.ELECTRON_RUN_AS_NODE;
262
279
 
263
280
  hooks: {
264
281
  onPostToolUse: async (input) => {
265
282
  if (input.toolName === "create" || input.toolName === "edit") {
266
283
  const filePath = input.toolArgs?.path;
267
- if (filePath) {
268
- // Open the file in VS Code
269
- exec(`code "${filePath}"`, () => {});
284
+ if (typeof filePath === "string" && filePath) {
285
+ try {
286
+ const child = spawn(vscodeExecutable, [resolve(filePath)], {
287
+ detached: true,
288
+ stdio: "ignore",
289
+ env: editorEnv,
290
+ });
291
+ child.on("error", (err) => console.error("Could not open file in VS Code:", err.message));
292
+ child.unref();
293
+ } catch (err) {
294
+ console.error("Could not open file in VS Code:", err.message);
295
+ }
270
296
  }
271
297
  }
272
298
  },
@@ -295,20 +321,34 @@ hooks: {
295
321
 
296
322
  ### Running a linter after every file edit
297
323
 
324
+ Install ESLint in the session's working directory (`npm install --save-dev eslint`)
325
+ and configure it for TypeScript. Only run this hook in workspaces whose ESLint
326
+ configuration and plugins you trust. Use its Node.js API rather than `npx`:
327
+ an extension's `process.execPath` can be the Copilot single-executable binary,
328
+ not a general-purpose Node.js interpreter.
329
+
298
330
  ```js
299
- import { exec } from "node:child_process";
331
+ import { createRequire } from "node:module";
332
+ import { resolve } from "node:path";
333
+ import { pathToFileURL } from "node:url";
334
+
335
+ const require = createRequire(import.meta.url);
300
336
 
301
337
  hooks: {
302
338
  onPostToolUse: async (input) => {
303
339
  if (input.toolName === "edit") {
304
340
  const filePath = input.toolArgs?.path;
305
- if (filePath?.endsWith(".ts")) {
306
- const result = await new Promise((resolve) => {
307
- exec(`npx eslint "${filePath}"`, (err, stdout) => {
308
- resolve(err ? stdout : "No lint errors.");
309
- });
310
- });
311
- return { additionalContext: `Lint result: ${result}` };
341
+ if (typeof filePath === "string" && filePath.endsWith(".ts")) {
342
+ try {
343
+ const eslintModule = require.resolve("eslint", { paths: [process.cwd()] });
344
+ const { ESLint } = await import(pathToFileURL(eslintModule).href);
345
+ const eslint = new ESLint({ cwd: process.cwd(), globInputPaths: false });
346
+ const results = await eslint.lintFiles([resolve(filePath)]);
347
+ const formatter = await eslint.loadFormatter("stylish");
348
+ return { additionalContext: `Lint result: ${formatter.format(results) || "No lint errors."}` };
349
+ } catch (err) {
350
+ return { additionalContext: `Lint failed: ${err.message}` };
351
+ }
312
352
  }
313
353
  }
314
354
  },
@@ -603,11 +643,22 @@ const session = await joinSession({
603
643
 
604
644
  An extension that combines tools, hooks, and events.
605
645
 
646
+ Use the same VS Code setup as in
647
+ [Reacting when the agent creates or edits a file](#reacting-when-the-agent-creates-or-edits-a-file).
648
+
606
649
  ```js
607
- import { execFile, exec } from "node:child_process";
650
+ import { execFile, spawn } from "node:child_process";
651
+ import { isAbsolute, resolve } from "node:path";
608
652
  import { joinSession } from "@github/copilot-sdk/extension";
609
653
 
610
654
  const isWindows = process.platform === "win32";
655
+ const vscodeExecutable = isWindows ? process.env.VSCODE_EXECUTABLE : "code";
656
+ if (!vscodeExecutable || (isWindows && !isAbsolute(vscodeExecutable))) {
657
+ throw new Error("Set VSCODE_EXECUTABLE to the absolute path of Code.exe.");
658
+ }
659
+ const editorEnv = { ...process.env };
660
+ // The native editor must open the file, not run it as a Node.js script.
661
+ delete editorEnv.ELECTRON_RUN_AS_NODE;
611
662
  let copyNextResponse = false;
612
663
 
613
664
  function copyToClipboard(text) {
@@ -617,8 +668,17 @@ function copyToClipboard(text) {
617
668
  }
618
669
 
619
670
  function openInEditor(filePath) {
620
- if (isWindows) exec(`code "${filePath}"`, () => {});
621
- else execFile("code", [filePath], () => {});
671
+ try {
672
+ const child = spawn(vscodeExecutable, [resolve(filePath)], {
673
+ detached: true,
674
+ stdio: "ignore",
675
+ env: editorEnv,
676
+ });
677
+ child.on("error", (err) => console.error("Could not open file in VS Code:", err.message));
678
+ child.unref();
679
+ } catch (err) {
680
+ console.error("Could not open file in VS Code:", err.message);
681
+ }
622
682
  }
623
683
 
624
684
  const session = await joinSession({
@@ -645,7 +705,7 @@ const session = await joinSession({
645
705
  onPostToolUse: async (input) => {
646
706
  if (input.toolName === "create" || input.toolName === "edit") {
647
707
  const filePath = input.toolArgs?.path;
648
- if (filePath) openInEditor(filePath);
708
+ if (typeof filePath === "string" && filePath) openInEditor(filePath);
649
709
  }
650
710
  },
651
711
  },