fix: align opencode-zen provider setup

This commit is contained in:
Peter Steinberger
2026-01-10 21:37:38 +01:00
parent 46e00ad5e7
commit 8a194b4abc
16 changed files with 263 additions and 96 deletions

View File

@@ -147,7 +147,7 @@ describe("applyAuthChoice", () => {
expect(result.config.agents?.defaults?.model?.primary).toBe(
"anthropic/claude-opus-4-5",
);
expect(result.config.models?.providers?.["opencode-zen"]).toBeDefined();
expect(result.agentModelOverride).toBe("opencode-zen/claude-opus-4-5");
expect(result.config.models?.providers?.["opencode-zen"]).toBeUndefined();
expect(result.agentModelOverride).toBe("opencode/claude-opus-4-5");
});
});

View File

@@ -710,8 +710,8 @@ export async function applyAuthChoice(params: {
});
await setOpencodeZenApiKey(String(key).trim(), params.agentDir);
nextConfig = applyAuthProfileConfig(nextConfig, {
profileId: "opencode-zen:default",
provider: "opencode-zen",
profileId: "opencode:default",
provider: "opencode",
mode: "api_key",
});
if (params.setDefaultModel) {
@@ -755,7 +755,7 @@ export function resolvePreferredProviderForAuthChoice(
case "minimax":
return "lmstudio";
case "opencode-zen":
return "opencode-zen";
return "opencode";
default:
return undefined;
}

View File

@@ -258,23 +258,10 @@ describe("applyMinimaxApiProviderConfig", () => {
});
describe("applyOpencodeZenProviderConfig", () => {
it("adds opencode-zen provider with correct settings", () => {
const cfg = applyOpencodeZenProviderConfig({});
expect(cfg.models?.providers?.["opencode-zen"]).toMatchObject({
baseUrl: "https://opencode.ai/zen/v1",
apiKey: "opencode-zen",
api: "openai-completions",
});
expect(
cfg.models?.providers?.["opencode-zen"]?.models.length,
).toBeGreaterThan(0);
});
it("adds allowlist entries for fallback models", () => {
it("adds allowlist entry for the default model", () => {
const cfg = applyOpencodeZenProviderConfig({});
const models = cfg.agents?.defaults?.models ?? {};
expect(Object.keys(models)).toContain("opencode-zen/claude-opus-4-5");
expect(Object.keys(models)).toContain("opencode-zen/gpt-5.2");
expect(Object.keys(models)).toContain("opencode/claude-opus-4-5");
});
it("preserves existing alias for the default model", () => {
@@ -282,13 +269,13 @@ describe("applyOpencodeZenProviderConfig", () => {
agents: {
defaults: {
models: {
"opencode-zen/claude-opus-4-5": { alias: "My Opus" },
"opencode/claude-opus-4-5": { alias: "My Opus" },
},
},
},
});
expect(
cfg.agents?.defaults?.models?.["opencode-zen/claude-opus-4-5"]?.alias,
cfg.agents?.defaults?.models?.["opencode/claude-opus-4-5"]?.alias,
).toBe("My Opus");
});
});
@@ -297,7 +284,7 @@ describe("applyOpencodeZenConfig", () => {
it("sets correct primary model", () => {
const cfg = applyOpencodeZenConfig({});
expect(cfg.agents?.defaults?.model?.primary).toBe(
"opencode-zen/claude-opus-4-5",
"opencode/claude-opus-4-5",
);
});

View File

@@ -1,11 +1,7 @@
import type { OAuthCredentials, OAuthProvider } from "@mariozechner/pi-ai";
import { resolveDefaultAgentDir } from "../agents/agent-scope.js";
import { upsertAuthProfile } from "../agents/auth-profiles.js";
import {
getOpencodeZenStaticFallbackModels,
OPENCODE_ZEN_API_BASE_URL,
OPENCODE_ZEN_DEFAULT_MODEL_REF,
} from "../agents/opencode-zen-models.js";
import { OPENCODE_ZEN_DEFAULT_MODEL_REF } from "../agents/opencode-zen-models.js";
import type { ClawdbotConfig } from "../config/config.js";
import type { ModelDefinitionConfig } from "../config/types.js";
@@ -450,10 +446,10 @@ export function applyMinimaxApiConfig(
export async function setOpencodeZenApiKey(key: string, agentDir?: string) {
upsertAuthProfile({
profileId: "opencode-zen:default",
profileId: "opencode:default",
credential: {
type: "api_key",
provider: "opencode-zen",
provider: "opencode",
key,
},
agentDir: agentDir ?? resolveDefaultAgentDir(),
@@ -463,21 +459,8 @@ export async function setOpencodeZenApiKey(key: string, agentDir?: string) {
export function applyOpencodeZenProviderConfig(
cfg: ClawdbotConfig,
): ClawdbotConfig {
const opencodeModels = getOpencodeZenStaticFallbackModels();
const providers = { ...cfg.models?.providers };
providers["opencode-zen"] = {
baseUrl: OPENCODE_ZEN_API_BASE_URL,
apiKey: "opencode-zen",
api: "openai-completions",
models: opencodeModels,
};
// Use the built-in opencode provider from pi-ai; only seed the allowlist alias.
const models = { ...cfg.agents?.defaults?.models };
for (const model of opencodeModels) {
const key = `opencode-zen/${model.id}`;
models[key] = models[key] ?? {};
}
models[OPENCODE_ZEN_DEFAULT_MODEL_REF] = {
...models[OPENCODE_ZEN_DEFAULT_MODEL_REF],
alias: models[OPENCODE_ZEN_DEFAULT_MODEL_REF]?.alias ?? "Opus",
@@ -492,10 +475,6 @@ export function applyOpencodeZenProviderConfig(
models,
},
},
models: {
mode: cfg.models?.mode ?? "merge",
providers,
},
};
}

View File

@@ -337,11 +337,11 @@ export async function runNonInteractiveOnboarding(
nextConfig = applyMinimaxConfig(nextConfig);
} else if (authChoice === "opencode-zen") {
const resolved = await resolveNonInteractiveApiKey({
provider: "opencode-zen",
provider: "opencode",
cfg: baseConfig,
flagValue: opts.opencodeZenApiKey,
flagName: "--opencode-zen-api-key",
envVar: "OPENCODE_ZEN_API_KEY",
envVar: "OPENCODE_API_KEY (or OPENCODE_ZEN_API_KEY)",
runtime,
});
if (!resolved) return;
@@ -349,8 +349,8 @@ export async function runNonInteractiveOnboarding(
await setOpencodeZenApiKey(resolved.key);
}
nextConfig = applyAuthProfileConfig(nextConfig, {
profileId: "opencode-zen:default",
provider: "opencode-zen",
profileId: "opencode:default",
provider: "opencode",
mode: "api_key",
});
nextConfig = applyOpencodeZenConfig(nextConfig);

View File

@@ -7,7 +7,7 @@ import {
} from "./opencode-zen-model-default.js";
describe("applyOpencodeZenModelDefault", () => {
it("sets opencode-zen default when model is unset", () => {
it("sets opencode default when model is unset", () => {
const cfg: ClawdbotConfig = { agents: { defaults: {} } };
const applied = applyOpencodeZenModelDefault(cfg);
expect(applied.changed).toBe(true);
@@ -36,6 +36,15 @@ describe("applyOpencodeZenModelDefault", () => {
expect(applied.next).toEqual(cfg);
});
it("no-ops when already legacy opencode-zen default", () => {
const cfg = {
agents: { defaults: { model: "opencode-zen/claude-opus-4-5" } },
} as ClawdbotConfig;
const applied = applyOpencodeZenModelDefault(cfg);
expect(applied.changed).toBe(false);
expect(applied.next).toEqual(cfg);
});
it("preserves fallbacks when setting primary", () => {
const cfg: ClawdbotConfig = {
agents: {

View File

@@ -1,7 +1,8 @@
import type { ClawdbotConfig } from "../config/config.js";
import type { AgentModelListConfig } from "../config/types.js";
export const OPENCODE_ZEN_DEFAULT_MODEL = "opencode-zen/claude-opus-4-5";
export const OPENCODE_ZEN_DEFAULT_MODEL = "opencode/claude-opus-4-5";
const LEGACY_OPENCODE_ZEN_DEFAULT_MODEL = "opencode-zen/claude-opus-4-5";
function resolvePrimaryModel(
model?: AgentModelListConfig | string,
@@ -18,7 +19,11 @@ export function applyOpencodeZenModelDefault(cfg: ClawdbotConfig): {
changed: boolean;
} {
const current = resolvePrimaryModel(cfg.agents?.defaults?.model)?.trim();
if (current === OPENCODE_ZEN_DEFAULT_MODEL) {
const normalizedCurrent =
current === LEGACY_OPENCODE_ZEN_DEFAULT_MODEL
? OPENCODE_ZEN_DEFAULT_MODEL
: current;
if (normalizedCurrent === OPENCODE_ZEN_DEFAULT_MODEL) {
return { next: cfg, changed: false };
}