feat: add CLI backend fallback

This commit is contained in:
Peter Steinberger
2026-01-10 23:31:25 +00:00
parent 07be761779
commit d8f1124d59
18 changed files with 1448 additions and 472 deletions

110
src/agents/cli-backends.ts Normal file
View File

@@ -0,0 +1,110 @@
import type { ClawdbotConfig } from "../config/config.js";
import type { CliBackendConfig } from "../config/types.js";
import { normalizeProviderId } from "./model-selection.js";
export type ResolvedCliBackend = {
id: string;
config: CliBackendConfig;
};
const CLAUDE_MODEL_ALIASES: Record<string, string> = {
opus: "opus",
"opus-4.5": "opus",
"opus-4": "opus",
"claude-opus-4-5": "opus",
"claude-opus-4": "opus",
sonnet: "sonnet",
"sonnet-4.5": "sonnet",
"sonnet-4.1": "sonnet",
"sonnet-4.0": "sonnet",
"claude-sonnet-4-5": "sonnet",
"claude-sonnet-4-1": "sonnet",
"claude-sonnet-4-0": "sonnet",
haiku: "haiku",
"haiku-3.5": "haiku",
"claude-haiku-3-5": "haiku",
};
const DEFAULT_CLAUDE_BACKEND: CliBackendConfig = {
command: "claude",
args: ["-p", "--output-format", "json", "--dangerously-skip-permissions"],
output: "json",
input: "arg",
modelArg: "--model",
modelAliases: CLAUDE_MODEL_ALIASES,
sessionArg: "--session-id",
sessionMode: "always",
sessionIdFields: [
"session_id",
"sessionId",
"conversation_id",
"conversationId",
],
systemPromptArg: "--append-system-prompt",
systemPromptMode: "append",
systemPromptWhen: "first",
clearEnv: ["ANTHROPIC_API_KEY"],
serialize: true,
};
function normalizeBackendKey(key: string): string {
return normalizeProviderId(key);
}
function pickBackendConfig(
config: Record<string, CliBackendConfig>,
normalizedId: string,
): CliBackendConfig | undefined {
for (const [key, entry] of Object.entries(config)) {
if (normalizeBackendKey(key) === normalizedId) return entry;
}
return undefined;
}
function mergeBackendConfig(
base: CliBackendConfig,
override?: CliBackendConfig,
): CliBackendConfig {
if (!override) return { ...base };
return {
...base,
...override,
args: override.args ?? base.args,
env: { ...base.env, ...override.env },
modelAliases: { ...base.modelAliases, ...override.modelAliases },
clearEnv: Array.from(
new Set([...(base.clearEnv ?? []), ...(override.clearEnv ?? [])]),
),
sessionIdFields: override.sessionIdFields ?? base.sessionIdFields,
};
}
export function resolveCliBackendIds(cfg?: ClawdbotConfig): Set<string> {
const ids = new Set<string>([normalizeBackendKey("claude-cli")]);
const configured = cfg?.agents?.defaults?.cliBackends ?? {};
for (const key of Object.keys(configured)) {
ids.add(normalizeBackendKey(key));
}
return ids;
}
export function resolveCliBackendConfig(
provider: string,
cfg?: ClawdbotConfig,
): ResolvedCliBackend | null {
const normalized = normalizeBackendKey(provider);
const configured = cfg?.agents?.defaults?.cliBackends ?? {};
const override = pickBackendConfig(configured, normalized);
if (normalized === "claude-cli") {
const merged = mergeBackendConfig(DEFAULT_CLAUDE_BACKEND, override);
const command = merged.command?.trim();
if (!command) return null;
return { id: normalized, config: { ...merged, command } };
}
if (!override) return null;
const command = override.command?.trim();
if (!command) return null;
return { id: normalized, config: { ...override, command } };
}