@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/dist/client.js CHANGED
@@ -21,6 +21,7 @@ import { getSdkProtocolVersion } from "./sdkProtocolVersion.js";
21
21
  import { CopilotSession } from "./session.js";
22
22
  import { createSessionFsAdapter } from "./sessionFsProvider.js";
23
23
  import { getTraceContext } from "./telemetry.js";
24
+ import { ToolSet } from "./toolSet.js";
24
25
  import { defaultJoinSessionPermissionHandler } from "./types.js";
25
26
  const MIN_PROTOCOL_VERSION = 3;
26
27
  function isZodSchema(value) {
@@ -53,6 +54,33 @@ function toWireCustomAgents(agents) {
53
54
  return { ...rest, mcpServers: toWireMcpServers(mcpServers) };
54
55
  });
55
56
  }
57
+ function toWireLargeOutput(config) {
58
+ if (!config) return void 0;
59
+ const { outputDirectory, ...rest } = config;
60
+ const wire = { ...rest };
61
+ if (outputDirectory !== void 0) {
62
+ wire.outputDir = outputDirectory;
63
+ }
64
+ return wire;
65
+ }
66
+ function toolFilterListToArray(value) {
67
+ if (value === void 0) {
68
+ return void 0;
69
+ }
70
+ return value instanceof ToolSet ? value.toArray() : value;
71
+ }
72
+ function validateToolFilterList(field, list) {
73
+ if (!list) {
74
+ return;
75
+ }
76
+ for (const entry of list) {
77
+ if (entry === "*") {
78
+ throw new Error(
79
+ `Invalid ${field} entry '*': there is no bare wildcard. Use one or more of \`new ToolSet().addBuiltIn('*')\`, \`.addMcp('*')\`, or \`.addCustom('*')\` to target a specific source.`
80
+ );
81
+ }
82
+ }
83
+ }
56
84
  function extractTransformCallbacks(systemMessage) {
57
85
  if (!systemMessage || systemMessage.mode !== "customize" || !systemMessage.sections) {
58
86
  return { wirePayload: systemMessage, transformCallbacks: void 0 };
@@ -235,8 +263,19 @@ class CopilotClient {
235
263
  telemetry: options.telemetry,
236
264
  baseDirectory: options.baseDirectory,
237
265
  sessionIdleTimeoutSeconds: options.sessionIdleTimeoutSeconds ?? 0,
238
- enableRemoteSessions: options.enableRemoteSessions ?? false
266
+ enableRemoteSessions: options.enableRemoteSessions ?? false,
267
+ mode: options.mode ?? "copilot-cli"
239
268
  };
269
+ if (this.options.mode === "empty") {
270
+ const hasPersistence = this.options.baseDirectory !== void 0 || this.sessionFsConfig !== null || // External runtimes manage their own persistence layer; the SDK
271
+ // can't enforce it from here.
272
+ conn.kind === "uri" || conn.kind === "parent-process";
273
+ if (!hasPersistence) {
274
+ throw new Error(
275
+ "CopilotClient was created with mode: 'empty' but neither 'baseDirectory' nor 'sessionFs' was set. Empty mode requires an explicit per-session persistence location; pick one."
276
+ );
277
+ }
278
+ }
240
279
  }
241
280
  connectionExtraArgs = [];
242
281
  /**
@@ -546,54 +585,190 @@ class CopilotClient {
546
585
  * });
547
586
  * ```
548
587
  */
549
- async createSession(config) {
550
- if (!this.connection) {
551
- await this.start();
588
+ /**
589
+ * Normalizes session-level tool filter options. Converts {@link ToolSet}
590
+ * instances to plain string arrays, rejects misuse (bare `"*"`) and the
591
+ * missing-availableTools case in `mode = "empty"`.
592
+ *
593
+ * The SDK always sends `toolFilterPrecedence: "excluded"` so callers can
594
+ * compose include + exclude lists naturally (e.g. "everything matching X
595
+ * except Y") regardless of mode. Allowlist-precedence is intentionally not
596
+ * exposed — it's available on the runtime side as a CLI-only concession to
597
+ * legacy behavior, but SDK consumers always get the composable semantics.
598
+ *
599
+ * @internal
600
+ */
601
+ resolveToolFilterOptions(config) {
602
+ const availableTools = toolFilterListToArray(config.availableTools);
603
+ const excludedTools = toolFilterListToArray(config.excludedTools);
604
+ validateToolFilterList("availableTools", availableTools);
605
+ validateToolFilterList("excludedTools", excludedTools);
606
+ if (this.options.mode === "empty") {
607
+ if (availableTools === void 0) {
608
+ throw new Error(
609
+ "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)`."
610
+ );
611
+ }
552
612
  }
553
- const sessionId = config.sessionId ?? randomUUID();
554
- const session = new CopilotSession(
555
- sessionId,
556
- this.connection,
557
- void 0,
558
- this.onGetTraceContext
559
- );
560
- session.registerTools(config.tools);
561
- session.registerCanvases(config.canvases);
562
- session.registerCommands(config.commands);
563
- session.registerPermissionHandler(config.onPermissionRequest);
564
- if (config.onUserInputRequest) {
565
- session.registerUserInputHandler(config.onUserInputRequest);
613
+ return { availableTools, excludedTools, toolFilterPrecedence: "excluded" };
614
+ }
615
+ /** Mode-specific defaults spread under the caller's config (app values win). */
616
+ configDefaultsForMode() {
617
+ if (this.options.mode === "empty") {
618
+ return {
619
+ enableSessionTelemetry: false,
620
+ mcpOAuthTokenStorage: "in-memory",
621
+ skipEmbeddingRetrieval: true,
622
+ embeddingCacheStorage: "in-memory",
623
+ enableOnDemandInstructionDiscovery: false,
624
+ enableFileHooks: false,
625
+ enableHostGitOperations: false,
626
+ enableSessionStore: false,
627
+ enableSkills: false
628
+ };
566
629
  }
567
- if (config.onElicitationRequest) {
568
- session.registerElicitationHandler(config.onElicitationRequest);
630
+ return {};
631
+ }
632
+ /**
633
+ * Returns the systemMessage config to use, adjusted for the current mode.
634
+ * In empty mode we ensure the environment_context section is removed
635
+ * unless the app has already taken control of it. `append` (and
636
+ * unspecified) mode is promoted to `customize` so we can also strip
637
+ * environment_context; the caller's `content` is preserved verbatim
638
+ * because the runtime appends it as additional instructions in both
639
+ * customize and append modes.
640
+ */
641
+ getSystemMessageConfigForMode(supplied) {
642
+ if (this.options.mode !== "empty") return supplied;
643
+ if (!supplied) {
644
+ return {
645
+ mode: "customize",
646
+ sections: { environment_context: { action: "remove" } }
647
+ };
569
648
  }
570
- if (config.onExitPlanModeRequest) {
571
- session.registerExitPlanModeHandler(config.onExitPlanModeRequest);
649
+ switch (supplied.mode) {
650
+ case "replace":
651
+ return supplied;
652
+ case "customize":
653
+ if (supplied.sections?.environment_context) return supplied;
654
+ return {
655
+ ...supplied,
656
+ sections: {
657
+ ...supplied.sections,
658
+ environment_context: { action: "remove" }
659
+ }
660
+ };
661
+ case "append":
662
+ case void 0:
663
+ return {
664
+ mode: "customize",
665
+ content: supplied.content,
666
+ sections: { environment_context: { action: "remove" } }
667
+ };
572
668
  }
573
- if (config.onAutoModeSwitchRequest) {
574
- session.registerAutoModeSwitchHandler(config.onAutoModeSwitchRequest);
669
+ }
670
+ /**
671
+ * Mode-specific options applied via session.options.update after create/resume.
672
+ *
673
+ * In empty mode, defaults the four overridable feature flags to safe values
674
+ * (caller values from `config` win). `installedPlugins=[]` is unconditional
675
+ * in empty mode — apps that need custom plugins should switch modes.
676
+ */
677
+ async updateSessionOptionsForMode(session, config) {
678
+ const patch = {};
679
+ if (this.options.mode === "empty") {
680
+ patch.skipCustomInstructions = config.skipCustomInstructions ?? true;
681
+ patch.customAgentsLocalOnly = config.customAgentsLocalOnly ?? true;
682
+ patch.coauthorEnabled = config.coauthorEnabled ?? false;
683
+ patch.manageScheduleEnabled = config.manageScheduleEnabled ?? false;
684
+ patch.installedPlugins = [];
685
+ } else {
686
+ if (config.skipCustomInstructions !== void 0)
687
+ patch.skipCustomInstructions = config.skipCustomInstructions;
688
+ if (config.customAgentsLocalOnly !== void 0)
689
+ patch.customAgentsLocalOnly = config.customAgentsLocalOnly;
690
+ if (config.coauthorEnabled !== void 0)
691
+ patch.coauthorEnabled = config.coauthorEnabled;
692
+ if (config.manageScheduleEnabled !== void 0)
693
+ patch.manageScheduleEnabled = config.manageScheduleEnabled;
694
+ }
695
+ if (Object.keys(patch).length === 0) {
696
+ return;
575
697
  }
576
- if (config.hooks) {
577
- session.registerHooks(config.hooks);
698
+ try {
699
+ await session.rpc.options.update(patch);
700
+ } catch (e) {
701
+ try {
702
+ await session.disconnect();
703
+ } catch {
704
+ }
705
+ throw e;
706
+ }
707
+ }
708
+ async createSession(config) {
709
+ if (!this.connection) {
710
+ await this.start();
578
711
  }
712
+ config = { ...this.configDefaultsForMode(), ...config };
713
+ config.systemMessage = this.getSystemMessageConfigForMode(config.systemMessage);
714
+ const callerSessionId = config.sessionId;
715
+ const useServerGeneratedId = config.cloud != null && callerSessionId == null;
716
+ const localSessionId = useServerGeneratedId ? void 0 : callerSessionId ?? randomUUID();
579
717
  const { wirePayload: wireSystemMessage, transformCallbacks } = extractTransformCallbacks(
580
718
  config.systemMessage
581
719
  );
582
- if (transformCallbacks) {
583
- session.registerTransformCallbacks(transformCallbacks);
584
- }
585
- if (config.onEvent) {
586
- session.on(config.onEvent);
720
+ const initializeSession = (sessionId) => {
721
+ const s = new CopilotSession(
722
+ sessionId,
723
+ this.connection,
724
+ void 0,
725
+ this.onGetTraceContext
726
+ );
727
+ s.registerTools(config.tools);
728
+ s.registerCanvases(config.canvases);
729
+ s.registerCommands(config.commands);
730
+ s.registerPermissionHandler(config.onPermissionRequest);
731
+ if (config.onUserInputRequest) {
732
+ s.registerUserInputHandler(config.onUserInputRequest);
733
+ }
734
+ if (config.onElicitationRequest) {
735
+ s.registerElicitationHandler(config.onElicitationRequest);
736
+ }
737
+ if (config.onExitPlanModeRequest) {
738
+ s.registerExitPlanModeHandler(config.onExitPlanModeRequest);
739
+ }
740
+ if (config.onAutoModeSwitchRequest) {
741
+ s.registerAutoModeSwitchHandler(config.onAutoModeSwitchRequest);
742
+ }
743
+ if (config.hooks) {
744
+ s.registerHooks(config.hooks);
745
+ }
746
+ if (transformCallbacks) {
747
+ s.registerTransformCallbacks(transformCallbacks);
748
+ }
749
+ if (config.onEvent) {
750
+ s.on(config.onEvent);
751
+ }
752
+ this.sessions.set(sessionId, s);
753
+ this.setupSessionFs(s, config);
754
+ return s;
755
+ };
756
+ let session;
757
+ let registeredId;
758
+ if (localSessionId !== void 0) {
759
+ session = initializeSession(localSessionId);
760
+ registeredId = localSessionId;
587
761
  }
588
- this.sessions.set(sessionId, session);
589
- this.setupSessionFs(session, config);
762
+ const toolFilterOptions = this.resolveToolFilterOptions(config);
590
763
  try {
591
764
  const response = await this.connection.sendRequest("session.create", {
592
765
  ...await getTraceContext(this.onGetTraceContext),
593
766
  model: config.model,
594
- sessionId,
767
+ sessionId: localSessionId,
595
768
  clientName: config.clientName,
596
769
  reasoningEffort: config.reasoningEffort,
770
+ reasoningSummary: config.reasoningSummary,
771
+ contextTier: config.contextTier,
597
772
  tools: config.tools?.map((tool) => ({
598
773
  name: tool.name,
599
774
  description: tool.description,
@@ -604,20 +779,24 @@ class CopilotClient {
604
779
  canvases: config.canvases?.map((canvas) => canvas.declaration),
605
780
  requestCanvasRenderer: config.requestCanvasRenderer,
606
781
  requestExtensions: config.requestExtensions,
782
+ extensionSdkPath: config.extensionSdkPath,
607
783
  extensionInfo: config.extensionInfo,
608
784
  commands: config.commands?.map((cmd) => ({
609
785
  name: cmd.name,
610
786
  description: cmd.description
611
787
  })),
612
788
  systemMessage: wireSystemMessage,
613
- availableTools: config.availableTools,
614
- excludedTools: config.excludedTools,
789
+ availableTools: toolFilterOptions.availableTools,
790
+ excludedTools: toolFilterOptions.excludedTools,
791
+ toolFilterPrecedence: toolFilterOptions.toolFilterPrecedence,
615
792
  provider: config.provider,
616
793
  enableSessionTelemetry: config.enableSessionTelemetry,
617
794
  modelCapabilities: config.modelCapabilities,
795
+ largeOutput: toWireLargeOutput(config.largeOutput),
618
796
  requestPermission: !!config.onPermissionRequest,
619
797
  requestUserInput: !!config.onUserInputRequest,
620
798
  requestElicitation: !!config.onElicitationRequest,
799
+ ...config.enableMcpApps ? { requestMcpApps: true } : {},
621
800
  requestExitPlanMode: !!config.onExitPlanModeRequest,
622
801
  requestAutoModeSwitch: !!config.onAutoModeSwitchRequest,
623
802
  hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)),
@@ -625,13 +804,23 @@ class CopilotClient {
625
804
  streaming: config.streaming,
626
805
  includeSubAgentStreamingEvents: config.includeSubAgentStreamingEvents ?? true,
627
806
  mcpServers: toWireMcpServers(config.mcpServers),
807
+ mcpOAuthTokenStorage: config.mcpOAuthTokenStorage,
628
808
  envValueMode: "direct",
629
809
  customAgents: toWireCustomAgents(config.customAgents),
630
810
  defaultAgent: config.defaultAgent,
631
811
  agent: config.agent,
632
- configDir: config.configDir,
812
+ configDir: config.configDirectory,
633
813
  enableConfigDiscovery: config.enableConfigDiscovery,
814
+ skipEmbeddingRetrieval: config.skipEmbeddingRetrieval,
815
+ embeddingCacheStorage: config.embeddingCacheStorage,
816
+ organizationCustomInstructions: config.organizationCustomInstructions,
817
+ enableOnDemandInstructionDiscovery: config.enableOnDemandInstructionDiscovery,
818
+ enableFileHooks: config.enableFileHooks,
819
+ enableHostGitOperations: config.enableHostGitOperations,
820
+ enableSessionStore: config.enableSessionStore,
821
+ enableSkills: config.enableSkills,
634
822
  skillDirectories: config.skillDirectories,
823
+ pluginDirectories: config.pluginDirectories,
635
824
  instructionDirectories: config.instructionDirectories,
636
825
  disabledSkills: config.disabledSkills,
637
826
  infiniteSessions: config.infiniteSessions,
@@ -639,11 +828,30 @@ class CopilotClient {
639
828
  remoteSession: config.remoteSession,
640
829
  cloud: config.cloud
641
830
  });
642
- const { workspacePath, capabilities } = response;
831
+ const {
832
+ sessionId: returnedSessionId,
833
+ workspacePath,
834
+ capabilities
835
+ } = response;
836
+ if (!returnedSessionId) {
837
+ throw new Error("session.create response did not include a sessionId");
838
+ }
839
+ if (localSessionId !== void 0 && localSessionId !== returnedSessionId) {
840
+ throw new Error(
841
+ `session.create returned sessionId ${returnedSessionId} but the caller requested ${localSessionId}`
842
+ );
843
+ }
844
+ if (session === void 0) {
845
+ session = initializeSession(returnedSessionId);
846
+ registeredId = returnedSessionId;
847
+ }
643
848
  session["_workspacePath"] = workspacePath;
644
849
  session.setCapabilities(capabilities);
850
+ await this.updateSessionOptionsForMode(session, config);
645
851
  } catch (e) {
646
- this.sessions.delete(sessionId);
852
+ if (registeredId !== void 0) {
853
+ this.sessions.delete(registeredId);
854
+ }
647
855
  throw e;
648
856
  }
649
857
  return session;
@@ -701,6 +909,8 @@ class CopilotClient {
701
909
  if (config.hooks) {
702
910
  session.registerHooks(config.hooks);
703
911
  }
912
+ config = { ...this.configDefaultsForMode(), ...config };
913
+ config.systemMessage = this.getSystemMessageConfigForMode(config.systemMessage);
704
914
  const { wirePayload: wireSystemMessage, transformCallbacks } = extractTransformCallbacks(
705
915
  config.systemMessage
706
916
  );
@@ -712,6 +922,7 @@ class CopilotClient {
712
922
  }
713
923
  this.sessions.set(sessionId, session);
714
924
  this.setupSessionFs(session, config);
925
+ const toolFilterOptions = this.resolveToolFilterOptions(config);
715
926
  try {
716
927
  const response = await this.connection.sendRequest("session.resume", {
717
928
  ...await getTraceContext(this.onGetTraceContext),
@@ -719,9 +930,12 @@ class CopilotClient {
719
930
  clientName: config.clientName,
720
931
  model: config.model,
721
932
  reasoningEffort: config.reasoningEffort,
933
+ reasoningSummary: config.reasoningSummary,
934
+ contextTier: config.contextTier,
722
935
  systemMessage: wireSystemMessage,
723
- availableTools: config.availableTools,
724
- excludedTools: config.excludedTools,
936
+ availableTools: toolFilterOptions.availableTools,
937
+ excludedTools: toolFilterOptions.excludedTools,
938
+ toolFilterPrecedence: toolFilterOptions.toolFilterPrecedence,
725
939
  enableSessionTelemetry: config.enableSessionTelemetry,
726
940
  tools: config.tools?.map((tool) => ({
727
941
  name: tool.name,
@@ -733,6 +947,7 @@ class CopilotClient {
733
947
  canvases: config.canvases?.map((canvas) => canvas.declaration),
734
948
  requestCanvasRenderer: config.requestCanvasRenderer,
735
949
  requestExtensions: config.requestExtensions,
950
+ extensionSdkPath: config.extensionSdkPath,
736
951
  extensionInfo: config.extensionInfo,
737
952
  commands: config.commands?.map((cmd) => ({
738
953
  name: cmd.name,
@@ -740,27 +955,39 @@ class CopilotClient {
740
955
  })),
741
956
  provider: config.provider,
742
957
  modelCapabilities: config.modelCapabilities,
958
+ largeOutput: toWireLargeOutput(config.largeOutput),
743
959
  requestPermission: config.onPermissionRequest !== defaultJoinSessionPermissionHandler,
744
960
  requestUserInput: !!config.onUserInputRequest,
745
961
  requestElicitation: !!config.onElicitationRequest,
962
+ ...config.enableMcpApps ? { requestMcpApps: true } : {},
746
963
  requestExitPlanMode: !!config.onExitPlanModeRequest,
747
964
  requestAutoModeSwitch: !!config.onAutoModeSwitchRequest,
748
965
  hooks: !!(config.hooks && Object.values(config.hooks).some(Boolean)),
749
966
  workingDirectory: config.workingDirectory,
750
- configDir: config.configDir,
967
+ configDir: config.configDirectory,
751
968
  enableConfigDiscovery: config.enableConfigDiscovery,
969
+ skipEmbeddingRetrieval: config.skipEmbeddingRetrieval,
970
+ embeddingCacheStorage: config.embeddingCacheStorage,
971
+ organizationCustomInstructions: config.organizationCustomInstructions,
972
+ enableOnDemandInstructionDiscovery: config.enableOnDemandInstructionDiscovery,
973
+ enableFileHooks: config.enableFileHooks,
974
+ enableHostGitOperations: config.enableHostGitOperations,
975
+ enableSessionStore: config.enableSessionStore,
976
+ enableSkills: config.enableSkills,
752
977
  streaming: config.streaming,
753
978
  includeSubAgentStreamingEvents: config.includeSubAgentStreamingEvents ?? true,
754
979
  mcpServers: toWireMcpServers(config.mcpServers),
980
+ mcpOAuthTokenStorage: config.mcpOAuthTokenStorage,
755
981
  envValueMode: "direct",
756
982
  customAgents: toWireCustomAgents(config.customAgents),
757
983
  defaultAgent: config.defaultAgent,
758
984
  agent: config.agent,
759
985
  skillDirectories: config.skillDirectories,
986
+ pluginDirectories: config.pluginDirectories,
760
987
  instructionDirectories: config.instructionDirectories,
761
988
  disabledSkills: config.disabledSkills,
762
989
  infiniteSessions: config.infiniteSessions,
763
- suppressResumeEvent: config.suppressResumeEvent,
990
+ disableResume: config.suppressResumeEvent,
764
991
  continuePendingWork: config.continuePendingWork,
765
992
  gitHubToken: config.gitHubToken,
766
993
  remoteSession: config.remoteSession,
@@ -770,6 +997,7 @@ class CopilotClient {
770
997
  session["_workspacePath"] = workspacePath;
771
998
  session.setCapabilities(capabilities);
772
999
  session.setOpenCanvases(openCanvases ?? []);
1000
+ await this.updateSessionOptionsForMode(session, config);
773
1001
  } catch (e) {
774
1002
  this.sessions.delete(sessionId);
775
1003
  throw e;
@@ -1148,6 +1376,9 @@ class CopilotClient {
1148
1376
  if (this.options.baseDirectory) {
1149
1377
  envWithoutNodeDebug.COPILOT_HOME = this.options.baseDirectory;
1150
1378
  }
1379
+ if (this.options.mode === "empty") {
1380
+ envWithoutNodeDebug.COPILOT_DISABLE_KEYTAR = "1";
1381
+ }
1151
1382
  if (!this.resolvedCliPath) {
1152
1383
  throw new Error(
1153
1384
  "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."
@@ -1,7 +1,7 @@
1
1
  import type { CopilotSession } from "./session.js";
2
2
  import { type ExtensionInfo, type PermissionHandler, type ResumeSessionConfig } from "./types.js";
3
3
  export { Canvas, CanvasError, createCanvas, type CanvasAction, type CanvasDeclaration, type CanvasHostContext, type CanvasJsonSchema, type CanvasOptions, } from "./canvas.js";
4
- export type JoinSessionConfig = Omit<ResumeSessionConfig, "onPermissionRequest"> & {
4
+ export type JoinSessionConfig = Omit<ResumeSessionConfig, "onPermissionRequest" | "extensionSdkPath"> & {
5
5
  onPermissionRequest?: PermissionHandler;
6
6
  };
7
7
  export type { ExtensionInfo };
package/dist/extension.js CHANGED
@@ -15,8 +15,10 @@ async function joinSession(config = {}) {
15
15
  );
16
16
  }
17
17
  const client = new CopilotClient({ _internalConnection: { kind: "parent-process" } });
18
+ const { extensionSdkPath: _stripped, ...rest } = config;
19
+ void _stripped;
18
20
  return client.resumeSession(sessionId, {
19
- ...config,
21
+ ...rest,
20
22
  onPermissionRequest: config.onPermissionRequest ?? defaultJoinSessionPermissionHandler,
21
23
  suppressResumeEvent: config.suppressResumeEvent ?? true
22
24
  });