Compare commits

..

4 Commits

Author SHA1 Message Date
Colin McDonnell d525fc21be show fallback indicator in model dropdown, move agent logs to main, bump to 0.0.191
Made-with: Cursor
2026-04-03 19:00:52 +00:00
Colin McDonnell 45fb07b34f bump action to 0.0.190
Made-with: Cursor
2026-04-03 18:56:00 +00:00
Colin McDonnell cbcc83806f fall back mimo-v2-pro-free to big-pickle
Made-with: Cursor
2026-04-03 18:53:34 +00:00
Colin McDonnell 536fae692a update snapshot for google/gemma-4-31b release
Made-with: Cursor
2026-04-03 18:49:24 +00:00
9 changed files with 25 additions and 46 deletions
-5
View File
@@ -51,12 +51,7 @@ export const agent = (input: Agent): Agent => {
return { return {
...input, ...input,
run: async (ctx: AgentRunContext): Promise<AgentResult> => { run: async (ctx: AgentRunContext): Promise<AgentResult> => {
if (ctx.payload.model) log.info(`» model: ${ctx.payload.model}`);
if (ctx.payload.timeout) log.info(`» timeout: ${ctx.payload.timeout}`);
log.info(`» push: ${ctx.payload.push}`);
log.info(`» shell: ${ctx.payload.shell}`);
log.debug(`» payload: ${JSON.stringify(ctx.payload, null, 2)}`); log.debug(`» payload: ${JSON.stringify(ctx.payload, null, 2)}`);
return input.run(ctx); return input.run(ctx);
}, },
}; };
+7 -10
View File
@@ -107843,8 +107843,7 @@ var providers = {
resolve: "opencode/mimo-v2-pro-free", resolve: "opencode/mimo-v2-pro-free",
envVars: [], envVars: [],
isFree: true, isFree: true,
deprecated: true, fallback: "opencode/big-pickle"
fallback: "opencode/nemotron-3-super-free"
}, },
"minimax-m2.5-free": { "minimax-m2.5-free": {
displayName: "MiniMax M2.5", displayName: "MiniMax M2.5",
@@ -107954,7 +107953,6 @@ var modelAliases = Object.entries(providers).flatMap(
openRouterResolve: def.openRouterResolve, openRouterResolve: def.openRouterResolve,
preferred: def.preferred ?? false, preferred: def.preferred ?? false,
isFree: def.isFree ?? false, isFree: def.isFree ?? false,
deprecated: def.deprecated ?? false,
fallback: def.fallback fallback: def.fallback
})) }))
); );
@@ -107967,8 +107965,7 @@ function resolveCliModel(slug) {
visited.add(current); visited.add(current);
const alias = modelAliases.find((a) => a.slug === current); const alias = modelAliases.find((a) => a.slug === current);
if (!alias) return void 0; if (!alias) return void 0;
if (!alias.deprecated) return alias.resolve; if (!alias.fallback) return alias.resolve;
if (!alias.fallback) return void 0;
current = alias.fallback; current = alias.fallback;
} }
return void 0; return void 0;
@@ -144709,7 +144706,7 @@ var import_semver = __toESM(require_semver2(), 1);
// package.json // package.json
var package_default = { var package_default = {
name: "@pullfrog/pullfrog", name: "@pullfrog/pullfrog",
version: "0.0.189", version: "0.0.191",
type: "module", type: "module",
files: [ files: [
"index.js", "index.js",
@@ -149174,10 +149171,6 @@ var agent = (input) => {
return { return {
...input, ...input,
run: async (ctx) => { run: async (ctx) => {
if (ctx.payload.model) log.info(`\xBB model: ${ctx.payload.model}`);
if (ctx.payload.timeout) log.info(`\xBB timeout: ${ctx.payload.timeout}`);
log.info(`\xBB push: ${ctx.payload.push}`);
log.info(`\xBB shell: ${ctx.payload.shell}`);
log.debug(`\xBB payload: ${JSON.stringify(ctx.payload, null, 2)}`); log.debug(`\xBB payload: ${JSON.stringify(ctx.payload, null, 2)}`);
return input.run(ctx); return input.run(ctx);
} }
@@ -151364,6 +151357,10 @@ async function main() {
toolContext.mcpServerUrl = mcpHttpServer.url; toolContext.mcpServerUrl = mcpHttpServer.url;
log.info(`\xBB MCP server started at ${mcpHttpServer.url}`); log.info(`\xBB MCP server started at ${mcpHttpServer.url}`);
timer.checkpoint("mcpServer"); timer.checkpoint("mcpServer");
if (payload.model) log.info(`\xBB model: ${payload.model}`);
if (payload.timeout) log.info(`\xBB timeout: ${payload.timeout}`);
log.info(`\xBB push: ${payload.push}`);
log.info(`\xBB shell: ${payload.shell}`);
const instructions = resolveInstructions({ const instructions = resolveInstructions({
payload, payload,
repo: runContext.repo, repo: runContext.repo,
+1
View File
@@ -23,6 +23,7 @@ export {
modelAliases, modelAliases,
parseModel, parseModel,
providers, providers,
resolveCliModel,
resolveModelSlug, resolveModelSlug,
} from "../external.ts"; } from "../external.ts";
export type { Mode } from "../modes.ts"; export type { Mode } from "../modes.ts";
+5 -1
View File
@@ -305,6 +305,11 @@ export async function main(): Promise<MainResult> {
log.info(`» MCP server started at ${mcpHttpServer.url}`); log.info(`» MCP server started at ${mcpHttpServer.url}`);
timer.checkpoint("mcpServer"); timer.checkpoint("mcpServer");
if (payload.model) log.info(`» model: ${payload.model}`);
if (payload.timeout) log.info(`» timeout: ${payload.timeout}`);
log.info(`» push: ${payload.push}`);
log.info(`» shell: ${payload.shell}`);
const instructions = resolveInstructions({ const instructions = resolveInstructions({
payload, payload,
repo: runContext.repo, repo: runContext.repo,
@@ -312,7 +317,6 @@ export async function main(): Promise<MainResult> {
outputSchema, outputSchema,
learnings: runContext.repoSettings.learnings, learnings: runContext.repoSettings.learnings,
}); });
// log instructions as soon as they are fully resolved
const logParts = [ const logParts = [
instructions.eventInstructions instructions.eventInstructions
? `EVENT-LEVEL INSTRUCTIONS:\n${instructions.eventInstructions}` ? `EVENT-LEVEL INSTRUCTIONS:\n${instructions.eventInstructions}`
+4 -11
View File
@@ -22,9 +22,7 @@ export interface ModelAlias {
preferred: boolean; preferred: boolean;
/** whether this alias is free and requires no API key */ /** whether this alias is free and requires no API key */
isFree: boolean; isFree: boolean;
/** model is deprecated on models.dev — resolve via fallback chain instead */ /** slug of a replacement model — presence implies this model is deprecated */
deprecated: boolean;
/** slug of another model to resolve to when this one is deprecated */
fallback: string | undefined; fallback: string | undefined;
} }
@@ -37,9 +35,7 @@ interface ModelDef {
preferred?: boolean; preferred?: boolean;
envVars?: readonly string[]; envVars?: readonly string[];
isFree?: boolean; isFree?: boolean;
/** model is deprecated on models.dev — kept for backward compatibility, resolved via fallback */ /** slug of a replacement model — presence implies this model is deprecated */
deprecated?: boolean;
/** slug of another model to fall back to (e.g. "opencode/nemotron-3-super-free") */
fallback?: string; fallback?: string;
} }
@@ -229,8 +225,7 @@ export const providers = {
resolve: "opencode/mimo-v2-pro-free", resolve: "opencode/mimo-v2-pro-free",
envVars: [], envVars: [],
isFree: true, isFree: true,
deprecated: true, fallback: "opencode/big-pickle",
fallback: "opencode/nemotron-3-super-free",
}, },
"minimax-m2.5-free": { "minimax-m2.5-free": {
displayName: "MiniMax M2.5", displayName: "MiniMax M2.5",
@@ -358,7 +353,6 @@ export const modelAliases: ModelAlias[] = Object.entries(providers).flatMap(
openRouterResolve: def.openRouterResolve, openRouterResolve: def.openRouterResolve,
preferred: def.preferred ?? false, preferred: def.preferred ?? false,
isFree: def.isFree ?? false, isFree: def.isFree ?? false,
deprecated: def.deprecated ?? false,
fallback: def.fallback, fallback: def.fallback,
})) }))
); );
@@ -385,8 +379,7 @@ export function resolveCliModel(slug: string): string | undefined {
visited.add(current); visited.add(current);
const alias = modelAliases.find((a) => a.slug === current); const alias = modelAliases.find((a) => a.slug === current);
if (!alias) return undefined; if (!alias) return undefined;
if (!alias.deprecated) return alias.resolve; if (!alias.fallback) return alias.resolve;
if (!alias.fallback) return undefined;
current = alias.fallback; current = alias.fallback;
} }
return undefined; return undefined;
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@pullfrog/pullfrog", "name": "@pullfrog/pullfrog",
"version": "0.0.189", "version": "0.0.191",
"type": "module", "type": "module",
"files": [ "files": [
"index.js", "index.js",
+2 -4
View File
@@ -37687,8 +37687,7 @@ var providers = {
resolve: "opencode/mimo-v2-pro-free", resolve: "opencode/mimo-v2-pro-free",
envVars: [], envVars: [],
isFree: true, isFree: true,
deprecated: true, fallback: "opencode/big-pickle"
fallback: "opencode/nemotron-3-super-free"
}, },
"minimax-m2.5-free": { "minimax-m2.5-free": {
displayName: "MiniMax M2.5", displayName: "MiniMax M2.5",
@@ -37776,7 +37775,6 @@ var modelAliases = Object.entries(providers).flatMap(
openRouterResolve: def.openRouterResolve, openRouterResolve: def.openRouterResolve,
preferred: def.preferred ?? false, preferred: def.preferred ?? false,
isFree: def.isFree ?? false, isFree: def.isFree ?? false,
deprecated: def.deprecated ?? false,
fallback: def.fallback fallback: def.fallback
})) }))
); );
@@ -41561,7 +41559,7 @@ var core3 = __toESM(require_core(), 1);
// package.json // package.json
var package_default = { var package_default = {
name: "@pullfrog/pullfrog", name: "@pullfrog/pullfrog",
version: "0.0.189", version: "0.0.191",
type: "module", type: "module",
files: [ files: [
"index.js", "index.js",
+2 -2
View File
@@ -11,8 +11,8 @@ exports[`latest model per provider snapshot > matches snapshot 1`] = `
"releaseDate": "2025-12-01", "releaseDate": "2025-12-01",
}, },
"google": { "google": {
"modelId": "gemini-3.1-flash-lite-preview", "modelId": "gemma-4-31b",
"releaseDate": "2026-03-03", "releaseDate": "2026-04-02",
}, },
"moonshotai": { "moonshotai": {
"modelId": "kimi-k2.5", "modelId": "kimi-k2.5",
+3 -12
View File
@@ -38,7 +38,7 @@ describe("models.dev validity", async () => {
).toBeDefined(); ).toBeDefined();
}); });
if (!alias.deprecated) { if (!alias.fallback) {
it(`${alias.resolve} is not deprecated`, () => { it(`${alias.resolve} is not deprecated`, () => {
const model = data[parsed.provider]?.models[parsed.modelId]; const model = data[parsed.provider]?.models[parsed.modelId];
if (!model) return; // covered by existence test above if (!model) return; // covered by existence test above
@@ -47,17 +47,8 @@ describe("models.dev validity", async () => {
} }
} }
// deprecated models must have a fallback that resolves to a non-deprecated model for (const alias of modelAliases.filter((a) => a.fallback)) {
const deprecatedAliases = modelAliases.filter((a) => a.deprecated); it(`${alias.slug} fallback chain resolves to a non-deprecated model`, () => {
for (const alias of deprecatedAliases) {
it(`${alias.slug} (deprecated) has a fallback`, () => {
expect(
alias.fallback,
`deprecated model "${alias.slug}" is missing a fallback`
).toBeDefined();
});
it(`${alias.slug} (deprecated) fallback chain resolves`, () => {
const resolved = resolveCliModel(alias.slug); const resolved = resolveCliModel(alias.slug);
expect( expect(
resolved, resolved,