mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
feat(providers): expose custom agent selection (#1700)
This commit is contained in:
@@ -10,6 +10,8 @@ Extend `ACPAgentClient` from `packages/server/src/server/agent/providers/acp-age
|
||||
|
||||
The only built-in ACP provider today is `copilot` (`copilot-acp-agent.ts`). `GenericACPAgentClient` (`generic-acp-agent.ts`) is also ACP-based but is used for user-defined custom providers configured via `extends: "acp"` overrides — see [docs/custom-providers.md](custom-providers.md).
|
||||
|
||||
Copilot custom agents are exposed through ACP session config, not the slash-command list. When custom agents are available, Copilot returns a select config option with `id: "agent"` and `category: "_agent"`; Paseo maps that to the `agent` provider feature. Copilot uses the agent display name as the option value, and the blank value means the default Copilot agent.
|
||||
|
||||
### Direct
|
||||
|
||||
Implement the `AgentClient` and `AgentSession` interfaces from `agent-sdk-types.ts` yourself. This gives full control but requires you to handle process management, streaming, permissions, and session persistence from scratch.
|
||||
|
||||
@@ -30,6 +30,7 @@ import {
|
||||
} from "./acp-agent.js";
|
||||
import type { ProcessTerminator, TreeKillTarget } from "../../../utils/tree-kill.js";
|
||||
import {
|
||||
COPILOT_AGENT_FEATURE_OPTION,
|
||||
COPILOT_ALLOW_ALL_MODE_ID,
|
||||
COPILOT_MODES,
|
||||
CopilotACPAgentClient,
|
||||
@@ -205,12 +206,16 @@ function selectConfigOption(
|
||||
};
|
||||
}
|
||||
|
||||
function createCopilotSessionWithConfig(modeId?: string | null): ACPAgentSession {
|
||||
function createCopilotSessionWithConfig(
|
||||
modeId?: string | null,
|
||||
featureValues?: Record<string, unknown>,
|
||||
): ACPAgentSession {
|
||||
return new ACPAgentSession(
|
||||
{
|
||||
provider: "copilot",
|
||||
cwd: "/tmp/paseo-acp-test",
|
||||
modeId: modeId ?? undefined,
|
||||
...(featureValues ? { featureValues } : {}),
|
||||
},
|
||||
{
|
||||
provider: "copilot",
|
||||
@@ -219,6 +224,7 @@ function createCopilotSessionWithConfig(modeId?: string | null): ACPAgentSession
|
||||
defaultModes: COPILOT_MODES,
|
||||
sessionResponseTransformer: transformCopilotSessionResponse,
|
||||
configOptionsTransformer: transformCopilotConfigOptions,
|
||||
configFeatureOptions: [COPILOT_AGENT_FEATURE_OPTION],
|
||||
modeIdTransformer: transformCopilotModeId,
|
||||
providerModeWriter: writeCopilotProviderMode,
|
||||
beforeModeWriter: beforeCopilotModeWriter,
|
||||
@@ -272,6 +278,27 @@ function copilotAllowAllConfigOption(currentValue: "on" | "off"): SessionConfigO
|
||||
};
|
||||
}
|
||||
|
||||
function copilotAgentConfigOption(currentValue: string): SessionConfigOption {
|
||||
return {
|
||||
id: "agent",
|
||||
name: "Agent",
|
||||
category: "_agent",
|
||||
type: "select",
|
||||
currentValue,
|
||||
options: [
|
||||
{
|
||||
value: "",
|
||||
name: "",
|
||||
},
|
||||
{
|
||||
value: "Probe Agent",
|
||||
name: "Probe Agent",
|
||||
description: "Temporary probe agent",
|
||||
},
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
function selectConfigOptionName(category: "mode" | "model" | "thought_level"): string {
|
||||
if (category === "mode") {
|
||||
return "Mode";
|
||||
@@ -1137,6 +1164,90 @@ describe("ACPAgentSession Zed parity", () => {
|
||||
]);
|
||||
await expect(session.getCurrentMode()).resolves.toBe(COPILOT_ALLOW_ALL_MODE_ID);
|
||||
});
|
||||
|
||||
test("exposes Copilot custom agents as a select feature", () => {
|
||||
const session = createCopilotSessionWithConfig();
|
||||
const internals = asInternals<ACPSessionInternals>(session);
|
||||
internals.configOptions = [copilotAgentConfigOption("")];
|
||||
|
||||
expect(session.features).toEqual([
|
||||
{
|
||||
type: "select",
|
||||
id: "agent",
|
||||
label: "Agent",
|
||||
description: "Use a Copilot custom agent profile",
|
||||
tooltip: "Select Copilot agent",
|
||||
icon: undefined,
|
||||
value: "",
|
||||
options: [
|
||||
{
|
||||
id: "",
|
||||
label: "Default",
|
||||
description: undefined,
|
||||
isDefault: true,
|
||||
metadata: undefined,
|
||||
},
|
||||
{
|
||||
id: "Probe Agent",
|
||||
label: "Probe Agent",
|
||||
description: "Temporary probe agent",
|
||||
isDefault: false,
|
||||
metadata: undefined,
|
||||
},
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
test("applies configured Copilot custom agent before the first turn", async () => {
|
||||
const setSessionConfigOption = vi.fn(async () => ({
|
||||
configOptions: [copilotAgentConfigOption("Probe Agent")],
|
||||
}));
|
||||
const session = createCopilotSessionWithConfig(null, { agent: "Probe Agent" });
|
||||
const { internals } = prepareConfiguredOverrideSession(session, {
|
||||
configOptions: [copilotAgentConfigOption("")],
|
||||
connection: { setSessionConfigOption },
|
||||
});
|
||||
|
||||
await internals.applyConfiguredOverrides();
|
||||
|
||||
expect(setSessionConfigOption).toHaveBeenCalledWith({
|
||||
sessionId: "session-1",
|
||||
configId: "agent",
|
||||
value: "Probe Agent",
|
||||
});
|
||||
expect(session.features).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "agent",
|
||||
value: "Probe Agent",
|
||||
}),
|
||||
]);
|
||||
});
|
||||
|
||||
test("sets Copilot custom agent through ACP config options", async () => {
|
||||
const setSessionConfigOption = vi.fn(async () => ({
|
||||
configOptions: [copilotAgentConfigOption("Probe Agent")],
|
||||
}));
|
||||
const session = createCopilotSessionWithConfig();
|
||||
prepareConfiguredOverrideSession(session, {
|
||||
configOptions: [copilotAgentConfigOption("")],
|
||||
connection: { setSessionConfigOption },
|
||||
});
|
||||
|
||||
await session.setFeature("agent", "Probe Agent");
|
||||
|
||||
expect(setSessionConfigOption).toHaveBeenCalledWith({
|
||||
sessionId: "session-1",
|
||||
configId: "agent",
|
||||
value: "Probe Agent",
|
||||
});
|
||||
expect(session.features).toEqual([
|
||||
expect.objectContaining({
|
||||
id: "agent",
|
||||
value: "Probe Agent",
|
||||
}),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("deriveModelDefinitionsFromACP", () => {
|
||||
@@ -1285,6 +1396,51 @@ describe("ACPAgentClient modelTransformer", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("ACPAgentClient config features", () => {
|
||||
test("derives features from configured ACP select options", async () => {
|
||||
class TestACPAgentClient extends ACPAgentClient {
|
||||
protected override async spawnProcess(): Promise<SpawnedACPProcess> {
|
||||
return {
|
||||
child: { kill: vi.fn(), exitCode: 0, signalCode: null, once: vi.fn() },
|
||||
connection: {
|
||||
newSession: vi.fn().mockResolvedValue({
|
||||
sessionId: "session-1",
|
||||
configOptions: [copilotAgentConfigOption("Probe Agent")],
|
||||
}),
|
||||
},
|
||||
initialize: { agentCapabilities: {} },
|
||||
} as SpawnedACPProcess;
|
||||
}
|
||||
|
||||
protected override async closeProbe(): Promise<void> {}
|
||||
}
|
||||
|
||||
const client = new TestACPAgentClient({
|
||||
provider: "copilot",
|
||||
logger: createTestLogger(),
|
||||
defaultCommand: ["copilot", "--acp"],
|
||||
configFeatureOptions: [COPILOT_AGENT_FEATURE_OPTION],
|
||||
});
|
||||
|
||||
await expect(
|
||||
client.listFeatures({
|
||||
provider: "copilot",
|
||||
cwd: "/tmp/acp-features",
|
||||
}),
|
||||
).resolves.toEqual([
|
||||
expect.objectContaining({
|
||||
type: "select",
|
||||
id: "agent",
|
||||
value: "Probe Agent",
|
||||
options: [
|
||||
expect.objectContaining({ id: "", label: "Default", isDefault: false }),
|
||||
expect.objectContaining({ id: "Probe Agent", label: "Probe Agent", isDefault: true }),
|
||||
],
|
||||
}),
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("ACPAgentClient sessionResponseTransformer", () => {
|
||||
class TestACPAgentClient extends ACPAgentClient {
|
||||
protected override async spawnProcess(): Promise<SpawnedACPProcess> {
|
||||
|
||||
@@ -62,6 +62,7 @@ import {
|
||||
getAgentStreamEventTurnId,
|
||||
type AgentCapabilityFlags,
|
||||
type AgentClient,
|
||||
type AgentFeature,
|
||||
type AgentLaunchContext,
|
||||
type AgentMetadata,
|
||||
type AgentMode,
|
||||
@@ -318,6 +319,7 @@ interface ACPAgentClientOptions {
|
||||
modelTransformer?: (models: AgentModelDefinition[]) => AgentModelDefinition[];
|
||||
sessionResponseTransformer?: (response: SessionStateResponse) => SessionStateResponse;
|
||||
configOptionsTransformer?: (configOptions: SessionConfigOption[]) => SessionConfigOption[];
|
||||
configFeatureOptions?: ACPConfigFeatureOption[];
|
||||
modeIdTransformer?: (modeId: string) => string | null;
|
||||
toolSnapshotTransformer?: (snapshot: ACPToolSnapshot) => ACPToolSnapshot;
|
||||
providerModeWriter?: (
|
||||
@@ -344,6 +346,7 @@ interface ACPAgentSessionOptions {
|
||||
modelTransformer?: (models: AgentModelDefinition[]) => AgentModelDefinition[];
|
||||
sessionResponseTransformer?: (response: SessionStateResponse) => SessionStateResponse;
|
||||
configOptionsTransformer?: (configOptions: SessionConfigOption[]) => SessionConfigOption[];
|
||||
configFeatureOptions?: ACPConfigFeatureOption[];
|
||||
modeIdTransformer?: (modeId: string) => string | null;
|
||||
toolSnapshotTransformer?: (snapshot: ACPToolSnapshot) => ACPToolSnapshot;
|
||||
providerModeWriter?: (
|
||||
@@ -426,6 +429,17 @@ interface ConfigOptionSelector {
|
||||
metadata?: AgentMetadata;
|
||||
}
|
||||
|
||||
export interface ACPConfigFeatureOption {
|
||||
id: string;
|
||||
configId: string;
|
||||
category: string;
|
||||
label: string;
|
||||
description?: string;
|
||||
tooltip?: string;
|
||||
icon?: string;
|
||||
emptyOptionLabel?: string;
|
||||
}
|
||||
|
||||
type SelectConfigOption = Extract<SessionConfigOption, { type: "select" }>;
|
||||
interface SelectConfigChoice {
|
||||
value: string;
|
||||
@@ -585,6 +599,31 @@ export function deriveModelDefinitionsFromACP(
|
||||
}));
|
||||
}
|
||||
|
||||
export function deriveFeaturesFromACP(
|
||||
configOptions: SessionConfigOption[] | null | undefined,
|
||||
featureOptions: ACPConfigFeatureOption[],
|
||||
): AgentFeature[] {
|
||||
return featureOptions.flatMap((featureOption) => {
|
||||
const option = findSelectConfigFeatureOption(configOptions, featureOption);
|
||||
if (!option) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return [
|
||||
{
|
||||
type: "select",
|
||||
id: featureOption.id,
|
||||
label: featureOption.label,
|
||||
description: featureOption.description,
|
||||
tooltip: featureOption.tooltip,
|
||||
icon: featureOption.icon,
|
||||
value: option.currentValue ?? null,
|
||||
options: deriveConfigFeatureSelectOptions(option, featureOption),
|
||||
},
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
export class ACPAgentClient implements AgentClient {
|
||||
readonly provider: string;
|
||||
readonly capabilities: AgentCapabilityFlags;
|
||||
@@ -600,6 +639,7 @@ export class ACPAgentClient implements AgentClient {
|
||||
private readonly configOptionsTransformer?: (
|
||||
configOptions: SessionConfigOption[],
|
||||
) => SessionConfigOption[];
|
||||
private readonly configFeatureOptions: ACPConfigFeatureOption[];
|
||||
private readonly modeIdTransformer?: (modeId: string) => string | null;
|
||||
private readonly toolSnapshotTransformer?: (snapshot: ACPToolSnapshot) => ACPToolSnapshot;
|
||||
private readonly providerModeWriter?: (
|
||||
@@ -631,6 +671,7 @@ export class ACPAgentClient implements AgentClient {
|
||||
this.modelTransformer = options.modelTransformer;
|
||||
this.sessionResponseTransformer = options.sessionResponseTransformer;
|
||||
this.configOptionsTransformer = options.configOptionsTransformer;
|
||||
this.configFeatureOptions = options.configFeatureOptions ?? [];
|
||||
this.modeIdTransformer = options.modeIdTransformer;
|
||||
this.toolSnapshotTransformer = options.toolSnapshotTransformer;
|
||||
this.providerModeWriter = options.providerModeWriter;
|
||||
@@ -656,6 +697,7 @@ export class ACPAgentClient implements AgentClient {
|
||||
modelTransformer: this.modelTransformer,
|
||||
sessionResponseTransformer: this.sessionResponseTransformer,
|
||||
configOptionsTransformer: this.configOptionsTransformer,
|
||||
configFeatureOptions: this.configFeatureOptions,
|
||||
modeIdTransformer: this.modeIdTransformer,
|
||||
toolSnapshotTransformer: this.toolSnapshotTransformer,
|
||||
providerModeWriter: this.providerModeWriter,
|
||||
@@ -702,6 +744,7 @@ export class ACPAgentClient implements AgentClient {
|
||||
modelTransformer: this.modelTransformer,
|
||||
sessionResponseTransformer: this.sessionResponseTransformer,
|
||||
configOptionsTransformer: this.configOptionsTransformer,
|
||||
configFeatureOptions: this.configFeatureOptions,
|
||||
modeIdTransformer: this.modeIdTransformer,
|
||||
toolSnapshotTransformer: this.toolSnapshotTransformer,
|
||||
providerModeWriter: this.providerModeWriter,
|
||||
@@ -748,6 +791,27 @@ export class ACPAgentClient implements AgentClient {
|
||||
}
|
||||
}
|
||||
|
||||
async listFeatures(config: AgentSessionConfig): Promise<AgentFeature[]> {
|
||||
if (this.configFeatureOptions.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
this.assertProvider(config);
|
||||
const probe = await this.spawnProcess(PROBE_ENV);
|
||||
try {
|
||||
const response = await this.runACPRequest(() =>
|
||||
probe.connection.newSession({
|
||||
cwd: config.cwd,
|
||||
mcpServers: [],
|
||||
}),
|
||||
);
|
||||
const transformed = this.transformSessionResponse(response);
|
||||
return deriveFeaturesFromACP(transformed.configOptions, this.configFeatureOptions);
|
||||
} finally {
|
||||
await this.closeProbe(probe);
|
||||
}
|
||||
}
|
||||
|
||||
async listImportableSessions(
|
||||
options?: ListImportableSessionsOptions,
|
||||
): Promise<ImportableProviderSession[]> {
|
||||
@@ -965,6 +1029,7 @@ export class ACPAgentSession implements AgentSession, ACPClient {
|
||||
private readonly configOptionsTransformer?: (
|
||||
configOptions: SessionConfigOption[],
|
||||
) => SessionConfigOption[];
|
||||
private readonly configFeatureOptions: ACPConfigFeatureOption[];
|
||||
private readonly modeIdTransformer?: (modeId: string) => string | null;
|
||||
private readonly toolSnapshotTransformer?: (snapshot: ACPToolSnapshot) => ACPToolSnapshot;
|
||||
private readonly providerModeWriter?: (
|
||||
@@ -1027,6 +1092,7 @@ export class ACPAgentSession implements AgentSession, ACPClient {
|
||||
this.modelTransformer = options.modelTransformer;
|
||||
this.sessionResponseTransformer = options.sessionResponseTransformer;
|
||||
this.configOptionsTransformer = options.configOptionsTransformer;
|
||||
this.configFeatureOptions = options.configFeatureOptions ?? [];
|
||||
this.modeIdTransformer = options.modeIdTransformer;
|
||||
this.toolSnapshotTransformer = options.toolSnapshotTransformer;
|
||||
this.providerModeWriter = options.providerModeWriter;
|
||||
@@ -1211,6 +1277,10 @@ export class ACPAgentSession implements AgentSession, ACPClient {
|
||||
return this.currentMode;
|
||||
}
|
||||
|
||||
get features(): AgentFeature[] {
|
||||
return deriveFeaturesFromACP(this.configOptions, this.configFeatureOptions);
|
||||
}
|
||||
|
||||
private ensureCommandsReadyDeferred(): void {
|
||||
if (this.commandsReadyDeferred || this.commandsReadySettled || this.cachedCommands.length > 0) {
|
||||
return;
|
||||
@@ -1543,6 +1613,44 @@ export class ACPAgentSession implements AgentSession, ACPClient {
|
||||
});
|
||||
}
|
||||
|
||||
async setFeature(featureId: string, value: unknown): Promise<void> {
|
||||
if (!this.connection || !this.sessionId) {
|
||||
throw new Error("ACP session not initialized");
|
||||
}
|
||||
|
||||
const featureOption = this.configFeatureOptions.find((option) => option.id === featureId);
|
||||
if (!featureOption) {
|
||||
throw new Error(`Unknown ${this.provider} feature: ${featureId}`);
|
||||
}
|
||||
|
||||
const option = findSelectConfigFeatureOption(this.configOptions, featureOption);
|
||||
if (!option) {
|
||||
throw new Error(`${this.provider} does not expose ACP feature '${featureId}'`);
|
||||
}
|
||||
|
||||
const requestedValue = normalizeConfigFeatureValue(value);
|
||||
const choice = findSelectConfigChoice({ option, value: requestedValue });
|
||||
if (!choice) {
|
||||
throw new Error(
|
||||
`${this.provider} feature '${featureId}' does not include option '${requestedValue}'`,
|
||||
);
|
||||
}
|
||||
|
||||
const response = await this.connection.setSessionConfigOption({
|
||||
sessionId: this.sessionId,
|
||||
configId: option.id,
|
||||
value: requestedValue,
|
||||
});
|
||||
const currentValue = this.applyConfigOptionResponse({
|
||||
response,
|
||||
configId: option.id,
|
||||
category: featureOption.category,
|
||||
requestedValue,
|
||||
label: featureOption.label,
|
||||
});
|
||||
this.config.featureValues = { ...this.config.featureValues, [featureId]: currentValue };
|
||||
}
|
||||
|
||||
private applyConfigOptionResponse({
|
||||
response,
|
||||
configId,
|
||||
@@ -2003,6 +2111,13 @@ export class ACPAgentSession implements AgentSession, ACPClient {
|
||||
if (this.config.thinkingOptionId && this.config.thinkingOptionId !== this.thinkingOptionId) {
|
||||
await this.setThinkingOption(this.config.thinkingOptionId);
|
||||
}
|
||||
const configuredFeatureValues = this.config.featureValues ?? {};
|
||||
for (const featureOption of this.configFeatureOptions) {
|
||||
if (!Object.prototype.hasOwnProperty.call(configuredFeatureValues, featureOption.id)) {
|
||||
continue;
|
||||
}
|
||||
await this.setFeature(featureOption.id, configuredFeatureValues[featureOption.id]);
|
||||
}
|
||||
}
|
||||
|
||||
private warnInvalidSelection(value: string, message: string): void {
|
||||
@@ -2362,6 +2477,19 @@ function findSelectConfigOption({
|
||||
return option ?? null;
|
||||
}
|
||||
|
||||
function findSelectConfigFeatureOption(
|
||||
configOptions: SessionConfigOption[] | null | undefined,
|
||||
featureOption: ACPConfigFeatureOption,
|
||||
): SelectConfigOption | null {
|
||||
const option = configOptions?.find(
|
||||
(entry): entry is SelectConfigOption =>
|
||||
entry.type === "select" &&
|
||||
entry.id === featureOption.configId &&
|
||||
entry.category === featureOption.category,
|
||||
);
|
||||
return option ?? null;
|
||||
}
|
||||
|
||||
function findSelectConfigChoice({
|
||||
option,
|
||||
value,
|
||||
@@ -2389,6 +2517,43 @@ function flattenSelectOptions(options: SelectConfigOption["options"]): SelectCon
|
||||
return flattened;
|
||||
}
|
||||
|
||||
function deriveConfigFeatureSelectOptions(
|
||||
option: SelectConfigOption,
|
||||
featureOption: ACPConfigFeatureOption,
|
||||
): ConfigOptionSelector[] {
|
||||
return flattenSelectOptions(option.options).map((choice) => ({
|
||||
id: choice.value,
|
||||
label: normalizeConfigFeatureOptionLabel(choice, featureOption),
|
||||
description: choice.description ?? undefined,
|
||||
isDefault: choice.value === option.currentValue,
|
||||
metadata: choice.group ? { group: choice.group } : undefined,
|
||||
}));
|
||||
}
|
||||
|
||||
function normalizeConfigFeatureOptionLabel(
|
||||
choice: SelectConfigChoice,
|
||||
featureOption: ACPConfigFeatureOption,
|
||||
): string {
|
||||
const name = choice.name.trim();
|
||||
if (name) {
|
||||
return name;
|
||||
}
|
||||
if (choice.value === "" && featureOption.emptyOptionLabel) {
|
||||
return featureOption.emptyOptionLabel;
|
||||
}
|
||||
return choice.value;
|
||||
}
|
||||
|
||||
function normalizeConfigFeatureValue(value: unknown): string {
|
||||
if (typeof value === "string") {
|
||||
return value;
|
||||
}
|
||||
if (value === null) {
|
||||
return "";
|
||||
}
|
||||
throw new Error(`ACP feature value must be a string`);
|
||||
}
|
||||
|
||||
function deriveSelectorOptions(
|
||||
configOptions: SessionConfigOption[] | null | undefined,
|
||||
category: string,
|
||||
|
||||
@@ -9,6 +9,7 @@ import {
|
||||
} from "../provider-launch-config.js";
|
||||
import {
|
||||
ACPAgentClient,
|
||||
type ACPConfigFeatureOption,
|
||||
type ACPBeforeModeWriteResult,
|
||||
type ACPProviderModeWriteResult,
|
||||
type ACPProviderModeWriterContext,
|
||||
@@ -44,6 +45,16 @@ const COPILOT_ALLOW_ALL_ON = "on";
|
||||
const COPILOT_ALLOW_ALL_OFF = "off";
|
||||
type SelectConfigOption = Extract<SessionConfigOption, { type: "select" }>;
|
||||
|
||||
export const COPILOT_AGENT_FEATURE_OPTION: ACPConfigFeatureOption = {
|
||||
id: "agent",
|
||||
configId: "agent",
|
||||
category: "_agent",
|
||||
label: "Agent",
|
||||
description: "Use a Copilot custom agent profile",
|
||||
tooltip: "Select Copilot agent",
|
||||
emptyOptionLabel: "Default",
|
||||
};
|
||||
|
||||
export const COPILOT_MODES: AgentMode[] = [
|
||||
{
|
||||
id: COPILOT_AGENT_MODE_ID,
|
||||
@@ -77,6 +88,7 @@ export class CopilotACPAgentClient extends ACPAgentClient {
|
||||
defaultModes: COPILOT_MODES,
|
||||
sessionResponseTransformer: transformCopilotSessionResponse,
|
||||
configOptionsTransformer: transformCopilotConfigOptions,
|
||||
configFeatureOptions: [COPILOT_AGENT_FEATURE_OPTION],
|
||||
modeIdTransformer: transformCopilotModeId,
|
||||
providerModeWriter: writeCopilotProviderMode,
|
||||
beforeModeWriter: beforeCopilotModeWriter,
|
||||
|
||||
Reference in New Issue
Block a user