diff --git a/packages/cli/src/commands/agent/run.ts b/packages/cli/src/commands/agent/run.ts index d698a9d2a..46ee973a7 100644 --- a/packages/cli/src/commands/agent/run.ts +++ b/packages/cli/src/commands/agent/run.ts @@ -30,7 +30,6 @@ export function addRunOptions(cmd: Command): Command { .option( "--provider ", "Agent provider, or provider/model (e.g. codex or codex/gpt-5.4)", - "claude", ) .option( "--model ", diff --git a/packages/cli/src/commands/schedule/shared.ts b/packages/cli/src/commands/schedule/shared.ts index 1d02f9e68..3087b88a7 100644 --- a/packages/cli/src/commands/schedule/shared.ts +++ b/packages/cli/src/commands/schedule/shared.ts @@ -86,20 +86,20 @@ export function formatDurationMs(durationMs: number): string { function resolveScheduleTarget(args: { targetValue: string | undefined; hasExplicitProviderSelection: boolean; - newAgentTarget: ScheduleTarget; + createNewAgentTarget: () => ScheduleTarget; }): ScheduleTarget { - const { targetValue, hasExplicitProviderSelection, newAgentTarget } = args; + const { targetValue, hasExplicitProviderSelection, createNewAgentTarget } = args; const currentAgentId = process.env.PASEO_AGENT_ID?.trim(); if (!targetValue) { if (currentAgentId && !hasExplicitProviderSelection) { return { type: "self", agentId: currentAgentId }; } - return newAgentTarget; + return createNewAgentTarget(); } if (targetValue === "new-agent") { - return newAgentTarget; + return createNewAgentTarget(); } if (hasExplicitProviderSelection) { @@ -155,22 +155,23 @@ export function parseScheduleCreateInput(options: { const targetValue = options.target?.trim(); const hasExplicitProviderSelection = options.provider !== undefined; - const resolvedProviderModel = resolveProviderAndModel({ - provider: options.provider, - defaultProvider: "claude", - }); - const newAgentTarget: ScheduleTarget = { - type: "new-agent", - config: { - provider: resolvedProviderModel.provider, - cwd: process.cwd(), - ...(resolvedProviderModel.model ? { model: resolvedProviderModel.model } : {}), - }, + const createNewAgentTarget = (): ScheduleTarget => { + const resolvedProviderModel = resolveProviderAndModel({ + provider: options.provider, + }); + return { + type: "new-agent", + config: { + provider: resolvedProviderModel.provider, + cwd: process.cwd(), + ...(resolvedProviderModel.model ? { model: resolvedProviderModel.model } : {}), + }, + }; }; const target = resolveScheduleTarget({ targetValue, hasExplicitProviderSelection, - newAgentTarget, + createNewAgentTarget, }); const maxRuns = diff --git a/packages/cli/src/utils/provider-model.test.ts b/packages/cli/src/utils/provider-model.test.ts new file mode 100644 index 000000000..1ce6874db --- /dev/null +++ b/packages/cli/src/utils/provider-model.test.ts @@ -0,0 +1,38 @@ +import { describe, expect, test } from "vitest"; + +import { resolveProviderAndModel } from "./provider-model.js"; + +describe("resolveProviderAndModel", () => { + test("requires an explicit provider when no default is supplied", () => { + expect(() => resolveProviderAndModel({})).toThrow( + expect.objectContaining({ + code: "MISSING_PROVIDER", + message: "Provider is required", + }), + ); + }); + + test("uses an explicit default provider when supplied by a scoped caller", () => { + expect(resolveProviderAndModel({ defaultProvider: "claude" })).toEqual({ + provider: "claude", + model: undefined, + }); + }); + + test("parses provider/model shorthand", () => { + expect(resolveProviderAndModel({ provider: "codex/gpt-5.4" })).toEqual({ + provider: "codex", + model: "gpt-5.4", + }); + }); + + test("rejects conflicting model values", () => { + expect(() => + resolveProviderAndModel({ provider: "codex/gpt-5.4", model: "gpt-5.4-mini" }), + ).toThrow( + expect.objectContaining({ + code: "CONFLICTING_MODEL_OPTIONS", + }), + ); + }); +}); diff --git a/packages/cli/src/utils/provider-model.ts b/packages/cli/src/utils/provider-model.ts index 7458e6c6d..a610c396b 100644 --- a/packages/cli/src/utils/provider-model.ts +++ b/packages/cli/src/utils/provider-model.ts @@ -14,9 +14,19 @@ export interface ResolvedProviderModel { export function resolveProviderAndModel( options: ResolveProviderAndModelOptions, ): ResolvedProviderModel { - const providerInput = options.provider?.trim() || options.defaultProvider || "claude"; + const providerInput = options.provider?.trim() || options.defaultProvider; const modelInput = options.model?.trim(); + if (!providerInput) { + const error: CommandError = { + code: "MISSING_PROVIDER", + message: "Provider is required", + details: + "Pass --provider or --provider /. Use `paseo provider ls` to see providers and `paseo provider models ` to see models.", + }; + throw error; + } + if (options.model !== undefined && !modelInput) { const error: CommandError = { code: "INVALID_MODEL",