Use safer automatic approval modes by default (#2213)

* feat(providers): default to safer automatic approvals

* fix(providers): preserve defaults on older Codex versions

* test(providers): cover Claude automatic approval default

* fix(providers): preserve Claude defaults on cloud transports

* fix(providers): resolve defaults from launch capabilities
This commit is contained in:
Mohamed Boudra
2026-07-18 23:03:39 +02:00
committed by GitHub
parent 0cfb9b6b94
commit c9bcfa7638
13 changed files with 253 additions and 27 deletions
@@ -575,6 +575,25 @@ describe("resolveFormState", () => {
expect(resolved.modeId).toBe("workspace-write"); expect(resolved.modeId).toBe("workspace-write");
}); });
it("falls back when the provider cannot advertise its preferred default mode", () => {
const providerMap = makeProviderMap({
...TEST_CODEX_DEFINITION,
defaultModeId: "auto-review",
modes: TEST_CODEX_DEFINITION.modes,
});
const resolved = resolveFormState(
undefined,
{ provider: "codex" },
CODEX_MODELS,
INITIAL_USER_MODIFIED,
makeState({ provider: "codex" }).form,
providerMap,
);
expect(resolved.modeId).toBe("auto");
});
it("ignores disabled ready providers when resolving selectable defaults", () => { it("ignores disabled ready providers when resolving selectable defaults", () => {
const entries: ProviderSnapshotEntry[] = [ const entries: ProviderSnapshotEntry[] = [
{ {
@@ -171,7 +171,12 @@ function resolvePreferredModeId(input: {
const preferredModeId = normalizeSelectedModeId(input.preferredModeId); const preferredModeId = normalizeSelectedModeId(input.preferredModeId);
if (preferredModeId) return preferredModeId; if (preferredModeId) return preferredModeId;
return input.providerDef?.defaultModeId ?? input.providerDef?.modes[0]?.id ?? ""; const defaultModeId = input.providerDef?.defaultModeId;
const modes = input.providerDef?.modes ?? [];
if (defaultModeId && (modes.length === 0 || modes.some((mode) => mode.id === defaultModeId))) {
return defaultModeId;
}
return modes[0]?.id ?? "";
} }
export function mergeSelectedComposerPreferences(args: { export function mergeSelectedComposerPreferences(args: {
+2 -2
View File
@@ -185,7 +185,7 @@ export const AGENT_PROVIDER_DEFINITIONS: AgentProviderDefinition[] = [
id: "claude", id: "claude",
label: "Claude", label: "Claude",
description: "Anthropic's multi-tool assistant with MCP support, streaming, and deep reasoning", description: "Anthropic's multi-tool assistant with MCP support, streaming, and deep reasoning",
defaultModeId: "default", defaultModeId: "auto",
modes: CLAUDE_MODES, modes: CLAUDE_MODES,
voice: { voice: {
enabled: true, enabled: true,
@@ -197,7 +197,7 @@ export const AGENT_PROVIDER_DEFINITIONS: AgentProviderDefinition[] = [
id: "codex", id: "codex",
label: "Codex", label: "Codex",
description: "OpenAI's Codex workspace agent with sandbox controls and optional network access", description: "OpenAI's Codex workspace agent with sandbox controls and optional network access",
defaultModeId: "auto", defaultModeId: "auto-review",
modes: CODEX_MODES, modes: CODEX_MODES,
voice: { voice: {
enabled: true, enabled: true,
@@ -35,6 +35,7 @@ import type {
AgentStreamEvent, AgentStreamEvent,
AgentTimelineItem, AgentTimelineItem,
ImportProviderSessionInput, ImportProviderSessionInput,
ResolveAgentDefaultModeInput,
} from "./agent-sdk-types.js"; } from "./agent-sdk-types.js";
import type { PaseoToolCatalog } from "./tools/types.js"; import type { PaseoToolCatalog } from "./tools/types.js";
import type { ProviderDefinition } from "./provider-registry.js"; import type { ProviderDefinition } from "./provider-registry.js";
@@ -111,11 +112,15 @@ function expectArchivedAgentRecord(
} }
class TestAgentClient implements AgentClient { class TestAgentClient implements AgentClient {
readonly provider = "codex" as const; readonly provider: AgentProvider;
readonly capabilities = TEST_CAPABILITIES; readonly capabilities = TEST_CAPABILITIES;
readonly createdConfigs: AgentSessionConfig[] = []; readonly createdConfigs: AgentSessionConfig[] = [];
readonly resumeOverrides: Array<Partial<AgentSessionConfig> | undefined> = []; readonly resumeOverrides: Array<Partial<AgentSessionConfig> | undefined> = [];
constructor(provider: AgentProvider = "codex") {
this.provider = provider;
}
async isAvailable(): Promise<boolean> { async isAvailable(): Promise<boolean> {
return true; return true;
} }
@@ -129,18 +134,18 @@ class TestAgentClient implements AgentClient {
return { return {
models: [ models: [
{ {
provider: "codex", provider: this.provider,
id: "gpt-5.4", id: "gpt-5.4",
label: "GPT-5.4", label: "GPT-5.4",
isDefault: true, isDefault: true,
}, },
{ {
provider: "codex", provider: this.provider,
id: "gpt-5.4-mini", id: "gpt-5.4-mini",
label: "GPT-5.4 Mini", label: "GPT-5.4 Mini",
}, },
{ {
provider: "codex", provider: this.provider,
id: "gpt-5.2-codex", id: "gpt-5.2-codex",
label: "GPT-5.2 Codex", label: "GPT-5.2 Codex",
}, },
@@ -156,7 +161,7 @@ class TestAgentClient implements AgentClient {
): Promise<AgentSession> { ): Promise<AgentSession> {
this.resumeOverrides.push(config); this.resumeOverrides.push(config);
return new TestAgentSession({ return new TestAgentSession({
provider: "codex", provider: this.provider,
cwd: config?.cwd ?? process.cwd(), cwd: config?.cwd ?? process.cwd(),
daemonAppendSystemPrompt: config?.daemonAppendSystemPrompt, daemonAppendSystemPrompt: config?.daemonAppendSystemPrompt,
}); });
@@ -935,9 +940,43 @@ test("normalizeConfig injects the provider default model when omitted", async ()
); );
expect(snapshot.config.model).toBe("gpt-5.4"); expect(snapshot.config.model).toBe("gpt-5.4");
expect(snapshot.config.modeId).toBe("auto-review");
});
test("normalizeConfig injects Claude's automatic approval default when omitted", async () => {
const workdir = mkdtempSync(join(tmpdir(), "agent-manager-claude-default-test-"));
const manager = new AgentManager({
clients: { claude: new TestAgentClient("claude") },
logger,
});
const snapshot = await manager.createAgent({ provider: "claude", cwd: workdir }, undefined, {
workspaceId: undefined,
});
expect(snapshot.config.modeId).toBe("auto"); expect(snapshot.config.modeId).toBe("auto");
}); });
test("normalizeConfig uses a capability-aware provider mode default", async () => {
const workdir = mkdtempSync(join(tmpdir(), "agent-manager-mode-default-test-"));
class CapabilityAwareClient extends TestAgentClient {
override async resolveDefaultModeId(input: ResolveAgentDefaultModeInput): Promise<string> {
return input.env?.CLAUDE_CODE_USE_BEDROCK === "1" ? "default" : "auto";
}
}
const manager = new AgentManager({
clients: { codex: new CapabilityAwareClient() },
logger,
});
const snapshot = await manager.createAgent({ provider: "codex", cwd: workdir }, undefined, {
workspaceId: undefined,
env: { CLAUDE_CODE_USE_BEDROCK: "1" },
});
expect(snapshot.config.modeId).toBe("default");
});
test("createAgent forwards request env into the spawned provider process", async () => { test("createAgent forwards request env into the spawned provider process", async () => {
const workdir = mkdtempSync(join(tmpdir(), "agent-manager-env-test-")); const workdir = mkdtempSync(join(tmpdir(), "agent-manager-env-test-"));
const client = new EnvProbeAgentClient(); const client = new EnvProbeAgentClient();
@@ -997,7 +1036,7 @@ test("normalizeConfig strips legacy 'default' model id", async () => {
); );
expect(snapshot.config.model).toBe("gpt-5.4"); expect(snapshot.config.model).toBe("gpt-5.4");
expect(snapshot.config.modeId).toBe("auto"); expect(snapshot.config.modeId).toBe("auto-review");
}); });
test("listDraftCommands returns no commands without guessing a missing model", async () => { test("listDraftCommands returns no commands without guessing a missing model", async () => {
@@ -1094,7 +1133,7 @@ test("listDraftCommands uses explicit model config without default model fetchin
provider: "codex", provider: "codex",
cwd: workdir, cwd: workdir,
model: "gpt-5.4", model: "gpt-5.4",
modeId: "auto", modeId: "auto-review",
}, },
]); ]);
}); });
@@ -1194,7 +1233,7 @@ test("listDraftFeatures uses explicit model config without default model fetchin
provider: "codex", provider: "codex",
cwd: workdir, cwd: workdir,
model: "gpt-5.4", model: "gpt-5.4",
modeId: "auto", modeId: "auto-review",
}, },
]); ]);
}); });
@@ -1651,7 +1690,7 @@ test("createAgent passes daemon launch env through the provider launch context",
provider: "codex", provider: "codex",
cwd: workdir, cwd: workdir,
model: "gpt-5.4", model: "gpt-5.4",
modeId: "auto", modeId: "auto-review",
}); });
expect(client.lastLaunchContext).toEqual({ expect(client.lastLaunchContext).toEqual({
agentId: snapshot.id, agentId: snapshot.id,
@@ -2448,7 +2487,7 @@ test("resumeAgentFromPersistence keeps metadata config, applies overrides, and p
}); });
expect(client.lastResumeOverrides).toMatchObject({ expect(client.lastResumeOverrides).toMatchObject({
model: "gpt-5.4", model: "gpt-5.4",
modeId: "auto", modeId: "auto-review",
systemPrompt: "new prompt", systemPrompt: "new prompt",
mcpServers: { mcpServers: {
paseo: { paseo: {
@@ -117,6 +117,7 @@ interface PreparedSessionConfig {
interface NormalizeConfigOptions { interface NormalizeConfigOptions {
resolveDefaultModel?: boolean; resolveDefaultModel?: boolean;
env?: Record<string, string>;
} }
interface TimeoutOptions { interface TimeoutOptions {
@@ -1034,7 +1035,11 @@ export class AgentManager {
this.assertAcceptingAgentRegistrations(); this.assertAcceptingAgentRegistrations();
const resolvedAgentId = validateAgentId(agentId ?? this.idFactory(), "createAgent"); const resolvedAgentId = validateAgentId(agentId ?? this.idFactory(), "createAgent");
await this.deleteAgentState(resolvedAgentId); await this.deleteAgentState(resolvedAgentId);
const { storedConfig, launchConfig } = await this.prepareSessionConfig(config, resolvedAgentId); const { storedConfig, launchConfig } = await this.prepareSessionConfig(
config,
resolvedAgentId,
options?.env,
);
this.requireEnabledProvider(storedConfig.provider); this.requireEnabledProvider(storedConfig.provider);
const client = await this.requireAvailableClient({ const client = await this.requireAvailableClient({
provider: storedConfig.provider, provider: storedConfig.provider,
@@ -4075,17 +4080,27 @@ export class AgentManager {
} }
if (!normalized.modeId) { if (!normalized.modeId) {
try { normalized.modeId = await this.resolveDefaultModeId(normalized, options.env);
normalized.modeId =
getAgentProviderDefinition(normalized.provider).defaultModeId ?? undefined;
} catch {
// Unknown provider
}
} }
return normalized; return normalized;
} }
private async resolveDefaultModeId(
config: AgentSessionConfig,
env?: Record<string, string>,
): Promise<string | undefined> {
const providerDefault = await this.clients
.get(config.provider)
?.resolveDefaultModeId?.({ config, env });
if (providerDefault) return providerDefault;
try {
return getAgentProviderDefinition(config.provider).defaultModeId ?? undefined;
} catch {
return undefined;
}
}
private async resolveDefaultModelId(config: AgentSessionConfig): Promise<string | undefined> { private async resolveDefaultModelId(config: AgentSessionConfig): Promise<string | undefined> {
const client = this.clients.get(config.provider); const client = this.clients.get(config.provider);
if (!client) { if (!client) {
@@ -4107,8 +4122,9 @@ export class AgentManager {
private async prepareSessionConfig( private async prepareSessionConfig(
config: AgentSessionConfig, config: AgentSessionConfig,
agentId: string, agentId: string,
env?: Record<string, string>,
): Promise<PreparedSessionConfig> { ): Promise<PreparedSessionConfig> {
const storedConfig = await this.normalizeConfig(stripInternalPaseoMcpServer(config)); const storedConfig = await this.normalizeConfig(stripInternalPaseoMcpServer(config), { env });
const launchConfig = this.applyDaemonAppendSystemPrompt( const launchConfig = this.applyDaemonAppendSystemPrompt(
withRuntimePaseoMcpServer({ withRuntimePaseoMcpServer({
config: storedConfig, config: storedConfig,
@@ -668,6 +668,12 @@ export type FetchCatalogOptions =
export interface ProviderCatalog { export interface ProviderCatalog {
models: AgentModelDefinition[]; models: AgentModelDefinition[];
modes: AgentMode[]; modes: AgentMode[];
defaultModeId?: string | null;
}
export interface ResolveAgentDefaultModeInput {
config: AgentSessionConfig;
env?: Record<string, string>;
} }
export interface AgentClient { export interface AgentClient {
@@ -690,6 +696,7 @@ export interface AgentClient {
* The registry is responsible for merging configured model overrides. * The registry is responsible for merging configured model overrides.
*/ */
fetchCatalog(options: FetchCatalogOptions): Promise<ProviderCatalog>; fetchCatalog(options: FetchCatalogOptions): Promise<ProviderCatalog>;
resolveDefaultModeId?(input: ResolveAgentDefaultModeInput): Promise<string | undefined>;
resolveCreateConfig?(input: ResolveAgentCreateConfigInput): ResolveAgentCreateConfigResult; resolveCreateConfig?(input: ResolveAgentCreateConfigInput): ResolveAgentCreateConfigResult;
isCreateConfigUnattended?(input: AgentCreateConfigUnattendedInput): boolean; isCreateConfigUnattended?(input: AgentCreateConfigUnattendedInput): boolean;
listCommands?(config: AgentSessionConfig): Promise<AgentSlashCommand[]>; listCommands?(config: AgentSessionConfig): Promise<AgentSlashCommand[]>;
@@ -814,7 +814,7 @@ test("enabled: false keeps provider metadata in registry", () => {
id: "claude", id: "claude",
label: "Claude", label: "Claude",
description: "Anthropic's multi-tool assistant with MCP support, streaming, and deep reasoning", description: "Anthropic's multi-tool assistant with MCP support, streaming, and deep reasoning",
defaultModeId: "default", defaultModeId: "auto",
enabled: false, enabled: false,
}); });
expect(registry.claude.modes).toEqual( expect(registry.claude.modes).toEqual(
@@ -1487,6 +1487,31 @@ describe("fetchCatalog", () => {
expect(catalog.models.map((model) => model.id)).toEqual(["profile-model", "extra-model"]); expect(catalog.models.map((model) => model.id)).toEqual(["profile-model", "extra-model"]);
}); });
test("replacement models still resolve the provider's capability-aware default mode", async () => {
const resolveDefaultModeId = vi.fn(async () => "default");
const injectedClient = {
provider: "codex",
capabilities: {},
resolveDefaultModeId,
isAvailable: vi.fn(async () => true),
} satisfies Partial<AgentClient> as AgentClient;
const registry = buildProviderRegistry(logger, {
providerOverrides: {
codex: { models: [{ id: "profile-model", label: "Profile Model" }] },
},
});
const catalog = await registry.codex.fetchCatalog(
{ scope: "workspace", cwd: "/tmp/catalog", force: false },
injectedClient,
);
expect(catalog.defaultModeId).toBe("default");
expect(resolveDefaultModeId).toHaveBeenCalledWith({
config: { provider: "codex", cwd: "/tmp/catalog" },
});
});
test("additionalModels can override replacement model fields", async () => { test("additionalModels can override replacement model fields", async () => {
const registry = buildProviderRegistry(logger, { const registry = buildProviderRegistry(logger, {
providerOverrides: { providerOverrides: {
@@ -14,6 +14,7 @@ import type {
ProviderCatalog, ProviderCatalog,
ResolveAgentCreateConfigInput, ResolveAgentCreateConfigInput,
ResolveAgentCreateConfigResult, ResolveAgentCreateConfigResult,
ResolveAgentDefaultModeInput,
} from "./agent-sdk-types.js"; } from "./agent-sdk-types.js";
import { import {
isDefaultAgentCreateConfigUnattended, isDefaultAgentCreateConfigUnattended,
@@ -427,12 +428,20 @@ function wrapClientProvider(
fetchCatalog: async (options) => { fetchCatalog: async (options) => {
const catalog = await inner.fetchCatalog(options); const catalog = await inner.fetchCatalog(options);
return { return {
...catalog,
models: mergeModels(provider, profileModels, additionalModels, catalog.models, { models: mergeModels(provider, profileModels, additionalModels, catalog.models, {
profileModelsAreAdditive, profileModelsAreAdditive,
}), }),
modes: catalog.modes, modes: catalog.modes,
}; };
}, },
resolveDefaultModeId: inner.resolveDefaultModeId
? async ({ config, env }: ResolveAgentDefaultModeInput) =>
await inner.resolveDefaultModeId?.({
config: { ...config, provider: inner.provider },
env,
})
: undefined,
resolveCreateConfig: inner.resolveCreateConfig?.bind(inner), resolveCreateConfig: inner.resolveCreateConfig?.bind(inner),
isCreateConfigUnattended: inner.isCreateConfigUnattended?.bind(inner), isCreateConfigUnattended: inner.isCreateConfigUnattended?.bind(inner),
listFeatures: listFeatures listFeatures: listFeatures
@@ -516,17 +525,25 @@ function createRegistryEntry(
// the single catalog API; otherwise use static/empty modes with no runtime. // the single catalog API; otherwise use static/empty modes with no runtime.
const models = mergeModelAdditions(provider, replacementModels, resolved.additionalModels); const models = mergeModelAdditions(provider, replacementModels, resolved.additionalModels);
if (hasStaticModes) { if (hasStaticModes) {
const defaultModeId = await catalogClient.resolveDefaultModeId?.({
config: {
provider,
cwd: options.scope === "workspace" ? options.cwd : process.cwd(),
},
});
return { return {
models, models,
modes: decorateModes(resolved.definition.modes), modes: decorateModes(resolved.definition.modes),
defaultModeId,
}; };
} }
const catalog = await catalogClient.fetchCatalog(options); const catalog = await catalogClient.fetchCatalog(options);
return { models, modes: decorateModes(catalog.modes) }; return { ...catalog, models, modes: decorateModes(catalog.modes) };
} }
const catalog = await catalogClient.fetchCatalog(options); const catalog = await catalogClient.fetchCatalog(options);
return { return {
...catalog,
models: mergeModels( models: mergeModels(
provider, provider,
resolved.profileModels, resolved.profileModels,
@@ -117,9 +117,11 @@ describe("ProviderSnapshotManager public surface", () => {
try { try {
const snapshot = manager.getSnapshot("/tmp/project"); const snapshot = manager.getSnapshot("/tmp/project");
const claude = snapshot.find((entry) => entry.provider === "claude"); const claude = snapshot.find((entry) => entry.provider === "claude");
const codex = snapshot.find((entry) => entry.provider === "codex");
expect(claude?.status).toBe("loading"); expect(claude?.status).toBe("loading");
expect(claude?.label).toBe("Claude"); expect(claude?.label).toBe("Claude");
expect(claude?.defaultModeId).toBe("default"); expect(claude?.defaultModeId).toBe("auto");
expect(codex?.defaultModeId).toBe("auto-review");
} finally { } finally {
manager.destroy(); manager.destroy();
} }
@@ -220,6 +222,34 @@ describe("ProviderSnapshotManager public surface", () => {
} }
}); });
test("ready snapshots publish the catalog's capability-aware default mode", async () => {
const manager = new ProviderSnapshotManager({
logger: createTestLogger(),
extraClients: {
codex: createExtraClient("codex", {
isAvailable: async () => true,
fetchCatalog: async () => ({
models: [],
modes: [{ id: "default", label: "Default", description: "Ask before running tools" }],
defaultModeId: "default",
}),
}),
},
});
try {
const entry = await manager.getProvider({
cwd: "/tmp/project",
provider: "codex",
wait: true,
});
expect(entry).toMatchObject({ status: "ready", defaultModeId: "default" });
} finally {
manager.destroy();
}
});
test("explicit refresh re-probes only the requested warm provider", async () => { test("explicit refresh re-probes only the requested warm provider", async () => {
const cwd = "/tmp/project"; const cwd = "/tmp/project";
const isAvailableCodex = vi.fn(async () => true); const isAvailableCodex = vi.fn(async () => true);
@@ -799,6 +799,8 @@ export class ProviderSnapshotManager {
setEntry({ setEntry({
...base, ...base,
defaultModeId:
catalog.defaultModeId === undefined ? definition.defaultModeId : catalog.defaultModeId,
status: "ready", status: "ready",
enabled: true, enabled: true,
models: catalog.models, models: catalog.models,
@@ -218,6 +218,37 @@ test("rejects auto mode when Claude Code uses Bedrock", async () => {
} }
}); });
test.each([
{ env: {}, expected: "auto" },
{ env: { CLAUDE_CODE_USE_BEDROCK: "1" }, expected: "default" },
{ env: { CLAUDE_CODE_USE_VERTEX: "true" }, expected: "default" },
])("defaults to $expected for transport environment $env", async ({ env, expected }) => {
const client = new ClaudeAgentClient({
logger: createTestLogger(),
runtimeSettings: { env },
});
await expect(
client.resolveDefaultModeId({
config: { provider: "claude", cwd: process.cwd() },
}),
).resolves.toBe(expected);
});
test("launch environment participates in the Claude default mode", async () => {
const client = new ClaudeAgentClient({
logger: createTestLogger(),
runtimeSettings: { env: {} },
});
await expect(
client.resolveDefaultModeId({
config: { provider: "claude", cwd: process.cwd() },
env: { CLAUDE_CODE_USE_BEDROCK: "1" },
}),
).resolves.toBe("default");
});
test("allows launch env to disable inherited Bedrock transport for auto mode", async () => { test("allows launch env to disable inherited Bedrock transport for auto mode", async () => {
const previousBedrock = process.env.CLAUDE_CODE_USE_BEDROCK; const previousBedrock = process.env.CLAUDE_CODE_USE_BEDROCK;
process.env.CLAUDE_CODE_USE_BEDROCK = "1"; process.env.CLAUDE_CODE_USE_BEDROCK = "1";
@@ -91,6 +91,7 @@ import {
type ListImportableSessionsOptions, type ListImportableSessionsOptions,
type McpServerConfig, type McpServerConfig,
type ProviderCatalog, type ProviderCatalog,
type ResolveAgentDefaultModeInput,
} from "../../agent-sdk-types.js"; } from "../../agent-sdk-types.js";
import { importSessionFromPersistence } from "../../provider-session-import.js"; import { importSessionFromPersistence } from "../../provider-session-import.js";
import { import {
@@ -1472,7 +1473,28 @@ export class ClaudeAgentClient implements AgentClient {
async fetchCatalog(_options: FetchCatalogOptions): Promise<ProviderCatalog> { async fetchCatalog(_options: FetchCatalogOptions): Promise<ProviderCatalog> {
// Claude exposes a global catalog here; cwd/force are intentionally irrelevant. // Claude exposes a global catalog here; cwd/force are intentionally irrelevant.
const models = await getClaudeModelsWithSettings(this.logger, this.configDir); const models = await getClaudeModelsWithSettings(this.logger, this.configDir);
return { models, modes: DEFAULT_MODES }; const modes = detectIneligibleAutoModeTransport(
createProviderEnv({ baseEnv: process.env, runtimeSettings: this.runtimeSettings }),
)
? DEFAULT_MODES.filter((mode) => mode.id !== "auto")
: DEFAULT_MODES;
return {
models,
modes,
defaultModeId: modes.some((mode) => mode.id === "auto") ? "auto" : "default",
};
}
async resolveDefaultModeId({
config,
env: launchEnv,
}: ResolveAgentDefaultModeInput): Promise<string> {
const env = createProviderEnv({
baseEnv: process.env,
runtimeSettings: this.runtimeSettings,
overlays: [config.extra?.claude?.env, launchEnv],
});
return detectIneligibleAutoModeTransport(env) ? "default" : "auto";
} }
async listFeatures(config: AgentSessionConfig): Promise<AgentFeature[]> { async listFeatures(config: AgentSessionConfig): Promise<AgentFeature[]> {
@@ -6379,8 +6379,21 @@ export class CodexAppServerAgentClient implements AgentClient {
} }
async fetchCatalog(_options: FetchCatalogOptions): Promise<ProviderCatalog> { async fetchCatalog(_options: FetchCatalogOptions): Promise<ProviderCatalog> {
const models = await this.fetchModelsFromAppServer(); const [models, autoReviewEnabled] = await Promise.all([
return { models, modes: CODEX_MODES }; this.fetchModelsFromAppServer(),
this.resolveAutoReviewEnabled(),
]);
return {
models,
defaultModeId: autoReviewEnabled ? "auto-review" : DEFAULT_CODEX_MODE_ID,
modes: autoReviewEnabled
? CODEX_MODES
: CODEX_MODES.filter((mode) => mode.id !== "auto-review"),
};
}
async resolveDefaultModeId(): Promise<string> {
return (await this.resolveAutoReviewEnabled()) ? "auto-review" : DEFAULT_CODEX_MODE_ID;
} }
private async fetchModelsFromAppServer(): Promise<AgentModelDefinition[]> { private async fetchModelsFromAppServer(): Promise<AgentModelDefinition[]> {