Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d525fc21be | |||
| 45fb07b34f | |||
| cbcc83806f | |||
| 536fae692a | |||
| b8c4d5b716 |
+1
-5
@@ -13,7 +13,7 @@
|
|||||||
* security is enforced at the tool layer, not the process layer.
|
* security is enforced at the tool layer, not the process layer.
|
||||||
*/
|
*/
|
||||||
import { execFileSync } from "node:child_process";
|
import { execFileSync } from "node:child_process";
|
||||||
import { mkdirSync, rmSync } from "node:fs";
|
import { mkdirSync } from "node:fs";
|
||||||
import { join } from "node:path";
|
import { join } from "node:path";
|
||||||
import { performance } from "node:perf_hooks";
|
import { performance } from "node:perf_hooks";
|
||||||
import { ghPullfrogMcpName } from "../external.ts";
|
import { ghPullfrogMcpName } from "../external.ts";
|
||||||
@@ -627,10 +627,6 @@ export const opentoad = agent({
|
|||||||
agent: "opencode",
|
agent: "opencode",
|
||||||
});
|
});
|
||||||
|
|
||||||
// remove untrusted project plugins/tools before agent starts (no env var to disable loading)
|
|
||||||
rmSync(join(process.cwd(), ".opencode", "plugins"), { recursive: true, force: true });
|
|
||||||
rmSync(join(process.cwd(), ".opencode", "tools"), { recursive: true, force: true });
|
|
||||||
|
|
||||||
const args = ["run", ctx.instructions.full, "--format", "json", "--print-logs"];
|
const args = ["run", ctx.instructions.full, "--format", "json", "--print-logs"];
|
||||||
|
|
||||||
// OPENCODE_PERMISSION has absolute highest precedence (merged after managed/MDM configs).
|
// OPENCODE_PERMISSION has absolute highest precedence (merged after managed/MDM configs).
|
||||||
|
|||||||
@@ -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);
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -107842,7 +107842,8 @@ var providers = {
|
|||||||
displayName: "MiMo V2 Pro",
|
displayName: "MiMo V2 Pro",
|
||||||
resolve: "opencode/mimo-v2-pro-free",
|
resolve: "opencode/mimo-v2-pro-free",
|
||||||
envVars: [],
|
envVars: [],
|
||||||
isFree: true
|
isFree: true,
|
||||||
|
fallback: "opencode/big-pickle"
|
||||||
},
|
},
|
||||||
"minimax-m2.5-free": {
|
"minimax-m2.5-free": {
|
||||||
displayName: "MiniMax M2.5",
|
displayName: "MiniMax M2.5",
|
||||||
@@ -107951,14 +107952,23 @@ var modelAliases = Object.entries(providers).flatMap(
|
|||||||
resolve: def.resolve,
|
resolve: def.resolve,
|
||||||
openRouterResolve: def.openRouterResolve,
|
openRouterResolve: def.openRouterResolve,
|
||||||
preferred: def.preferred ?? false,
|
preferred: def.preferred ?? false,
|
||||||
isFree: def.isFree ?? false
|
isFree: def.isFree ?? false,
|
||||||
|
fallback: def.fallback
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
function resolveModelSlug(slug) {
|
var MAX_FALLBACK_DEPTH = 10;
|
||||||
return modelAliases.find((a) => a.slug === slug)?.resolve;
|
|
||||||
}
|
|
||||||
function resolveCliModel(slug) {
|
function resolveCliModel(slug) {
|
||||||
return resolveModelSlug(slug);
|
let current = slug;
|
||||||
|
const visited = /* @__PURE__ */ new Set();
|
||||||
|
for (let i = 0; i < MAX_FALLBACK_DEPTH; i++) {
|
||||||
|
if (visited.has(current)) return void 0;
|
||||||
|
visited.add(current);
|
||||||
|
const alias = modelAliases.find((a) => a.slug === current);
|
||||||
|
if (!alias) return void 0;
|
||||||
|
if (!alias.fallback) return alias.resolve;
|
||||||
|
current = alias.fallback;
|
||||||
|
}
|
||||||
|
return void 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// utils/buildPullfrogFooter.ts
|
// utils/buildPullfrogFooter.ts
|
||||||
@@ -144696,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.188",
|
version: "0.0.191",
|
||||||
type: "module",
|
type: "module",
|
||||||
files: [
|
files: [
|
||||||
"index.js",
|
"index.js",
|
||||||
@@ -149161,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);
|
||||||
}
|
}
|
||||||
@@ -149541,7 +149547,7 @@ var claude = agent({
|
|||||||
|
|
||||||
// agents/opentoad.ts
|
// agents/opentoad.ts
|
||||||
import { execFileSync as execFileSync3 } from "node:child_process";
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
||||||
import { mkdirSync as mkdirSync4, rmSync } from "node:fs";
|
import { mkdirSync as mkdirSync4 } from "node:fs";
|
||||||
import { join as join11 } from "node:path";
|
import { join as join11 } from "node:path";
|
||||||
import { performance as performance7 } from "node:perf_hooks";
|
import { performance as performance7 } from "node:perf_hooks";
|
||||||
async function installOpencodeCli() {
|
async function installOpencodeCli() {
|
||||||
@@ -149925,8 +149931,6 @@ var opentoad = agent({
|
|||||||
env: homeEnv,
|
env: homeEnv,
|
||||||
agent: "opencode"
|
agent: "opencode"
|
||||||
});
|
});
|
||||||
rmSync(join11(process.cwd(), ".opencode", "plugins"), { recursive: true, force: true });
|
|
||||||
rmSync(join11(process.cwd(), ".opencode", "tools"), { recursive: true, force: true });
|
|
||||||
const args2 = ["run", ctx.instructions.full, "--format", "json", "--print-logs"];
|
const args2 = ["run", ctx.instructions.full, "--format", "json", "--print-logs"];
|
||||||
const permissionOverride = JSON.stringify({
|
const permissionOverride = JSON.stringify({
|
||||||
external_directory: { "*": "deny", "/tmp/*": "allow" }
|
external_directory: { "*": "deny", "/tmp/*": "allow" }
|
||||||
@@ -151353,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,
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -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}`
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ 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;
|
||||||
|
/** slug of a replacement model — presence implies this model is deprecated */
|
||||||
|
fallback: string | undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ModelDef {
|
interface ModelDef {
|
||||||
@@ -33,6 +35,8 @@ interface ModelDef {
|
|||||||
preferred?: boolean;
|
preferred?: boolean;
|
||||||
envVars?: readonly string[];
|
envVars?: readonly string[];
|
||||||
isFree?: boolean;
|
isFree?: boolean;
|
||||||
|
/** slug of a replacement model — presence implies this model is deprecated */
|
||||||
|
fallback?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ProviderConfig {
|
export interface ProviderConfig {
|
||||||
@@ -221,6 +225,7 @@ export const providers = {
|
|||||||
resolve: "opencode/mimo-v2-pro-free",
|
resolve: "opencode/mimo-v2-pro-free",
|
||||||
envVars: [],
|
envVars: [],
|
||||||
isFree: true,
|
isFree: true,
|
||||||
|
fallback: "opencode/big-pickle",
|
||||||
},
|
},
|
||||||
"minimax-m2.5-free": {
|
"minimax-m2.5-free": {
|
||||||
displayName: "MiniMax M2.5",
|
displayName: "MiniMax M2.5",
|
||||||
@@ -348,6 +353,7 @@ 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,
|
||||||
|
fallback: def.fallback,
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -358,7 +364,23 @@ export function resolveModelSlug(slug: string): string | undefined {
|
|||||||
return modelAliases.find((a) => a.slug === slug)?.resolve;
|
return modelAliases.find((a) => a.slug === slug)?.resolve;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** resolve a model slug to the CLI-ready model string (full models.dev specifier) */
|
const MAX_FALLBACK_DEPTH = 10;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* resolve a model slug to the CLI-ready model string, following the fallback
|
||||||
|
* chain when a model is deprecated. returns the first non-deprecated resolve
|
||||||
|
* target, or undefined if the chain is exhausted or broken.
|
||||||
|
*/
|
||||||
export function resolveCliModel(slug: string): string | undefined {
|
export function resolveCliModel(slug: string): string | undefined {
|
||||||
return resolveModelSlug(slug);
|
let current = slug;
|
||||||
|
const visited = new Set<string>();
|
||||||
|
for (let i = 0; i < MAX_FALLBACK_DEPTH; i++) {
|
||||||
|
if (visited.has(current)) return undefined;
|
||||||
|
visited.add(current);
|
||||||
|
const alias = modelAliases.find((a) => a.slug === current);
|
||||||
|
if (!alias) return undefined;
|
||||||
|
if (!alias.fallback) return alias.resolve;
|
||||||
|
current = alias.fallback;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@pullfrog/pullfrog",
|
"name": "@pullfrog/pullfrog",
|
||||||
"version": "0.0.188",
|
"version": "0.0.191",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"files": [
|
"files": [
|
||||||
"index.js",
|
"index.js",
|
||||||
|
|||||||
@@ -37686,7 +37686,8 @@ var providers = {
|
|||||||
displayName: "MiMo V2 Pro",
|
displayName: "MiMo V2 Pro",
|
||||||
resolve: "opencode/mimo-v2-pro-free",
|
resolve: "opencode/mimo-v2-pro-free",
|
||||||
envVars: [],
|
envVars: [],
|
||||||
isFree: true
|
isFree: true,
|
||||||
|
fallback: "opencode/big-pickle"
|
||||||
},
|
},
|
||||||
"minimax-m2.5-free": {
|
"minimax-m2.5-free": {
|
||||||
displayName: "MiniMax M2.5",
|
displayName: "MiniMax M2.5",
|
||||||
@@ -37773,7 +37774,8 @@ var modelAliases = Object.entries(providers).flatMap(
|
|||||||
resolve: def.resolve,
|
resolve: def.resolve,
|
||||||
openRouterResolve: def.openRouterResolve,
|
openRouterResolve: def.openRouterResolve,
|
||||||
preferred: def.preferred ?? false,
|
preferred: def.preferred ?? false,
|
||||||
isFree: def.isFree ?? false
|
isFree: def.isFree ?? false,
|
||||||
|
fallback: def.fallback
|
||||||
}))
|
}))
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -41557,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.188",
|
version: "0.0.191",
|
||||||
type: "module",
|
type: "module",
|
||||||
files: [
|
files: [
|
||||||
"index.js",
|
"index.js",
|
||||||
|
|||||||
@@ -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",
|
||||||
|
|||||||
+17
-5
@@ -1,5 +1,5 @@
|
|||||||
import { describe, expect, it } from "vitest";
|
import { describe, expect, it } from "vitest";
|
||||||
import { type ModelProvider, modelAliases, providers } from "../models.ts";
|
import { type ModelProvider, modelAliases, providers, resolveCliModel } from "../models.ts";
|
||||||
|
|
||||||
type ModelsDevModel = {
|
type ModelsDevModel = {
|
||||||
name: string;
|
name: string;
|
||||||
@@ -38,10 +38,22 @@ describe("models.dev validity", async () => {
|
|||||||
).toBeDefined();
|
).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it(`${alias.resolve} is not deprecated`, () => {
|
if (!alias.fallback) {
|
||||||
const model = data[parsed.provider]?.models[parsed.modelId];
|
it(`${alias.resolve} is not deprecated`, () => {
|
||||||
if (!model) return; // covered by existence test above
|
const model = data[parsed.provider]?.models[parsed.modelId];
|
||||||
expect(model.status, `${alias.resolve} is deprecated on models.dev`).not.toBe("deprecated");
|
if (!model) return; // covered by existence test above
|
||||||
|
expect(model.status, `${alias.resolve} is deprecated on models.dev`).not.toBe("deprecated");
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const alias of modelAliases.filter((a) => a.fallback)) {
|
||||||
|
it(`${alias.slug} fallback chain resolves to a non-deprecated model`, () => {
|
||||||
|
const resolved = resolveCliModel(alias.slug);
|
||||||
|
expect(
|
||||||
|
resolved,
|
||||||
|
`fallback chain for "${alias.slug}" does not resolve to a non-deprecated model`
|
||||||
|
).toBeDefined();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user