feat: enable/disable providers from settings
Adds a per-provider toggle in Settings → Providers, persisted via daemon config, so users can turn off providers they don't have installed (or don't want to use) without removing them from the registry. Disabled providers stay visible everywhere — settings, `paseo provider ls`, MCP `list_providers`, model/mode lists — marked as disabled rather than silently filtered. Availability probes are skipped for them, and attempts to launch an agent or fetch models/modes from a disabled provider return a clear "Provider '<id>' is disabled" error. Schema additions (`enabled` on `ProviderSnapshotEntry`, MCP provider summary) are backward-compatible: optional with a default of true so older clients continue to parse new daemon messages. CLI: `paseo provider ls` gains an ENABLED column; JSON output includes the new field.
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
import { describe, expect, test } from "vitest";
|
||||
import {
|
||||
GetProvidersSnapshotResponseMessageSchema,
|
||||
ProviderSnapshotEntrySchema,
|
||||
ProvidersSnapshotUpdateMessageSchema,
|
||||
} from "./messages.js";
|
||||
|
||||
describe("provider snapshot message schemas", () => {
|
||||
test("defaults missing provider snapshot entry enabled state to true", () => {
|
||||
const parsed = ProviderSnapshotEntrySchema.parse({
|
||||
provider: "codex",
|
||||
status: "ready",
|
||||
label: "Codex",
|
||||
});
|
||||
|
||||
expect(parsed.enabled).toBe(true);
|
||||
});
|
||||
|
||||
test("preserves disabled provider snapshot entries", () => {
|
||||
const parsed = ProviderSnapshotEntrySchema.parse({
|
||||
provider: "claude",
|
||||
status: "unavailable",
|
||||
enabled: false,
|
||||
label: "Claude",
|
||||
});
|
||||
|
||||
expect(parsed.enabled).toBe(false);
|
||||
});
|
||||
|
||||
test("preserves enabled provider snapshot entries", () => {
|
||||
const parsed = ProviderSnapshotEntrySchema.parse({
|
||||
provider: "opencode",
|
||||
status: "loading",
|
||||
enabled: true,
|
||||
label: "OpenCode",
|
||||
});
|
||||
|
||||
expect(parsed.enabled).toBe(true);
|
||||
});
|
||||
|
||||
test("defaults missing enabled state in providers snapshot response entries", () => {
|
||||
const parsed = GetProvidersSnapshotResponseMessageSchema.parse({
|
||||
type: "get_providers_snapshot_response",
|
||||
payload: {
|
||||
entries: [
|
||||
{
|
||||
provider: "codex",
|
||||
status: "ready",
|
||||
label: "Codex",
|
||||
},
|
||||
{
|
||||
provider: "claude",
|
||||
status: "unavailable",
|
||||
enabled: false,
|
||||
label: "Claude",
|
||||
},
|
||||
],
|
||||
generatedAt: "2026-04-24T00:00:00.000Z",
|
||||
requestId: "req-providers",
|
||||
},
|
||||
});
|
||||
|
||||
expect(parsed.payload.entries.map((entry) => entry.enabled)).toEqual([true, false]);
|
||||
});
|
||||
|
||||
test("defaults missing enabled state in providers snapshot update entries", () => {
|
||||
const parsed = ProvidersSnapshotUpdateMessageSchema.parse({
|
||||
type: "providers_snapshot_update",
|
||||
payload: {
|
||||
cwd: "/tmp/repo",
|
||||
entries: [
|
||||
{
|
||||
provider: "codex",
|
||||
status: "ready",
|
||||
label: "Codex",
|
||||
},
|
||||
],
|
||||
generatedAt: "2026-04-24T00:00:00.000Z",
|
||||
},
|
||||
});
|
||||
|
||||
expect(parsed.payload.entries[0]?.enabled).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -98,7 +98,6 @@ import type {
|
||||
AgentPermissionRequest,
|
||||
AgentPermissionResponse,
|
||||
AgentPersistenceHandle,
|
||||
ProviderSnapshotEntry,
|
||||
ProviderStatus,
|
||||
AgentRuntimeInfo,
|
||||
AgentTimelineItem,
|
||||
@@ -169,9 +168,10 @@ const AgentModelDefinitionSchema: z.ZodType<AgentModelDefinition> = z.object({
|
||||
defaultThinkingOptionId: z.string().optional(),
|
||||
});
|
||||
|
||||
const ProviderSnapshotEntrySchema: z.ZodType<ProviderSnapshotEntry> = z.object({
|
||||
export const ProviderSnapshotEntrySchema = z.object({
|
||||
provider: AgentProviderSchema,
|
||||
status: ProviderStatusSchema,
|
||||
enabled: z.boolean().optional().default(true),
|
||||
error: z.string().optional(),
|
||||
models: z.array(AgentModelDefinitionSchema).optional(),
|
||||
modes: z.array(AgentModeSchema).optional(),
|
||||
|
||||
Reference in New Issue
Block a user