feat: upgrade MiniMax from M2.5 to M2.7 and expand to more agents/categories
- Upgrade minimax-m2.5 → minimax-m2.7 (latest model) across all agents and categories - Replace minimax-m2.5-free with minimax-m2.7-highspeed (optimized speed variant) - Expand MiniMax fallback coverage to atlas, sisyphus-junior, writing, and unspecified-low - Add isMiniMaxModel() detection function in types.ts for model family detection - Update all tests (58 passing) and documentation
This commit is contained in:
@@ -13,8 +13,8 @@ Agent factories following `createXXXAgent(model) → AgentConfig` pattern. Each
|
||||
| **Sisyphus** | claude-opus-4-6 max | 0.1 | all | k2p5 → kimi-k2.5 → gpt-5.4 medium → glm-5 → big-pickle | Main orchestrator, plans + delegates |
|
||||
| **Hephaestus** | gpt-5.3-codex medium | 0.1 | all | gpt-5.4 medium (copilot) | Autonomous deep worker |
|
||||
| **Oracle** | gpt-5.4 high | 0.1 | subagent | gemini-3.1-pro high → claude-opus-4-6 max | Read-only consultation |
|
||||
| **Librarian** | gemini-3-flash | 0.1 | subagent | minimax-m2.5-free → big-pickle | External docs/code search |
|
||||
| **Explore** | grok-code-fast-1 | 0.1 | subagent | minimax-m2.5-free → claude-haiku-4-5 → gpt-5-nano | Contextual grep |
|
||||
| **Librarian** | minimax-m2.7 | 0.1 | subagent | minimax-m2.7-highspeed → claude-haiku-4-5 → gpt-5-nano | External docs/code search |
|
||||
| **Explore** | grok-code-fast-1 | 0.1 | subagent | minimax-m2.7-highspeed → minimax-m2.7 → claude-haiku-4-5 → gpt-5-nano | Contextual grep |
|
||||
| **Multimodal-Looker** | gpt-5.3-codex medium | 0.1 | subagent | k2p5 → gemini-3-flash → glm-4.6v → gpt-5-nano | PDF/image analysis |
|
||||
| **Metis** | claude-opus-4-6 max | **0.3** | subagent | gpt-5.4 high → gemini-3.1-pro high | Pre-planning consultant |
|
||||
| **Momus** | gpt-5.4 xhigh | 0.1 | subagent | claude-opus-4-6 max → gemini-3.1-pro high | Plan reviewer |
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, test, expect } from "bun:test";
|
||||
import { isGptModel, isGeminiModel, isGpt5_4Model } from "./types";
|
||||
import { isGptModel, isGeminiModel, isGpt5_4Model, isMiniMaxModel } from "./types";
|
||||
|
||||
describe("isGpt5_4Model", () => {
|
||||
test("detects gpt-5.4 models", () => {
|
||||
@@ -79,6 +79,28 @@ describe("isGptModel", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("isMiniMaxModel", () => {
|
||||
test("detects minimax models with provider prefix", () => {
|
||||
expect(isMiniMaxModel("opencode-go/minimax-m2.7")).toBe(true);
|
||||
expect(isMiniMaxModel("opencode/minimax-m2.7-highspeed")).toBe(true);
|
||||
expect(isMiniMaxModel("opencode-go/minimax-m2.5")).toBe(true);
|
||||
expect(isMiniMaxModel("opencode/minimax-m2.5-free")).toBe(true);
|
||||
});
|
||||
|
||||
test("detects minimax models without provider prefix", () => {
|
||||
expect(isMiniMaxModel("minimax-m2.7")).toBe(true);
|
||||
expect(isMiniMaxModel("minimax-m2.7-highspeed")).toBe(true);
|
||||
expect(isMiniMaxModel("minimax-m2.5")).toBe(true);
|
||||
});
|
||||
|
||||
test("does not match non-minimax models", () => {
|
||||
expect(isMiniMaxModel("openai/gpt-5.4")).toBe(false);
|
||||
expect(isMiniMaxModel("anthropic/claude-opus-4-6")).toBe(false);
|
||||
expect(isMiniMaxModel("google/gemini-3.1-pro")).toBe(false);
|
||||
expect(isMiniMaxModel("opencode-go/kimi-k2.5")).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isGeminiModel", () => {
|
||||
test("#given google provider models #then returns true", () => {
|
||||
expect(isGeminiModel("google/gemini-3.1-pro")).toBe(true);
|
||||
|
||||
@@ -91,6 +91,11 @@ export function isGpt5_3CodexModel(model: string): boolean {
|
||||
|
||||
const GEMINI_PROVIDERS = ["google/", "google-vertex/"];
|
||||
|
||||
export function isMiniMaxModel(model: string): boolean {
|
||||
const modelName = extractModelName(model).toLowerCase();
|
||||
return modelName.includes("minimax");
|
||||
}
|
||||
|
||||
export function isGeminiModel(model: string): boolean {
|
||||
if (GEMINI_PROVIDERS.some((prefix) => model.startsWith(prefix))) return true;
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ export function generateModelConfig(config: InstallConfig): GeneratedOmoConfig {
|
||||
for (const [role, req] of Object.entries(CLI_AGENT_MODEL_REQUIREMENTS)) {
|
||||
if (role === "librarian") {
|
||||
if (avail.opencodeGo) {
|
||||
agents[role] = { model: "opencode-go/minimax-m2.5" }
|
||||
agents[role] = { model: "opencode-go/minimax-m2.7" }
|
||||
} else if (avail.zai) {
|
||||
agents[role] = { model: ZAI_MODEL }
|
||||
}
|
||||
@@ -68,7 +68,7 @@ export function generateModelConfig(config: InstallConfig): GeneratedOmoConfig {
|
||||
} else if (avail.opencodeZen) {
|
||||
agents[role] = { model: "opencode/claude-haiku-4-5" }
|
||||
} else if (avail.opencodeGo) {
|
||||
agents[role] = { model: "opencode-go/minimax-m2.5" }
|
||||
agents[role] = { model: "opencode-go/minimax-m2.7" }
|
||||
} else if (avail.copilot) {
|
||||
agents[role] = { model: "github-copilot/gpt-5-mini" }
|
||||
} else {
|
||||
|
||||
@@ -53,8 +53,8 @@ describe("generateModelConfig OpenAI-only model catalog", () => {
|
||||
const result = generateModelConfig(config)
|
||||
|
||||
// #then
|
||||
expect(result.agents?.explore).toEqual({ model: "opencode-go/minimax-m2.5" })
|
||||
expect(result.agents?.librarian).toEqual({ model: "opencode-go/minimax-m2.5" })
|
||||
expect(result.agents?.explore).toEqual({ model: "opencode-go/minimax-m2.7" })
|
||||
expect(result.agents?.librarian).toEqual({ model: "opencode-go/minimax-m2.7" })
|
||||
expect(result.categories?.quick).toEqual({ model: "openai/gpt-5.4-mini" })
|
||||
})
|
||||
})
|
||||
|
||||
@@ -64,23 +64,23 @@ describe("AGENT_MODEL_REQUIREMENTS", () => {
|
||||
expect(last.model).toBe("big-pickle")
|
||||
})
|
||||
|
||||
test("librarian has valid fallbackChain with opencode-go/minimax-m2.5 as primary", () => {
|
||||
test("librarian has valid fallbackChain with opencode-go/minimax-m2.7 as primary", () => {
|
||||
// given - librarian agent requirement
|
||||
const librarian = AGENT_MODEL_REQUIREMENTS["librarian"]
|
||||
|
||||
// when - accessing librarian requirement
|
||||
// then - fallbackChain exists with opencode-go/minimax-m2.5 as first entry
|
||||
// then - fallbackChain exists with opencode-go/minimax-m2.7 as first entry
|
||||
expect(librarian).toBeDefined()
|
||||
expect(librarian.fallbackChain).toBeArray()
|
||||
expect(librarian.fallbackChain.length).toBeGreaterThan(0)
|
||||
|
||||
const primary = librarian.fallbackChain[0]
|
||||
expect(primary.providers[0]).toBe("opencode-go")
|
||||
expect(primary.model).toBe("minimax-m2.5")
|
||||
expect(primary.model).toBe("minimax-m2.7")
|
||||
|
||||
const second = librarian.fallbackChain[1]
|
||||
expect(second.providers[0]).toBe("opencode")
|
||||
expect(second.model).toBe("minimax-m2.5-free")
|
||||
expect(second.model).toBe("minimax-m2.7-highspeed")
|
||||
|
||||
const tertiary = librarian.fallbackChain[2]
|
||||
expect(tertiary.providers).toContain("anthropic")
|
||||
@@ -95,7 +95,7 @@ describe("AGENT_MODEL_REQUIREMENTS", () => {
|
||||
const explore = AGENT_MODEL_REQUIREMENTS["explore"]
|
||||
|
||||
// when - accessing explore requirement
|
||||
// then - fallbackChain: grok → opencode-go/minimax → minimax-free → haiku → nano
|
||||
// then - fallbackChain: grok → minimax-m2.7-highspeed → minimax-m2.7 → haiku → nano
|
||||
expect(explore).toBeDefined()
|
||||
expect(explore.fallbackChain).toBeArray()
|
||||
expect(explore.fallbackChain).toHaveLength(5)
|
||||
@@ -106,11 +106,11 @@ describe("AGENT_MODEL_REQUIREMENTS", () => {
|
||||
|
||||
const secondary = explore.fallbackChain[1]
|
||||
expect(secondary.providers).toContain("opencode-go")
|
||||
expect(secondary.model).toBe("minimax-m2.5")
|
||||
expect(secondary.model).toBe("minimax-m2.7-highspeed")
|
||||
|
||||
const tertiary = explore.fallbackChain[2]
|
||||
expect(tertiary.providers).toContain("opencode")
|
||||
expect(tertiary.model).toBe("minimax-m2.5-free")
|
||||
expect(tertiary.model).toBe("minimax-m2.7")
|
||||
|
||||
const quaternary = explore.fallbackChain[3]
|
||||
expect(quaternary.providers).toContain("anthropic")
|
||||
@@ -211,7 +211,7 @@ describe("AGENT_MODEL_REQUIREMENTS", () => {
|
||||
// then - fallbackChain exists with claude-sonnet-4-6 as first entry
|
||||
expect(atlas).toBeDefined()
|
||||
expect(atlas.fallbackChain).toBeArray()
|
||||
expect(atlas.fallbackChain.length).toBeGreaterThan(0)
|
||||
expect(atlas.fallbackChain).toHaveLength(4)
|
||||
|
||||
const primary = atlas.fallbackChain[0]
|
||||
expect(primary.model).toBe("claude-sonnet-4-6")
|
||||
@@ -227,15 +227,20 @@ describe("AGENT_MODEL_REQUIREMENTS", () => {
|
||||
model: "gpt-5.4",
|
||||
variant: "medium",
|
||||
})
|
||||
|
||||
const quaternary = atlas.fallbackChain[3]
|
||||
expect(quaternary.model).toBe("minimax-m2.7")
|
||||
expect(quaternary.providers[0]).toBe("opencode-go")
|
||||
})
|
||||
|
||||
test("sisyphus-junior has an OpenAI fallback before big-pickle", () => {
|
||||
test("sisyphus-junior has an OpenAI fallback and minimax before big-pickle", () => {
|
||||
// given - sisyphus-junior agent requirement
|
||||
const sisyphusJunior = AGENT_MODEL_REQUIREMENTS["sisyphus-junior"]
|
||||
|
||||
// when - locating the OpenAI fallback entry
|
||||
const openAiFallback = sisyphusJunior.fallbackChain.find((entry) => entry.providers.includes("openai"))
|
||||
const openAiFallbackIndex = sisyphusJunior.fallbackChain.findIndex((entry) => entry.providers.includes("openai"))
|
||||
const minimaxIndex = sisyphusJunior.fallbackChain.findIndex((entry) => entry.model === "minimax-m2.7")
|
||||
const bigPickleIndex = sisyphusJunior.fallbackChain.findIndex((entry) => entry.model === "big-pickle")
|
||||
|
||||
// then
|
||||
@@ -245,7 +250,8 @@ describe("AGENT_MODEL_REQUIREMENTS", () => {
|
||||
variant: "medium",
|
||||
})
|
||||
expect(openAiFallbackIndex).toBeGreaterThan(-1)
|
||||
expect(bigPickleIndex).toBeGreaterThan(openAiFallbackIndex)
|
||||
expect(minimaxIndex).toBeGreaterThan(openAiFallbackIndex)
|
||||
expect(bigPickleIndex).toBeGreaterThan(minimaxIndex)
|
||||
})
|
||||
|
||||
test("hephaestus supports openai, github-copilot, venice, and opencode providers", () => {
|
||||
@@ -437,10 +443,10 @@ describe("CATEGORY_MODEL_REQUIREMENTS", () => {
|
||||
const writing = CATEGORY_MODEL_REQUIREMENTS["writing"]
|
||||
|
||||
// when - accessing writing requirement
|
||||
// then - fallbackChain: gemini-3-flash -> kimi-k2.5 -> claude-sonnet-4-6
|
||||
// then - fallbackChain: gemini-3-flash -> kimi-k2.5 -> claude-sonnet-4-6 -> minimax-m2.7
|
||||
expect(writing).toBeDefined()
|
||||
expect(writing.fallbackChain).toBeArray()
|
||||
expect(writing.fallbackChain).toHaveLength(3)
|
||||
expect(writing.fallbackChain).toHaveLength(4)
|
||||
|
||||
const primary = writing.fallbackChain[0]
|
||||
expect(primary.model).toBe("gemini-3-flash")
|
||||
@@ -453,6 +459,10 @@ describe("CATEGORY_MODEL_REQUIREMENTS", () => {
|
||||
const third = writing.fallbackChain[2]
|
||||
expect(third.model).toBe("claude-sonnet-4-6")
|
||||
expect(third.providers[0]).toBe("anthropic")
|
||||
|
||||
const fourth = writing.fallbackChain[3]
|
||||
expect(fourth.model).toBe("minimax-m2.7")
|
||||
expect(fourth.providers[0]).toBe("opencode-go")
|
||||
})
|
||||
|
||||
test("all 8 categories have valid fallbackChain arrays", () => {
|
||||
|
||||
@@ -72,8 +72,8 @@ export const AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
|
||||
},
|
||||
librarian: {
|
||||
fallbackChain: [
|
||||
{ providers: ["opencode-go"], model: "minimax-m2.5" },
|
||||
{ providers: ["opencode"], model: "minimax-m2.5-free" },
|
||||
{ providers: ["opencode-go"], model: "minimax-m2.7" },
|
||||
{ providers: ["opencode"], model: "minimax-m2.7-highspeed" },
|
||||
{ providers: ["anthropic", "opencode"], model: "claude-haiku-4-5" },
|
||||
{ providers: ["opencode"], model: "gpt-5-nano" },
|
||||
],
|
||||
@@ -81,8 +81,8 @@ export const AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
|
||||
explore: {
|
||||
fallbackChain: [
|
||||
{ providers: ["github-copilot"], model: "grok-code-fast-1" },
|
||||
{ providers: ["opencode-go"], model: "minimax-m2.5" },
|
||||
{ providers: ["opencode"], model: "minimax-m2.5-free" },
|
||||
{ providers: ["opencode-go"], model: "minimax-m2.7-highspeed" },
|
||||
{ providers: ["opencode"], model: "minimax-m2.7" },
|
||||
{ providers: ["anthropic", "opencode"], model: "claude-haiku-4-5" },
|
||||
{ providers: ["opencode"], model: "gpt-5-nano" },
|
||||
],
|
||||
@@ -159,6 +159,7 @@ export const AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
|
||||
model: "gpt-5.4",
|
||||
variant: "medium",
|
||||
},
|
||||
{ providers: ["opencode-go"], model: "minimax-m2.7" },
|
||||
],
|
||||
},
|
||||
"sisyphus-junior": {
|
||||
@@ -170,6 +171,7 @@ export const AGENT_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
|
||||
model: "gpt-5.4",
|
||||
variant: "medium",
|
||||
},
|
||||
{ providers: ["opencode-go"], model: "minimax-m2.7" },
|
||||
{ providers: ["opencode"], model: "big-pickle" },
|
||||
],
|
||||
},
|
||||
@@ -263,7 +265,7 @@ export const CATEGORY_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
|
||||
providers: ["google", "github-copilot", "opencode"],
|
||||
model: "gemini-3-flash",
|
||||
},
|
||||
{ providers: ["opencode-go"], model: "minimax-m2.5" },
|
||||
{ providers: ["opencode-go"], model: "minimax-m2.7" },
|
||||
{ providers: ["opencode"], model: "gpt-5-nano" },
|
||||
],
|
||||
},
|
||||
@@ -283,6 +285,7 @@ export const CATEGORY_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
|
||||
providers: ["google", "github-copilot", "opencode"],
|
||||
model: "gemini-3-flash",
|
||||
},
|
||||
{ providers: ["opencode-go"], model: "minimax-m2.7" },
|
||||
],
|
||||
},
|
||||
"unspecified-high": {
|
||||
@@ -325,6 +328,7 @@ export const CATEGORY_MODEL_REQUIREMENTS: Record<string, ModelRequirement> = {
|
||||
providers: ["anthropic", "github-copilot", "opencode"],
|
||||
model: "claude-sonnet-4-6",
|
||||
},
|
||||
{ providers: ["opencode-go"], model: "minimax-m2.7" },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user