Fix bare Claude model alias resolution in OpenCode converter
normalizeModel() turned bare aliases like 'haiku' into 'anthropic/haiku', which is not a valid OpenCode model ID. This caused ProviderModelNotFoundError when agents using model: haiku (e.g. learnings-researcher, lint) were invoked during workflows like /plan. Add CLAUDE_FAMILY_ALIASES map to resolve haiku, sonnet, and opus to their full model IDs (e.g. anthropic/claude-haiku-4-5). A console.warn alerts during conversion so the map can be updated when new versions release.
This commit is contained in:
@@ -250,8 +250,24 @@ function rewriteClaudePaths(body: string): string {
|
|||||||
.replace(/\.claude\//g, ".opencode/")
|
.replace(/\.claude\//g, ".opencode/")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Bare Claude family aliases used in Claude Code (e.g. `model: haiku`).
|
||||||
|
// Update these when new model generations are released.
|
||||||
|
const CLAUDE_FAMILY_ALIASES: Record<string, string> = {
|
||||||
|
haiku: "claude-haiku-4-5",
|
||||||
|
sonnet: "claude-sonnet-4-5",
|
||||||
|
opus: "claude-opus-4-6",
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeModel(model: string): string {
|
function normalizeModel(model: string): string {
|
||||||
if (model.includes("/")) return model
|
if (model.includes("/")) return model
|
||||||
|
if (CLAUDE_FAMILY_ALIASES[model]) {
|
||||||
|
const resolved = `anthropic/${CLAUDE_FAMILY_ALIASES[model]}`
|
||||||
|
console.warn(
|
||||||
|
`Warning: bare model alias "${model}" mapped to "${resolved}". ` +
|
||||||
|
`Update CLAUDE_FAMILY_ALIASES if a newer version is available.`,
|
||||||
|
)
|
||||||
|
return resolved
|
||||||
|
}
|
||||||
if (/^claude-/.test(model)) return `anthropic/${model}`
|
if (/^claude-/.test(model)) return `anthropic/${model}`
|
||||||
if (/^(gpt-|o1-|o3-)/.test(model)) return `openai/${model}`
|
if (/^(gpt-|o1-|o3-)/.test(model)) return `openai/${model}`
|
||||||
if (/^gemini-/.test(model)) return `google/${model}`
|
if (/^gemini-/.test(model)) return `google/${model}`
|
||||||
|
|||||||
@@ -75,6 +75,35 @@ describe("convertClaudeToOpenCode", () => {
|
|||||||
expect(modelCommand?.model).toBe("openai/gpt-4o")
|
expect(modelCommand?.model).toBe("openai/gpt-4o")
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test("resolves bare Claude model aliases to full IDs", () => {
|
||||||
|
const plugin: ClaudePlugin = {
|
||||||
|
root: "/tmp/plugin",
|
||||||
|
manifest: { name: "fixture", version: "1.0.0" },
|
||||||
|
agents: [
|
||||||
|
{
|
||||||
|
name: "cheap-agent",
|
||||||
|
description: "Agent using bare alias",
|
||||||
|
body: "Test agent.",
|
||||||
|
sourcePath: "/tmp/plugin/agents/cheap-agent.md",
|
||||||
|
model: "haiku",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
commands: [],
|
||||||
|
skills: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
const bundle = convertClaudeToOpenCode(plugin, {
|
||||||
|
agentMode: "subagent",
|
||||||
|
inferTemperature: false,
|
||||||
|
permissions: "none",
|
||||||
|
})
|
||||||
|
|
||||||
|
const agent = bundle.agents.find((a) => a.name === "cheap-agent")
|
||||||
|
expect(agent).toBeDefined()
|
||||||
|
const parsed = parseFrontmatter(agent!.content)
|
||||||
|
expect(parsed.data.model).toBe("anthropic/claude-haiku-4-5")
|
||||||
|
})
|
||||||
|
|
||||||
test("converts hooks into plugin file", async () => {
|
test("converts hooks into plugin file", async () => {
|
||||||
const plugin = await loadClaudePlugin(fixtureRoot)
|
const plugin = await loadClaudePlugin(fixtureRoot)
|
||||||
const bundle = convertClaudeToOpenCode(plugin, {
|
const bundle = convertClaudeToOpenCode(plugin, {
|
||||||
|
|||||||
Reference in New Issue
Block a user