@github/copilot-sdk 1.0.0-beta.8 → 1.0.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/README.md CHANGED
@@ -2,8 +2,6 @@
2
2
 
3
3
  TypeScript SDK for programmatic control of GitHub Copilot CLI via JSON-RPC.
4
4
 
5
- > **Note:** This SDK is in public preview and may change in breaking ways.
6
-
7
5
  ## Installation
8
6
 
9
7
  ```bash
@@ -57,7 +55,7 @@ await session.disconnect();
57
55
  await client.stop();
58
56
  ```
59
57
 
60
- Sessions also support `Symbol.asyncDispose` for use with [`await using`](https://github.com/tc39/proposal-explicit-resource-management) (TypeScript 5.2+/Node.js 18.0+):
58
+ Sessions also support `Symbol.asyncDispose` for use with [`await using`](https://github.com/tc39/proposal-explicit-resource-management) (TypeScript 5.2+ / Node.js 20+):
61
59
 
62
60
  ```typescript
63
61
  await using session = await client.createSession({
@@ -82,14 +80,20 @@ new CopilotClient(options?: CopilotClientOptions)
82
80
  - `connection?: RuntimeConnection` - How to connect to the Copilot runtime. Construct via the factory functions on `RuntimeConnection`:
83
81
  - `RuntimeConnection.forStdio({ path?, args? })` (default) — spawn the runtime and communicate over its stdin/stdout.
84
82
  - `RuntimeConnection.forTcp({ port?, connectionToken?, path?, args? })` — spawn the runtime as a TCP server.
85
- - `RuntimeConnection.forUri(url, { connectionToken? })` — connect to an already-running runtime (mutually exclusive with `gitHubToken`/`useLoggedInUser`).
86
- - `cwd?: string` - Working directory for the runtime process (default: current process cwd).
83
+ - `RuntimeConnection.forUri(url, { connectionToken? })` — connect to an already-running runtime (mutually exclusive with `gitHubToken`/`useLoggedInUser`). There is no top-level `cliUrl` shortcut; use this factory for URL-based connections.
84
+ - `mode?: "empty" | "copilot-cli"` - Defaulting strategy. Use `"empty"` for multi-user server mode; defaults to `"copilot-cli"`.
85
+ - `workingDirectory?: string` - Working directory for the runtime process (default: current process cwd).
87
86
  - `baseDirectory?: string` - Base directory for Copilot data (session state, config, etc.). Sets `COPILOT_HOME` on the spawned runtime. When not set, the runtime defaults to `~/.copilot`. Ignored when connecting via `RuntimeConnection.forUri`.
88
- - `logLevel?: string` - Log level. When omitted, the runtime uses its own default (currently `"info"`).
87
+ - `logLevel?: "none" | "error" | "warning" | "info" | "debug" | "all"` - Log level. When omitted, the runtime uses its own default (currently `"info"`).
88
+ - `env?: Record<string, string | undefined>` - Environment variables for the runtime process. When omitted, inherits `process.env`.
89
89
  - `gitHubToken?: string` - GitHub token for authentication. When provided, takes priority over other auth methods.
90
90
  - `useLoggedInUser?: boolean` - Whether to use logged-in user for authentication (default: true, but false when `gitHubToken` is provided). Cannot be used with `RuntimeConnection.forUri`.
91
+ - `onListModels?: () => Promise<ModelInfo[]> | ModelInfo[]` - Optional model-list provider, useful when using a custom provider.
91
92
  - `telemetry?: TelemetryConfig` - OpenTelemetry configuration for the runtime process. Providing this object enables telemetry — no separate flag needed. See [Telemetry](#telemetry) below.
92
93
  - `onGetTraceContext?: TraceContextProvider` - Advanced: callback for linking your application's own OpenTelemetry spans into the same distributed trace as the runtime's spans. Not needed for normal telemetry collection. See [Telemetry](#telemetry) below.
94
+ - `sessionFs?: SessionFsConfig` - Custom session filesystem provider.
95
+ - `sessionIdleTimeoutSeconds?: number` - Server-wide idle timeout for sessions in seconds. Ignored when connecting via `RuntimeConnection.forUri`.
96
+ - `enableRemoteSessions?: boolean` - Enable Mission Control remote session support. Ignored when connecting via `RuntimeConnection.forUri`.
93
97
 
94
98
  #### Methods
95
99
 
@@ -163,7 +167,7 @@ Get the ID of the session currently displayed in the TUI. Only available when co
163
167
 
164
168
  Request the TUI to switch to displaying the specified session. Only available in TUI+server mode.
165
169
 
166
- ##### `on(eventType: SessionLifecycleEventType, handler): () => void`
170
+ ##### `onLifecycle(eventType: SessionLifecycleEventType, handler): () => void`
167
171
 
168
172
  Subscribe to a specific session lifecycle event type. Returns an unsubscribe function.
169
173
 
@@ -173,7 +177,7 @@ const unsubscribe = client.onLifecycle("session.foreground", (event) => {
173
177
  });
174
178
  ```
175
179
 
176
- ##### `on(handler: SessionLifecycleHandler): () => void`
180
+ ##### `onLifecycle(handler: SessionLifecycleHandler): () => void`
177
181
 
178
182
  Subscribe to all session lifecycle events. Returns an unsubscribe function.
179
183
 
@@ -957,7 +961,7 @@ const session = await client.createSession({
957
961
  };
958
962
  },
959
963
 
960
- // Called after each tool execution
964
+ // Called after each successful tool execution
961
965
  onPostToolUse: async (input, invocation) => {
962
966
  console.log(`Tool ${input.toolName} completed`);
963
967
  // Optionally modify the result or add context
@@ -966,6 +970,16 @@ const session = await client.createSession({
966
970
  };
967
971
  },
968
972
 
973
+ // Called after a tool execution whose result was "failure".
974
+ // onPostToolUse does NOT fire for failed tool calls — register this
975
+ // hook to observe them. Input includes `error` (the failure message
976
+ // extracted from the tool's result), not the full result object.
977
+ onPostToolUseFailure: async (input, invocation) => {
978
+ console.log(`Tool ${input.toolName} failed: ${input.error}`);
979
+ // Optionally append hidden guidance to the model.
980
+ return { additionalContext: "Suggest checking inputs and retrying." };
981
+ },
982
+
969
983
  // Called when user submits a prompt
970
984
  onUserPromptSubmitted: async (input, invocation) => {
971
985
  console.log(`User prompt: ${input.prompt}`);
@@ -1001,7 +1015,8 @@ const session = await client.createSession({
1001
1015
  **Available hooks:**
1002
1016
 
1003
1017
  - `onPreToolUse` - Intercept tool calls before execution. Can allow/deny or modify arguments.
1004
- - `onPostToolUse` - Process tool results after execution. Can modify results or add context.
1018
+ - `onPostToolUse` - Process tool results after **successful** execution. Can modify results or add context.
1019
+ - `onPostToolUseFailure` - Observe and append hidden guidance to the model after tool executions whose result was `"failure"`. Register this in addition to `onPostToolUse` to see failed tool calls.
1005
1020
  - `onUserPromptSubmitted` - Intercept user prompts. Can modify the prompt before processing.
1006
1021
  - `onSessionStart` - Run logic when a session starts or resumes.
1007
1022
  - `onSessionEnd` - Cleanup or logging when session ends.
package/dist/canvas.d.ts CHANGED
@@ -1,14 +1,17 @@
1
1
  import type { CanvasJsonSchema, CanvasProviderCloseRequest, CanvasProviderInvokeActionRequest, CanvasProviderOpenRequest, CanvasProviderOpenResult } from "./generated/rpc.js";
2
- export type { CanvasJsonSchema, CanvasHostContext } from "./generated/rpc.js";
2
+ export type { CanvasJsonSchema, CanvasHostContext, CanvasHostContextCapabilities, } from "./generated/rpc.js";
3
3
  /**
4
4
  * Extension-owned canvases declared via
5
5
  * `joinSession({ canvases: [createCanvas({...})] })`.
6
6
  *
7
7
  * The runtime sends provider callbacks as `canvas.open`, `canvas.close`, and
8
- * `canvas.invokeAction` JSON-RPC requests via the codegen client session API
8
+ * `canvas.action.invoke` JSON-RPC requests via the codegen client session API
9
9
  * pipeline. The SDK routes those requests by `canvasId` to the in-process
10
10
  * handlers bound by `createCanvas`. Re-opening with an existing `instanceId`
11
11
  * is how the host focuses an existing panel; reload is a renderer-only concern.
12
+ *
13
+ * @experimental Canvas types are part of an experimental wire-protocol surface
14
+ * and may change or be removed in future SDK or CLI releases.
12
15
  */
13
16
  /**
14
17
  * A single agent-callable action contributed by a canvas. The metadata
@@ -18,6 +21,9 @@ export type { CanvasJsonSchema, CanvasHostContext } from "./generated/rpc.js";
18
21
  *
19
22
  * Names MUST NOT start with `canvas.` — that prefix is reserved for
20
23
  * lifecycle verbs.
24
+ *
25
+ * @experimental This type is part of an experimental wire-protocol surface
26
+ * and may change or be removed in future SDK or CLI releases.
21
27
  */
22
28
  export interface CanvasAction {
23
29
  /** Action identifier, unique within the canvas. */
@@ -32,6 +38,9 @@ export interface CanvasAction {
32
38
  /**
33
39
  * Declarative metadata for a single canvas, serialized over the wire on
34
40
  * `session.create` / `session.resume`.
41
+ *
42
+ * @experimental This type is part of an experimental wire-protocol surface
43
+ * and may change or be removed in future SDK or CLI releases.
35
44
  */
36
45
  export interface CanvasDeclaration {
37
46
  /** Canvas id, unique within the declaring connection. */
@@ -45,7 +54,12 @@ export interface CanvasDeclaration {
45
54
  /** Agent-invocable actions exposed via `invoke_canvas_action`. */
46
55
  actions?: Omit<CanvasAction, "handler">[];
47
56
  }
48
- /** Structured error returned from canvas handlers. */
57
+ /**
58
+ * Structured error returned from canvas handlers.
59
+ *
60
+ * @experimental This class is part of an experimental wire-protocol surface
61
+ * and may change or be removed in future SDK or CLI releases.
62
+ */
49
63
  export declare class CanvasError extends Error {
50
64
  readonly code: string;
51
65
  constructor(code: string, message: string);
@@ -55,6 +69,9 @@ export declare class CanvasError extends Error {
55
69
  /**
56
70
  * Options accepted by {@link createCanvas}. Combines the declarative
57
71
  * {@link CanvasDeclaration} fields with the in-process handler closures.
72
+ *
73
+ * @experimental This interface is part of an experimental wire-protocol surface
74
+ * and may change or be removed in future SDK or CLI releases.
58
75
  */
59
76
  export interface CanvasOptions {
60
77
  /** @see CanvasDeclaration.id */
@@ -87,6 +104,9 @@ export interface CanvasOptions {
87
104
  * ergonomics) where other SDKs (Rust, Python, Go, .NET) expose a single
88
105
  * `CanvasHandler` per session that switches on `canvasId`. Both shapes target
89
106
  * the same JSON-RPC wire protocol; the divergence is API ergonomics only.
107
+ *
108
+ * @experimental This class is part of an experimental wire-protocol surface
109
+ * and may change or be removed in future SDK or CLI releases.
90
110
  */
91
111
  export declare class Canvas {
92
112
  readonly declaration: CanvasDeclaration;
@@ -99,5 +119,8 @@ export declare class Canvas {
99
119
  * `DefineTool`'s co-location ergonomics) where other SDKs (Rust, Python, Go,
100
120
  * .NET) expose a single `CanvasHandler` per session that switches on
101
121
  * `canvasId`. Both shapes target the same JSON-RPC wire protocol.
122
+ *
123
+ * @experimental This function is part of an experimental wire-protocol surface
124
+ * and may change or be removed in future SDK or CLI releases.
102
125
  */
103
126
  export declare function createCanvas(options: CanvasOptions): Canvas;
@@ -34,6 +34,7 @@ var import_sdkProtocolVersion = require("./sdkProtocolVersion.js");
34
34
  var import_session = require("./session.js");
35
35
  var import_sessionFsProvider = require("./sessionFsProvider.js");
36
36
  var import_telemetry = require("./telemetry.js");
37
+ var import_toolSet = require("./toolSet.js");
37
38
  var import_types = require("./types.js");
38
39
  const import_meta = {};
39
40
  const MIN_PROTOCOL_VERSION = 3;
@@ -67,6 +68,33 @@ function toWireCustomAgents(agents) {
67
68
  return { ...rest, mcpServers: toWireMcpServers(mcpServers) };
68
69
  });
69
70
  }
71
+ function toWireLargeOutput(config) {
72
+ if (!config) return void 0;
73
+ const { outputDirectory, ...rest } = config;
74
+ const wire = { ...rest };
75
+ if (outputDirectory !== void 0) {
76
+ wire.outputDir = outputDirectory;
77
+ }
78
+ return wire;
79
+ }
80
+ function toolFilterListToArray(value) {
81
+ if (value === void 0) {
82
+ return void 0;
83
+ }
84
+ return value instanceof import_toolSet.ToolSet ? value.toArray() : value;
85
+ }
86
+ function validateToolFilterList(field, list) {
87
+ if (!list) {
88
+ return;
89
+ }
90
+ for (const entry of list) {
91
+ if (entry === "*") {
92
+ throw new Error(
93
+ `Invalid ${field} entry '*': there is no bare wildcard. Use one or more of \`new ToolSet().addBuiltIn('*')\`, \`.addMcp('*')\`, or \`.addCustom('*')\` to target a specific source.`
94
+ );
95
+ }
96
+ }
97
+ }
70
98
  function extractTransformCallbacks(systemMessage) {
71
99
  if (!systemMessage || systemMessage.mode !== "customize" || !systemMessage.sections) {
72
100
  return { wirePayload: systemMessage, transformCallbacks: void 0 };
@@ -249,8 +277,19 @@ class CopilotClient {
249
277
  telemetry: options.telemetry,
250
278
  baseDirectory: options.baseDirectory,
251
279
  sessionIdleTimeoutSeconds: options.sessionIdleTimeoutSeconds ?? 0,
252
- enableRemoteSessions: options.enableRemoteSessions ?? false
280
+ enableRemoteSessions: options.enableRemoteSessions ?? false,
281
+ mode: options.mode ?? "copilot-cli"
253
282
  };
283
+ if (this.options.mode === "empty") {
284
+ const hasPersistence = this.options.baseDirectory !== void 0 || this.sessionFsConfig !== null || // External runtimes manage their own persistence layer; the SDK
285
+ // can't enforce it from here.
286
+ conn.kind === "uri" || conn.kind === "parent-process";
287
+ if (!hasPersistence) {
288
+ throw new Error(
289
+ "CopilotClient was created with mode: 'empty' but neither 'baseDirectory' nor 'sessionFs' was set. Empty mode requires an explicit per-session persistence location; pick one."
290
+ );
291
+ }
292
+ }
254
293
  }
255
294
  connectionExtraArgs = [];
256
295
  /**
@@ -560,54 +599,190 @@ class CopilotClient {
560
599
  * });
561
600
  * ```
562
601
  */
563
- async createSession(config) {
564
- if (!this.connection) {
565
- await this.start();
602
+ /**
603
+ * Normalizes session-level tool filter options. Converts {@link ToolSet}
604
+ * instances to plain string arrays, rejects misuse (bare `"*"`) and the
605
+ * missing-availableTools case in `mode = "empty"`.
606
+ *
607
+ * The SDK always sends `toolFilterPrecedence: "excluded"` so callers can
608
+ * compose include + exclude lists naturally (e.g. "everything matching X
609
+ * except Y") regardless of mode. Allowlist-precedence is intentionally not
610
+ * exposed — it's available on the runtime side as a CLI-only concession to
611
+ * legacy behavior, but SDK consumers always get the composable semantics.
612
+ *
613
+ * @internal
614
+ */
615
+ resolveToolFilterOptions(config) {
616
+ const availableTools = toolFilterListToArray(config.availableTools);
617
+ const excludedTools = toolFilterListToArray(config.excludedTools);
618
+ validateToolFilterList("availableTools", availableTools);
619
+ validateToolFilterList("excludedTools", excludedTools);
620
+ if (this.options.mode === "empty") {
621
+ if (availableTools === void 0) {
622
+ throw new Error(
623
+ "CopilotClient is in mode: 'empty' but the session config did not specify 'availableTools'. Empty mode requires every session to explicitly opt into the tools it wants \u2014 e.g. `new ToolSet().addBuiltIn(BuiltInTools.Isolated)`."
624
+ );
625
+ }
566
626
  }
567
- const sessionId = config.sessionId ?? (0, import_node_crypto.randomUUID)();
568
- const session = new import_session.CopilotSession(
569
- sessionId,
570
- this.connection,
571
- void 0,
572
- this.onGetTraceContext
573
- );
574
- session.registerTools(config.tools);
575
- session.registerCanvases(config.canvases);
576
- session.registerCommands(config.commands);
577
- session.registerPermissionHandler(config.onPermissionRequest);
578
- if (config.onUserInputRequest) {
579
- session.registerUserInputHandler(config.onUserInputRequest);
627
+ return { availableTools, excludedTools, toolFilterPrecedence: "excluded" };
628
+ }
629
+ /** Mode-specific defaults spread under the caller's config (app values win). */
630
+ configDefaultsForMode() {
631
+ if (this.options.mode === "empty") {
632
+ return {
633
+ enableSessionTelemetry: false,
634
+ mcpOAuthTokenStorage: "in-memory",
635
+ skipEmbeddingRetrieval: true,
636
+ embeddingCacheStorage: "in-memory",
637
+ enableOnDemandInstructionDiscovery: false,
638
+ enableFileHooks: false,
639
+ enableHostGitOperations: false,
640
+ enableSessionStore: false,
641
+ enableSkills: false
642
+ };
580
643
  }
581
- if (config.onElicitationRequest) {
582
- session.registerElicitationHandler(config.onElicitationRequest);
644
+ return {};
645
+ }
646
+ /**
647
+ * Returns the systemMessage config to use, adjusted for the current mode.
648
+ * In empty mode we ensure the environment_context section is removed
649
+ * unless the app has already taken control of it. `append` (and
650
+ * unspecified) mode is promoted to `customize` so we can also strip
651
+ * environment_context; the caller's `content` is preserved verbatim
652
+ * because the runtime appends it as additional instructions in both
653
+ * customize and append modes.
654
+ */
655
+ getSystemMessageConfigForMode(supplied) {
656
+ if (this.options.mode !== "empty") return supplied;
657
+ if (!supplied) {
658
+ return {
659
+ mode: "customize",
660
+ sections: { environment_context: { action: "remove" } }
661
+ };
583
662
  }
584
- if (config.onExitPlanModeRequest) {
585
- session.registerExitPlanModeHandler(config.onExitPlanModeRequest);
663
+ switch (supplied.mode) {
664
+ case "replace":
665
+ return supplied;
666
+ case "customize":
667
+ if (supplied.sections?.environment_context) return supplied;
668
+ return {
669
+ ...supplied,
670
+ sections: {
671
+ ...supplied.sections,
672
+ environment_context: { action: "remove" }
673
+ }
674
+ };
675
+ case "append":
676
+ case void 0:
677
+ return {
678
+ mode: "customize",
679
+ content: supplied.content,
680
+ sections: { environment_context: { action: "remove" } }
681
+ };
586
682
  }
587
- if (config.onAutoModeSwitchRequest) {
588
- session.registerAutoModeSwitchHandler(config.onAutoModeSwitchRequest);
683
+ }
684
+ /**
685
+ * Mode-specific options applied via session.options.update after create/resume.
686
+ *
687
+ * In empty mode, defaults the four overridable feature flags to safe values
688
+ * (caller values from `config` win). `installedPlugins=[]` is unconditional
689
+ * in empty mode — apps that need custom plugins should switch modes.
690
+ */
691
+ async updateSessionOptionsForMode(session, config) {
692
+ const patch = {};
693
+ if (this.options.mode === "empty") {
694
+ patch.skipCustomInstructions = config.skipCustomInstructions ?? true;
695
+ patch.customAgentsLocalOnly = config.customAgentsLocalOnly ?? true;
696
+ patch.coauthorEnabled = config.coauthorEnabled ?? false;
697
+ patch.manageScheduleEnabled = config.manageScheduleEnabled ?? false;
698
+ patch.installedPlugins = [];
699
+ } else {
700
+ if (config.skipCustomInstructions !== void 0)
701
+ patch.skipCustomInstructions = config.skipCustomInstructions;
702
+ if (config.customAgentsLocalOnly !== void 0)
703
+ patch.customAgentsLocalOnly = config.customAgentsLocalOnly;
704
+ if (config.coauthorEnabled !== void 0)
705
+ patch.coauthorEnabled = config.coauthorEnabled;
706
+ if (config.manageScheduleEnabled !== void 0)
707
+ patch.manageScheduleEnabled = config.manageScheduleEnabled;
708
+ }
709
+ if (Object.keys(patch).length === 0) {
710
+ return;
589
711
  }
590
- if (config.hooks) {
591
- session.registerHooks(config.hooks);
712
+ try {
713
+ await session.rpc.options.update(patch);
714
+ } catch (e) {
715
+ try {
716
+ await session.disconnect();
717
+ } catch {
718
+ }
719
+ throw e;
720
+ }
721
+ }
722
+ async createSession(config) {
723
+ if (!this.connection) {
724
+ await this.start();
592
725
  }
726
+ config = { ...this.configDefaultsForMode(), ...config };
727
+ config.systemMessage = this.getSystemMessageConfigForMode(config.systemMessage);
728
+ const callerSessionId = config.sessionId;
729
+ const useServerGeneratedId = config.cloud != null && callerSessionId == null;
730
+ const localSessionId = useServerGeneratedId ? void 0 : callerSessionId ?? (0, import_node_crypto.randomUUID)();
593
731
  const { wirePayload: wireSystemMessage, transformCallbacks } = extractTransformCallbacks(
594
732
  config.systemMessage
595
733
  );
596
- if (transformCallbacks) {
597
- session.registerTransformCallbacks(transformCallbacks);
598
- }
599
- if (config.onEvent) {
600
- session.on(config.onEvent);
734
+ const initializeSession = (sessionId) => {
735
+ const s = new import_session.CopilotSession(
736
+ sessionId,
737
+ this.connection,
738
+ void 0,
739
+ this.onGetTraceContext
740
+ );
741
+ s.registerTools(config.tools);
742
+ s.registerCanvases(config.canvases);
743
+ s.registerCommands(config.commands);
744
+ s.registerPermissionHandler(config.onPermissionRequest);
745
+ if (config.onUserInputRequest) {
746
+ s.registerUserInputHandler(config.onUserInputRequest);
747
+ }
748
+ if (config.onElicitationRequest) {
749
+ s.registerElicitationHandler(config.onElicitationRequest);
750
+ }
751
+ if (config.onExitPlanModeRequest) {
752
+ s.registerExitPlanModeHandler(config.onExitPlanModeRequest);
753
+ }
754
+ if (config.onAutoModeSwitchRequest) {
755
+ s.registerAutoModeSwitchHandler(config.onAutoModeSwitchRequest);
756
+ }
757
+ if (config.hooks) {
758
+ s.registerHooks(config.hooks);
759
+ }
760
+ if (transformCallbacks) {
761
+ s.registerTransformCallbacks(transformCallbacks);
762
+ }
763
+ if (config.onEvent) {
764
+ s.on(config.onEvent);
765
+ }
766
+ this.sessions.set(sessionId, s);
767
+ this.setupSessionFs(s, config);
768
+ return s;
769
+ };
770
+ let session;
771
+ let registeredId;
772
+ if (localSessionId !== void 0) {
773
+ session = initializeSession(localSessionId);
774
+ registeredId = localSessionId;
601
775
  }
602
- this.sessions.set(sessionId, session);
603
- this.setupSessionFs(session, config);
776
+ const toolFilterOptions = this.resolveToolFilterOptions(config);
604
777
  try {
605
778
  const response = await this.connection.sendRequest("session.create", {
606
779
  ...await (0, import_telemetry.getTraceContext)(this.onGetTraceContext),
607
780
  model: config.model,
608
- sessionId,
781
+ sessionId: localSessionId,
609
782
  clientName: config.clientName,
610
783
  reasoningEffort: config.reasoningEffort,
784
+ reasoningSummary: config.reasoningSummary,
785
+ contextTier: config.contextTier,
611
786
  tools: config.tools?.map((tool) => ({
612
787
  name: tool.name,
613
788
  description: tool.description,
@@ -618,20 +793,24 @@ class CopilotClient {
618
793
  canvases: config.canvases?.map((canvas) => canvas.declaration),
619
794
  requestCanvasRenderer: config.requestCanvasRenderer,
620
795
  requestExtensions: config.requestExtensions,
796
+ extensionSdkPath: config.extensionSdkPath,
621
797
  extensionInfo: config.extensionInfo,
622
798
  commands: config.commands?.map((cmd) => ({
623
799
  name: cmd.name,
624
800
  description: cmd.description
625
801
  })),
626
802
  systemMessage: wireSystemMessage,
627
- availableTools: config.availableTools,
628
- excludedTools: config.excludedTools,
803
+ availableTools: toolFilterOptions.availableTools,
804
+ excludedTools: toolFilterOptions.excludedTools,
805
+ toolFilterPrecedence: toolFilterOptions.toolFilterPrecedence,
629
806
  provider: config.provider,
630
807
  enableSessionTelemetry: config.enableSessionTelemetry,
631
808
  modelCapabilities: config.modelCapabilities,
809
+ largeOutput: toWireLargeOutput(config.largeOutput),
632
810
  requestPermission: !!config.onPermissionRequest,
633
811
  requestUserInput: !!config.onUserInputRequest,
634
812
  requestElicitation: !!config.onElicitationRequest,
813
+ ...config.enableMcpApps ? { requestMcpApps: true } : {},
635
814
  requestExitPlanMode: !!config.onExitPlanModeRequest,
636
815
  requestAutoModeSwitch: !!config.onAutoModeSwitchRequest,
637
816
  hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)),
@@ -639,13 +818,23 @@ class CopilotClient {
639
818
  streaming: config.streaming,
640
819
  includeSubAgentStreamingEvents: config.includeSubAgentStreamingEvents ?? true,
641
820
  mcpServers: toWireMcpServers(config.mcpServers),
821
+ mcpOAuthTokenStorage: config.mcpOAuthTokenStorage,
642
822
  envValueMode: "direct",
643
823
  customAgents: toWireCustomAgents(config.customAgents),
644
824
  defaultAgent: config.defaultAgent,
645
825
  agent: config.agent,
646
- configDir: config.configDir,
826
+ configDir: config.configDirectory,
647
827
  enableConfigDiscovery: config.enableConfigDiscovery,
828
+ skipEmbeddingRetrieval: config.skipEmbeddingRetrieval,
829
+ embeddingCacheStorage: config.embeddingCacheStorage,
830
+ organizationCustomInstructions: config.organizationCustomInstructions,
831
+ enableOnDemandInstructionDiscovery: config.enableOnDemandInstructionDiscovery,
832
+ enableFileHooks: config.enableFileHooks,
833
+ enableHostGitOperations: config.enableHostGitOperations,
834
+ enableSessionStore: config.enableSessionStore,
835
+ enableSkills: config.enableSkills,
648
836
  skillDirectories: config.skillDirectories,
837
+ pluginDirectories: config.pluginDirectories,
649
838
  instructionDirectories: config.instructionDirectories,
650
839
  disabledSkills: config.disabledSkills,
651
840
  infiniteSessions: config.infiniteSessions,
@@ -653,11 +842,30 @@ class CopilotClient {
653
842
  remoteSession: config.remoteSession,
654
843
  cloud: config.cloud
655
844
  });
656
- const { workspacePath, capabilities } = response;
845
+ const {
846
+ sessionId: returnedSessionId,
847
+ workspacePath,
848
+ capabilities
849
+ } = response;
850
+ if (!returnedSessionId) {
851
+ throw new Error("session.create response did not include a sessionId");
852
+ }
853
+ if (localSessionId !== void 0 && localSessionId !== returnedSessionId) {
854
+ throw new Error(
855
+ `session.create returned sessionId ${returnedSessionId} but the caller requested ${localSessionId}`
856
+ );
857
+ }
858
+ if (session === void 0) {
859
+ session = initializeSession(returnedSessionId);
860
+ registeredId = returnedSessionId;
861
+ }
657
862
  session["_workspacePath"] = workspacePath;
658
863
  session.setCapabilities(capabilities);
864
+ await this.updateSessionOptionsForMode(session, config);
659
865
  } catch (e) {
660
- this.sessions.delete(sessionId);
866
+ if (registeredId !== void 0) {
867
+ this.sessions.delete(registeredId);
868
+ }
661
869
  throw e;
662
870
  }
663
871
  return session;
@@ -715,6 +923,8 @@ class CopilotClient {
715
923
  if (config.hooks) {
716
924
  session.registerHooks(config.hooks);
717
925
  }
926
+ config = { ...this.configDefaultsForMode(), ...config };
927
+ config.systemMessage = this.getSystemMessageConfigForMode(config.systemMessage);
718
928
  const { wirePayload: wireSystemMessage, transformCallbacks } = extractTransformCallbacks(
719
929
  config.systemMessage
720
930
  );
@@ -726,6 +936,7 @@ class CopilotClient {
726
936
  }
727
937
  this.sessions.set(sessionId, session);
728
938
  this.setupSessionFs(session, config);
939
+ const toolFilterOptions = this.resolveToolFilterOptions(config);
729
940
  try {
730
941
  const response = await this.connection.sendRequest("session.resume", {
731
942
  ...await (0, import_telemetry.getTraceContext)(this.onGetTraceContext),
@@ -733,9 +944,12 @@ class CopilotClient {
733
944
  clientName: config.clientName,
734
945
  model: config.model,
735
946
  reasoningEffort: config.reasoningEffort,
947
+ reasoningSummary: config.reasoningSummary,
948
+ contextTier: config.contextTier,
736
949
  systemMessage: wireSystemMessage,
737
- availableTools: config.availableTools,
738
- excludedTools: config.excludedTools,
950
+ availableTools: toolFilterOptions.availableTools,
951
+ excludedTools: toolFilterOptions.excludedTools,
952
+ toolFilterPrecedence: toolFilterOptions.toolFilterPrecedence,
739
953
  enableSessionTelemetry: config.enableSessionTelemetry,
740
954
  tools: config.tools?.map((tool) => ({
741
955
  name: tool.name,
@@ -747,6 +961,7 @@ class CopilotClient {
747
961
  canvases: config.canvases?.map((canvas) => canvas.declaration),
748
962
  requestCanvasRenderer: config.requestCanvasRenderer,
749
963
  requestExtensions: config.requestExtensions,
964
+ extensionSdkPath: config.extensionSdkPath,
750
965
  extensionInfo: config.extensionInfo,
751
966
  commands: config.commands?.map((cmd) => ({
752
967
  name: cmd.name,
@@ -754,27 +969,39 @@ class CopilotClient {
754
969
  })),
755
970
  provider: config.provider,
756
971
  modelCapabilities: config.modelCapabilities,
972
+ largeOutput: toWireLargeOutput(config.largeOutput),
757
973
  requestPermission: config.onPermissionRequest !== import_types.defaultJoinSessionPermissionHandler,
758
974
  requestUserInput: !!config.onUserInputRequest,
759
975
  requestElicitation: !!config.onElicitationRequest,
976
+ ...config.enableMcpApps ? { requestMcpApps: true } : {},
760
977
  requestExitPlanMode: !!config.onExitPlanModeRequest,
761
978
  requestAutoModeSwitch: !!config.onAutoModeSwitchRequest,
762
979
  hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)),
763
980
  workingDirectory: config.workingDirectory,
764
- configDir: config.configDir,
981
+ configDir: config.configDirectory,
765
982
  enableConfigDiscovery: config.enableConfigDiscovery,
983
+ skipEmbeddingRetrieval: config.skipEmbeddingRetrieval,
984
+ embeddingCacheStorage: config.embeddingCacheStorage,
985
+ organizationCustomInstructions: config.organizationCustomInstructions,
986
+ enableOnDemandInstructionDiscovery: config.enableOnDemandInstructionDiscovery,
987
+ enableFileHooks: config.enableFileHooks,
988
+ enableHostGitOperations: config.enableHostGitOperations,
989
+ enableSessionStore: config.enableSessionStore,
990
+ enableSkills: config.enableSkills,
766
991
  streaming: config.streaming,
767
992
  includeSubAgentStreamingEvents: config.includeSubAgentStreamingEvents ?? true,
768
993
  mcpServers: toWireMcpServers(config.mcpServers),
994
+ mcpOAuthTokenStorage: config.mcpOAuthTokenStorage,
769
995
  envValueMode: "direct",
770
996
  customAgents: toWireCustomAgents(config.customAgents),
771
997
  defaultAgent: config.defaultAgent,
772
998
  agent: config.agent,
773
999
  skillDirectories: config.skillDirectories,
1000
+ pluginDirectories: config.pluginDirectories,
774
1001
  instructionDirectories: config.instructionDirectories,
775
1002
  disabledSkills: config.disabledSkills,
776
1003
  infiniteSessions: config.infiniteSessions,
777
- suppressResumeEvent: config.suppressResumeEvent,
1004
+ disableResume: config.suppressResumeEvent,
778
1005
  continuePendingWork: config.continuePendingWork,
779
1006
  gitHubToken: config.gitHubToken,
780
1007
  remoteSession: config.remoteSession,
@@ -784,6 +1011,7 @@ class CopilotClient {
784
1011
  session["_workspacePath"] = workspacePath;
785
1012
  session.setCapabilities(capabilities);
786
1013
  session.setOpenCanvases(openCanvases ?? []);
1014
+ await this.updateSessionOptionsForMode(session, config);
787
1015
  } catch (e) {
788
1016
  this.sessions.delete(sessionId);
789
1017
  throw e;
@@ -1162,6 +1390,9 @@ class CopilotClient {
1162
1390
  if (this.options.baseDirectory) {
1163
1391
  envWithoutNodeDebug.COPILOT_HOME = this.options.baseDirectory;
1164
1392
  }
1393
+ if (this.options.mode === "empty") {
1394
+ envWithoutNodeDebug.COPILOT_DISABLE_KEYTAR = "1";
1395
+ }
1165
1396
  if (!this.resolvedCliPath) {
1166
1397
  throw new Error(
1167
1398
  "Path to Copilot CLI is required. Please supply it via `RuntimeConnection.forStdio({ path })` or `RuntimeConnection.forTcp({ path })`, set the COPILOT_CLI_PATH environment variable, or use `RuntimeConnection.forUri(...)` to connect to an already-running runtime."
@@ -35,8 +35,10 @@ async function joinSession(config = {}) {
35
35
  );
36
36
  }
37
37
  const client = new import_client.CopilotClient({ _internalConnection: { kind: "parent-process" } });
38
+ const { extensionSdkPath: _stripped, ...rest } = config;
39
+ void _stripped;
38
40
  return client.resumeSession(sessionId, {
39
- ...config,
41
+ ...rest,
40
42
  onPermissionRequest: config.onPermissionRequest ?? import_types.defaultJoinSessionPermissionHandler,
41
43
  suppressResumeEvent: config.suppressResumeEvent ?? true
42
44
  });