@github/copilot-sdk 1.0.0-beta.8 → 1.0.0-beta.9
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 +13 -2
- package/dist/cjs/client.js +158 -5
- package/dist/cjs/generated/rpc.js +26 -1
- package/dist/cjs/index.js +5 -0
- package/dist/cjs/session.js +2 -0
- package/dist/cjs/toolSet.js +107 -0
- package/dist/client.d.ts +17 -25
- package/dist/client.js +158 -5
- package/dist/generated/rpc.d.ts +472 -42
- package/dist/generated/rpc.js +26 -1
- package/dist/generated/session-events.d.ts +154 -1
- package/dist/index.d.ts +2 -1
- package/dist/index.js +3 -0
- package/dist/session.js +2 -0
- package/dist/toolSet.d.ts +75 -0
- package/dist/toolSet.js +82 -0
- package/dist/types.d.ts +134 -6
- package/docs/agent-author.md +31 -7
- package/docs/examples.md +20 -13
- package/package.json +2 -2
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,24 @@ function toWireCustomAgents(agents) {
|
|
|
53
54
|
return { ...rest, mcpServers: toWireMcpServers(mcpServers) };
|
|
54
55
|
});
|
|
55
56
|
}
|
|
57
|
+
function toolFilterListToArray(value) {
|
|
58
|
+
if (value === void 0) {
|
|
59
|
+
return void 0;
|
|
60
|
+
}
|
|
61
|
+
return value instanceof ToolSet ? value.toArray() : value;
|
|
62
|
+
}
|
|
63
|
+
function validateToolFilterList(field, list) {
|
|
64
|
+
if (!list) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
for (const entry of list) {
|
|
68
|
+
if (entry === "*") {
|
|
69
|
+
throw new Error(
|
|
70
|
+
`Invalid ${field} entry '*': there is no bare wildcard. Use one or more of \`new ToolSet().addBuiltIn('*')\`, \`.addMcp('*')\`, or \`.addCustom('*')\` to target a specific source.`
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
56
75
|
function extractTransformCallbacks(systemMessage) {
|
|
57
76
|
if (!systemMessage || systemMessage.mode !== "customize" || !systemMessage.sections) {
|
|
58
77
|
return { wirePayload: systemMessage, transformCallbacks: void 0 };
|
|
@@ -235,8 +254,19 @@ class CopilotClient {
|
|
|
235
254
|
telemetry: options.telemetry,
|
|
236
255
|
baseDirectory: options.baseDirectory,
|
|
237
256
|
sessionIdleTimeoutSeconds: options.sessionIdleTimeoutSeconds ?? 0,
|
|
238
|
-
enableRemoteSessions: options.enableRemoteSessions ?? false
|
|
257
|
+
enableRemoteSessions: options.enableRemoteSessions ?? false,
|
|
258
|
+
mode: options.mode ?? "copilot-cli"
|
|
239
259
|
};
|
|
260
|
+
if (this.options.mode === "empty") {
|
|
261
|
+
const hasPersistence = this.options.baseDirectory !== void 0 || this.sessionFsConfig !== null || // External runtimes manage their own persistence layer; the SDK
|
|
262
|
+
// can't enforce it from here.
|
|
263
|
+
conn.kind === "uri" || conn.kind === "parent-process";
|
|
264
|
+
if (!hasPersistence) {
|
|
265
|
+
throw new Error(
|
|
266
|
+
"CopilotClient was created with mode: 'empty' but neither 'baseDirectory' nor 'sessionFs' was set. Empty mode requires an explicit per-session persistence location; pick one."
|
|
267
|
+
);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
240
270
|
}
|
|
241
271
|
connectionExtraArgs = [];
|
|
242
272
|
/**
|
|
@@ -546,10 +576,122 @@ class CopilotClient {
|
|
|
546
576
|
* });
|
|
547
577
|
* ```
|
|
548
578
|
*/
|
|
579
|
+
/**
|
|
580
|
+
* Normalizes session-level tool filter options. Converts {@link ToolSet}
|
|
581
|
+
* instances to plain string arrays, rejects misuse (bare `"*"`) and the
|
|
582
|
+
* missing-availableTools case in `mode = "empty"`.
|
|
583
|
+
*
|
|
584
|
+
* The SDK always sends `toolFilterPrecedence: "excluded"` so callers can
|
|
585
|
+
* compose include + exclude lists naturally (e.g. "everything matching X
|
|
586
|
+
* except Y") regardless of mode. Allowlist-precedence is intentionally not
|
|
587
|
+
* exposed — it's available on the runtime side as a CLI-only concession to
|
|
588
|
+
* legacy behavior, but SDK consumers always get the composable semantics.
|
|
589
|
+
*
|
|
590
|
+
* @internal
|
|
591
|
+
*/
|
|
592
|
+
resolveToolFilterOptions(config) {
|
|
593
|
+
const availableTools = toolFilterListToArray(config.availableTools);
|
|
594
|
+
const excludedTools = toolFilterListToArray(config.excludedTools);
|
|
595
|
+
validateToolFilterList("availableTools", availableTools);
|
|
596
|
+
validateToolFilterList("excludedTools", excludedTools);
|
|
597
|
+
if (this.options.mode === "empty") {
|
|
598
|
+
if (availableTools === void 0) {
|
|
599
|
+
throw new Error(
|
|
600
|
+
"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)`."
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
return { availableTools, excludedTools, toolFilterPrecedence: "excluded" };
|
|
605
|
+
}
|
|
606
|
+
/** Mode-specific defaults spread under the caller's config (app values win). */
|
|
607
|
+
configDefaultsForMode() {
|
|
608
|
+
if (this.options.mode === "empty") {
|
|
609
|
+
return { enableSessionTelemetry: false };
|
|
610
|
+
}
|
|
611
|
+
return {};
|
|
612
|
+
}
|
|
613
|
+
/**
|
|
614
|
+
* Returns the systemMessage config to use, adjusted for the current mode.
|
|
615
|
+
* In empty mode we ensure the environment_context section is removed
|
|
616
|
+
* unless the app has already taken control of it. `append` (and
|
|
617
|
+
* unspecified) mode is promoted to `customize` so we can also strip
|
|
618
|
+
* environment_context; the caller's `content` is preserved verbatim
|
|
619
|
+
* because the runtime appends it as additional instructions in both
|
|
620
|
+
* customize and append modes.
|
|
621
|
+
*/
|
|
622
|
+
getSystemMessageConfigForMode(supplied) {
|
|
623
|
+
if (this.options.mode !== "empty") return supplied;
|
|
624
|
+
if (!supplied) {
|
|
625
|
+
return {
|
|
626
|
+
mode: "customize",
|
|
627
|
+
sections: { environment_context: { action: "remove" } }
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
switch (supplied.mode) {
|
|
631
|
+
case "replace":
|
|
632
|
+
return supplied;
|
|
633
|
+
case "customize":
|
|
634
|
+
if (supplied.sections?.environment_context) return supplied;
|
|
635
|
+
return {
|
|
636
|
+
...supplied,
|
|
637
|
+
sections: {
|
|
638
|
+
...supplied.sections,
|
|
639
|
+
environment_context: { action: "remove" }
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
case "append":
|
|
643
|
+
case void 0:
|
|
644
|
+
return {
|
|
645
|
+
mode: "customize",
|
|
646
|
+
content: supplied.content,
|
|
647
|
+
sections: { environment_context: { action: "remove" } }
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
/**
|
|
652
|
+
* Mode-specific options applied via session.options.update after create/resume.
|
|
653
|
+
*
|
|
654
|
+
* In empty mode, defaults the four overridable feature flags to safe values
|
|
655
|
+
* (caller values from `config` win). `installedPlugins=[]` is unconditional
|
|
656
|
+
* in empty mode — apps that need custom plugins should switch modes.
|
|
657
|
+
*/
|
|
658
|
+
async updateSessionOptionsForMode(session, config) {
|
|
659
|
+
const patch = {};
|
|
660
|
+
if (this.options.mode === "empty") {
|
|
661
|
+
patch.skipCustomInstructions = config.skipCustomInstructions ?? true;
|
|
662
|
+
patch.customAgentsLocalOnly = config.customAgentsLocalOnly ?? true;
|
|
663
|
+
patch.coauthorEnabled = config.coauthorEnabled ?? false;
|
|
664
|
+
patch.manageScheduleEnabled = config.manageScheduleEnabled ?? false;
|
|
665
|
+
patch.installedPlugins = [];
|
|
666
|
+
} else {
|
|
667
|
+
if (config.skipCustomInstructions !== void 0)
|
|
668
|
+
patch.skipCustomInstructions = config.skipCustomInstructions;
|
|
669
|
+
if (config.customAgentsLocalOnly !== void 0)
|
|
670
|
+
patch.customAgentsLocalOnly = config.customAgentsLocalOnly;
|
|
671
|
+
if (config.coauthorEnabled !== void 0)
|
|
672
|
+
patch.coauthorEnabled = config.coauthorEnabled;
|
|
673
|
+
if (config.manageScheduleEnabled !== void 0)
|
|
674
|
+
patch.manageScheduleEnabled = config.manageScheduleEnabled;
|
|
675
|
+
}
|
|
676
|
+
if (Object.keys(patch).length === 0) {
|
|
677
|
+
return;
|
|
678
|
+
}
|
|
679
|
+
try {
|
|
680
|
+
await session.rpc.options.update(patch);
|
|
681
|
+
} catch (e) {
|
|
682
|
+
try {
|
|
683
|
+
await session.disconnect();
|
|
684
|
+
} catch {
|
|
685
|
+
}
|
|
686
|
+
throw e;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
549
689
|
async createSession(config) {
|
|
550
690
|
if (!this.connection) {
|
|
551
691
|
await this.start();
|
|
552
692
|
}
|
|
693
|
+
config = { ...this.configDefaultsForMode(), ...config };
|
|
694
|
+
config.systemMessage = this.getSystemMessageConfigForMode(config.systemMessage);
|
|
553
695
|
const sessionId = config.sessionId ?? randomUUID();
|
|
554
696
|
const session = new CopilotSession(
|
|
555
697
|
sessionId,
|
|
@@ -587,6 +729,7 @@ class CopilotClient {
|
|
|
587
729
|
}
|
|
588
730
|
this.sessions.set(sessionId, session);
|
|
589
731
|
this.setupSessionFs(session, config);
|
|
732
|
+
const toolFilterOptions = this.resolveToolFilterOptions(config);
|
|
590
733
|
try {
|
|
591
734
|
const response = await this.connection.sendRequest("session.create", {
|
|
592
735
|
...await getTraceContext(this.onGetTraceContext),
|
|
@@ -610,8 +753,9 @@ class CopilotClient {
|
|
|
610
753
|
description: cmd.description
|
|
611
754
|
})),
|
|
612
755
|
systemMessage: wireSystemMessage,
|
|
613
|
-
availableTools:
|
|
614
|
-
excludedTools:
|
|
756
|
+
availableTools: toolFilterOptions.availableTools,
|
|
757
|
+
excludedTools: toolFilterOptions.excludedTools,
|
|
758
|
+
toolFilterPrecedence: toolFilterOptions.toolFilterPrecedence,
|
|
615
759
|
provider: config.provider,
|
|
616
760
|
enableSessionTelemetry: config.enableSessionTelemetry,
|
|
617
761
|
modelCapabilities: config.modelCapabilities,
|
|
@@ -642,6 +786,7 @@ class CopilotClient {
|
|
|
642
786
|
const { workspacePath, capabilities } = response;
|
|
643
787
|
session["_workspacePath"] = workspacePath;
|
|
644
788
|
session.setCapabilities(capabilities);
|
|
789
|
+
await this.updateSessionOptionsForMode(session, config);
|
|
645
790
|
} catch (e) {
|
|
646
791
|
this.sessions.delete(sessionId);
|
|
647
792
|
throw e;
|
|
@@ -701,6 +846,8 @@ class CopilotClient {
|
|
|
701
846
|
if (config.hooks) {
|
|
702
847
|
session.registerHooks(config.hooks);
|
|
703
848
|
}
|
|
849
|
+
config = { ...this.configDefaultsForMode(), ...config };
|
|
850
|
+
config.systemMessage = this.getSystemMessageConfigForMode(config.systemMessage);
|
|
704
851
|
const { wirePayload: wireSystemMessage, transformCallbacks } = extractTransformCallbacks(
|
|
705
852
|
config.systemMessage
|
|
706
853
|
);
|
|
@@ -712,6 +859,7 @@ class CopilotClient {
|
|
|
712
859
|
}
|
|
713
860
|
this.sessions.set(sessionId, session);
|
|
714
861
|
this.setupSessionFs(session, config);
|
|
862
|
+
const toolFilterOptions = this.resolveToolFilterOptions(config);
|
|
715
863
|
try {
|
|
716
864
|
const response = await this.connection.sendRequest("session.resume", {
|
|
717
865
|
...await getTraceContext(this.onGetTraceContext),
|
|
@@ -720,8 +868,9 @@ class CopilotClient {
|
|
|
720
868
|
model: config.model,
|
|
721
869
|
reasoningEffort: config.reasoningEffort,
|
|
722
870
|
systemMessage: wireSystemMessage,
|
|
723
|
-
availableTools:
|
|
724
|
-
excludedTools:
|
|
871
|
+
availableTools: toolFilterOptions.availableTools,
|
|
872
|
+
excludedTools: toolFilterOptions.excludedTools,
|
|
873
|
+
toolFilterPrecedence: toolFilterOptions.toolFilterPrecedence,
|
|
725
874
|
enableSessionTelemetry: config.enableSessionTelemetry,
|
|
726
875
|
tools: config.tools?.map((tool) => ({
|
|
727
876
|
name: tool.name,
|
|
@@ -770,6 +919,7 @@ class CopilotClient {
|
|
|
770
919
|
session["_workspacePath"] = workspacePath;
|
|
771
920
|
session.setCapabilities(capabilities);
|
|
772
921
|
session.setOpenCanvases(openCanvases ?? []);
|
|
922
|
+
await this.updateSessionOptionsForMode(session, config);
|
|
773
923
|
} catch (e) {
|
|
774
924
|
this.sessions.delete(sessionId);
|
|
775
925
|
throw e;
|
|
@@ -1148,6 +1298,9 @@ class CopilotClient {
|
|
|
1148
1298
|
if (this.options.baseDirectory) {
|
|
1149
1299
|
envWithoutNodeDebug.COPILOT_HOME = this.options.baseDirectory;
|
|
1150
1300
|
}
|
|
1301
|
+
if (this.options.mode === "empty") {
|
|
1302
|
+
envWithoutNodeDebug.COPILOT_DISABLE_KEYTAR = "1";
|
|
1303
|
+
}
|
|
1151
1304
|
if (!this.resolvedCliPath) {
|
|
1152
1305
|
throw new Error(
|
|
1153
1306
|
"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."
|