extract resolveModel() to run before agent selection

model resolution was duplicated inside each agent (opentoad, claude) and
PULLFROG_MODEL override was not considered when choosing the agent. now
resolveModel() runs first in main.ts, its result feeds into resolveAgent()
for agent selection, and the resolved model is passed to the agent via
ctx.resolvedModel. agents only handle their own fallback (opentoad: auto-select
via opencode models, claude: strip provider prefix).

also removes the hardcoded anthropic/claude-sonnet test runner default since
ANTHROPIC_API_KEY is no longer in CI.

Made-with: Cursor
This commit is contained in:
Colin McDonnell
2026-04-01 06:51:18 +00:00
committed by pullfrog[bot]
parent 1f1e3995f9
commit b1f9878877
8 changed files with 83 additions and 127 deletions
+1 -1
View File
@@ -3,7 +3,7 @@ import { resolveAgent } from "./agent.ts";
describe("resolveAgent", () => {
it("returns opentoad", () => {
const agent = resolveAgent();
const agent = resolveAgent({});
expect(agent.name).toBe("opentoad");
});
});
+31 -4
View File
@@ -1,6 +1,6 @@
import type { Agent } from "../agents/index.ts";
import { agents } from "../agents/index.ts";
import { getModelProvider } from "../models.ts";
import { getModelProvider, resolveCliModel } from "../models.ts";
import { log } from "./cli.ts";
function hasEnvVar(name: string): boolean {
@@ -12,7 +12,34 @@ function hasClaudeCodeAuth(): boolean {
return hasEnvVar("CLAUDE_CODE_OAUTH_TOKEN") || hasEnvVar("ANTHROPIC_API_KEY");
}
export function resolveAgent(ctx?: { model?: string | undefined }): Agent {
/**
* resolve the effective model for this run.
*
* priority:
* 1. PULLFROG_MODEL env var (explicit specifier override)
* 2. slug from repo config / payload → alias registry
* 3. undefined — agent will auto-select
*/
export function resolveModel(ctx: { slug?: string | undefined }): string | undefined {
const envModel = process.env.PULLFROG_MODEL?.trim();
if (envModel) {
log.info(`» model: ${envModel} (override via PULLFROG_MODEL)`);
return envModel;
}
if (ctx.slug) {
const resolved = resolveCliModel(ctx.slug);
if (resolved) {
log.info(`» model: ${resolved} (resolved from ${ctx.slug})`);
return resolved;
}
log.warning(`» unknown model slug "${ctx.slug}" — agent will auto-select`);
}
return undefined;
}
export function resolveAgent(ctx: { model?: string | undefined }): Agent {
// 1. explicit env var override (escape hatch)
const envAgent = process.env.PULLFROG_AGENT?.trim();
if (envAgent) {
@@ -24,7 +51,7 @@ export function resolveAgent(ctx?: { model?: string | undefined }): Agent {
}
// 2. if model is Anthropic and Claude Code credentials are available, use Claude Code
if (ctx?.model) {
if (ctx.model) {
try {
const provider = getModelProvider(ctx.model);
if (provider === "anthropic" && hasClaudeCodeAuth()) {
@@ -32,7 +59,7 @@ export function resolveAgent(ctx?: { model?: string | undefined }): Agent {
return agents.claude;
}
} catch {
// invalid model slug format — fall through
// invalid model format — fall through
}
}