diff --git a/src/agents/model-fallback.ts b/src/agents/model-fallback.ts index 2f7e4f556..7fba489d3 100644 --- a/src/agents/model-fallback.ts +++ b/src/agents/model-fallback.ts @@ -59,7 +59,10 @@ function resolveFallbackCandidates(params: { const seen = new Set(); const candidates: ModelCandidate[] = []; - const addCandidate = (candidate: ModelCandidate, enforceAllowlist: boolean) => { + const addCandidate = ( + candidate: ModelCandidate, + enforceAllowlist: boolean, + ) => { if (!candidate.provider || !candidate.model) return; const key = modelKey(candidate.provider, candidate.model); if (seen.has(key)) return; diff --git a/src/agents/model-scan.ts b/src/agents/model-scan.ts index ac5deefbb..a235073e2 100644 --- a/src/agents/model-scan.ts +++ b/src/agents/model-scan.ts @@ -1,13 +1,13 @@ -import { Type } from "@sinclair/typebox"; import { + type Context, complete, getEnvApiKey, getModel, - type Context, type Model, - type Tool, type OpenAICompletionsOptions, + type Tool, } from "@mariozechner/pi-ai"; +import { Type } from "@sinclair/typebox"; const OPENROUTER_MODELS_URL = "https://openrouter.ai/api/v1/models"; const DEFAULT_TIMEOUT_MS = 12_000; @@ -130,9 +130,7 @@ async function fetchOpenRouterModels( const id = typeof obj.id === "string" ? obj.id.trim() : ""; if (!id) return null; const name = - typeof obj.name === "string" && obj.name.trim() - ? obj.name.trim() - : id; + typeof obj.name === "string" && obj.name.trim() ? obj.name.trim() : id; const contextLength = typeof obj.context_length === "number" && @@ -274,7 +272,7 @@ async function mapWithConcurrency( fn: (item: T, index: number) => Promise, ): Promise { const limit = Math.max(1, Math.floor(concurrency)); - const results: R[] = new Array(items.length); + const results = Array.from({ length: items.length }) as R[]; let nextIndex = 0; const worker = async () => { @@ -296,8 +294,7 @@ export async function scanOpenRouterModels( options: OpenRouterScanOptions = {}, ): Promise { const fetchImpl = options.fetchImpl ?? fetch; - const apiKey = - options.apiKey?.trim() || getEnvApiKey("openrouter") || ""; + const apiKey = options.apiKey?.trim() || getEnvApiKey("openrouter") || ""; if (!apiKey) { throw new Error( "Missing OpenRouter API key. Set OPENROUTER_API_KEY to run models scan.", @@ -337,10 +334,7 @@ export async function scanOpenRouterModels( return true; }); - const baseModel = getModel( - "openrouter", - "openrouter/auto", - ) as OpenAIModel; + const baseModel = getModel("openrouter", "openrouter/auto") as OpenAIModel; return mapWithConcurrency(filtered, concurrency, async (entry) => { const model: OpenAIModel = { diff --git a/src/auto-reply/reply/agent-runner.ts b/src/auto-reply/reply/agent-runner.ts index f5eaccf74..5603428ad 100644 --- a/src/auto-reply/reply/agent-runner.ts +++ b/src/auto-reply/reply/agent-runner.ts @@ -1,11 +1,11 @@ import crypto from "node:crypto"; import { lookupContextTokens } from "../../agents/context.js"; import { DEFAULT_CONTEXT_TOKENS } from "../../agents/defaults.js"; +import { runWithModelFallback } from "../../agents/model-fallback.js"; import { queueEmbeddedPiMessage, runEmbeddedPiAgent, } from "../../agents/pi-embedded.js"; -import { runWithModelFallback } from "../../agents/model-fallback.js"; import { loadSessionStore, type SessionEntry, @@ -209,7 +209,9 @@ export async function runReplyAgent(params: { }); if (stripped.didStrip && !didLogHeartbeatStrip) { didLogHeartbeatStrip = true; - logVerbose("Stripped stray HEARTBEAT_OK token from reply"); + logVerbose( + "Stripped stray HEARTBEAT_OK token from reply", + ); } if ( stripped.shouldSkip && @@ -297,7 +299,9 @@ export async function runReplyAgent(params: { }); if (stripped.didStrip && !didLogHeartbeatStrip) { didLogHeartbeatStrip = true; - logVerbose("Stripped stray HEARTBEAT_OK token from reply"); + logVerbose( + "Stripped stray HEARTBEAT_OK token from reply", + ); } if ( stripped.shouldSkip && diff --git a/src/auto-reply/status.ts b/src/auto-reply/status.ts index 1a808e86c..67458ce80 100644 --- a/src/auto-reply/status.ts +++ b/src/auto-reply/status.ts @@ -133,7 +133,8 @@ export function buildStatusMessage(args: StatusArgs): string { defaultProvider: DEFAULT_PROVIDER, defaultModel: DEFAULT_MODEL, }); - const provider = entry?.modelProvider ?? resolved.provider ?? DEFAULT_PROVIDER; + const provider = + entry?.modelProvider ?? resolved.provider ?? DEFAULT_PROVIDER; let model = entry?.model ?? resolved.model ?? DEFAULT_MODEL; let contextTokens = entry?.contextTokens ?? diff --git a/src/cli/models-cli.ts b/src/cli/models-cli.ts index c3e427c40..7637e235b 100644 --- a/src/cli/models-cli.ts +++ b/src/cli/models-cli.ts @@ -64,9 +64,7 @@ export function registerModelsCli(program: Command) { } }); - const aliases = models - .command("aliases") - .description("Manage model aliases"); + const aliases = models.command("aliases").description("Manage model aliases"); aliases .command("list") diff --git a/src/commands/agent.ts b/src/commands/agent.ts index 8b385c303..eba607696 100644 --- a/src/commands/agent.ts +++ b/src/commands/agent.ts @@ -6,13 +6,13 @@ import { DEFAULT_PROVIDER, } from "../agents/defaults.js"; import { loadModelCatalog } from "../agents/model-catalog.js"; +import { runWithModelFallback } from "../agents/model-fallback.js"; import { buildAllowedModelSet, modelKey, resolveConfiguredModelRef, resolveThinkingDefault, } from "../agents/model-selection.js"; -import { runWithModelFallback } from "../agents/model-fallback.js"; import { runEmbeddedPiAgent } from "../agents/pi-embedded.js"; import { buildWorkspaceSkillSnapshot } from "../agents/skills.js"; import { @@ -443,8 +443,7 @@ export async function agentCommand( // Update token+model fields in the session store. if (sessionStore && sessionKey) { const usage = result.meta.agentMeta?.usage; - const modelUsed = - result.meta.agentMeta?.model ?? fallbackModel ?? model; + const modelUsed = result.meta.agentMeta?.model ?? fallbackModel ?? model; const providerUsed = result.meta.agentMeta?.provider ?? fallbackProvider ?? provider; const contextTokens = diff --git a/src/commands/models.ts b/src/commands/models.ts index 9916c0529..06c52b70d 100644 --- a/src/commands/models.ts +++ b/src/commands/models.ts @@ -1,4 +1,3 @@ -export { modelsListCommand, modelsStatusCommand } from "./models/list.js"; export { modelsAliasesAddCommand, modelsAliasesListCommand, @@ -10,5 +9,6 @@ export { modelsFallbacksListCommand, modelsFallbacksRemoveCommand, } from "./models/fallbacks.js"; +export { modelsListCommand, modelsStatusCommand } from "./models/list.js"; export { modelsScanCommand } from "./models/scan.js"; export { modelsSetCommand } from "./models/set.js"; diff --git a/src/commands/models/aliases.ts b/src/commands/models/aliases.ts index 9ed12db3f..fe670e9f0 100644 --- a/src/commands/models/aliases.ts +++ b/src/commands/models/aliases.ts @@ -1,7 +1,4 @@ -import { - CONFIG_PATH_CLAWDBOT, - loadConfig, -} from "../../config/config.js"; +import { CONFIG_PATH_CLAWDBOT, loadConfig } from "../../config/config.js"; import type { RuntimeEnv } from "../../runtime.js"; import { ensureFlagCompatibility, @@ -47,7 +44,7 @@ export async function modelsAliasesAddCommand( const alias = normalizeAlias(aliasRaw); const updated = await updateConfig((cfg) => { const resolved = resolveModelTarget({ raw: modelRaw, cfg }); - const nextAliases = { ...(cfg.agent?.modelAliases ?? {}) }; + const nextAliases = { ...cfg.agent?.modelAliases }; nextAliases[alias] = `${resolved.provider}/${resolved.model}`; return { ...cfg, @@ -68,7 +65,7 @@ export async function modelsAliasesRemoveCommand( ) { const alias = normalizeAlias(aliasRaw); const updated = await updateConfig((cfg) => { - const nextAliases = { ...(cfg.agent?.modelAliases ?? {}) }; + const nextAliases = { ...cfg.agent?.modelAliases }; if (!nextAliases[alias]) { throw new Error(`Alias not found: ${alias}`); } @@ -83,7 +80,10 @@ export async function modelsAliasesRemoveCommand( }); runtime.log(`Updated ${CONFIG_PATH_CLAWDBOT}`); - if (!updated.agent?.modelAliases || Object.keys(updated.agent.modelAliases).length === 0) { + if ( + !updated.agent?.modelAliases || + Object.keys(updated.agent.modelAliases).length === 0 + ) { runtime.log("No aliases configured."); } } diff --git a/src/commands/models/fallbacks.ts b/src/commands/models/fallbacks.ts index ebfe406e7..81f825abb 100644 --- a/src/commands/models/fallbacks.ts +++ b/src/commands/models/fallbacks.ts @@ -1,12 +1,9 @@ -import { - CONFIG_PATH_CLAWDBOT, - loadConfig, -} from "../../config/config.js"; -import type { RuntimeEnv } from "../../runtime.js"; import { buildModelAliasIndex, resolveModelRefFromString, } from "../../agents/model-selection.js"; +import { CONFIG_PATH_CLAWDBOT, loadConfig } from "../../config/config.js"; +import type { RuntimeEnv } from "../../runtime.js"; import { DEFAULT_PROVIDER, ensureFlagCompatibility, @@ -60,8 +57,8 @@ export async function modelsFallbacksAddCommand( aliasIndex, }), ) - .filter(Boolean) - .map((entry) => modelKey(entry!.ref.provider, entry!.ref.model)); + .filter((entry): entry is NonNullable => Boolean(entry)) + .map((entry) => modelKey(entry.ref.provider, entry.ref.model)); if (existingKeys.includes(targetKey)) return cfg; diff --git a/src/commands/models/list.ts b/src/commands/models/list.ts index e74bb8b7d..dd236d247 100644 --- a/src/commands/models/list.ts +++ b/src/commands/models/list.ts @@ -1,22 +1,22 @@ -import chalk from "chalk"; +import { type Api, getEnvApiKey, type Model } from "@mariozechner/pi-ai"; import { discoverAuthStorage, discoverModels, } from "@mariozechner/pi-coding-agent"; -import { getEnvApiKey, type Api, type Model } from "@mariozechner/pi-ai"; +import chalk from "chalk"; import { resolveClawdbotAgentDir } from "../../agents/agent-paths.js"; -import { ensureClawdbotModelsJson } from "../../agents/models-config.js"; import { buildModelAliasIndex, parseModelRef, - resolveModelRefFromString, resolveConfiguredModelRef, + resolveModelRefFromString, } from "../../agents/model-selection.js"; +import { ensureClawdbotModelsJson } from "../../agents/models-config.js"; import { + type ClawdbotConfig, CONFIG_PATH_CLAWDBOT, loadConfig, - type ClawdbotConfig, } from "../../config/config.js"; import { info } from "../../globals.js"; import type { RuntimeEnv } from "../../runtime.js"; @@ -35,7 +35,9 @@ const LOCAL_PAD = 5; const AUTH_PAD = 5; const isRich = (opts?: { json?: boolean; plain?: boolean }) => - Boolean(process.stdout.isTTY && chalk.level > 0 && !opts?.json && !opts?.plain); + Boolean( + process.stdout.isTTY && chalk.level > 0 && !opts?.json && !opts?.plain, + ); const pad = (value: string, size: number) => value.padEnd(size); diff --git a/src/commands/models/scan.ts b/src/commands/models/scan.ts index d02882cd5..13ce6c6f5 100644 --- a/src/commands/models/scan.ts +++ b/src/commands/models/scan.ts @@ -3,9 +3,10 @@ import { discoverAuthStorage } from "@mariozechner/pi-coding-agent"; import { resolveClawdbotAgentDir } from "../../agents/agent-paths.js"; import { - scanOpenRouterModels, type ModelScanResult, + scanOpenRouterModels, } from "../../agents/model-scan.js"; +import { CONFIG_PATH_CLAWDBOT } from "../../config/config.js"; import { warn } from "../../globals.js"; import type { RuntimeEnv } from "../../runtime.js"; import { @@ -14,7 +15,6 @@ import { formatTokenK, updateConfig, } from "./shared.js"; -import { CONFIG_PATH_CLAWDBOT } from "../../config/config.js"; const MODEL_PAD = 42; const CTX_PAD = 8; @@ -27,7 +27,6 @@ const truncate = (value: string, max: number) => { return `${value.slice(0, max - 3)}...`; }; - function sortScanResults(results: ModelScanResult[]): ModelScanResult[] { return results.slice().sort((a, b) => { const aImage = a.image.ok ? 1 : 0; @@ -133,16 +132,20 @@ export async function modelsScanCommand( runtime: RuntimeEnv, ) { const minParams = opts.minParams ? Number(opts.minParams) : undefined; - if (minParams !== undefined && (!Number.isFinite(minParams) || minParams < 0)) { + if ( + minParams !== undefined && + (!Number.isFinite(minParams) || minParams < 0) + ) { throw new Error("--min-params must be >= 0"); } const maxAgeDays = opts.maxAgeDays ? Number(opts.maxAgeDays) : undefined; - if (maxAgeDays !== undefined && (!Number.isFinite(maxAgeDays) || maxAgeDays < 0)) { + if ( + maxAgeDays !== undefined && + (!Number.isFinite(maxAgeDays) || maxAgeDays < 0) + ) { throw new Error("--max-age-days must be >= 0"); } - const maxCandidates = opts.maxCandidates - ? Number(opts.maxCandidates) - : 6; + const maxCandidates = opts.maxCandidates ? Number(opts.maxCandidates) : 6; if (!Number.isFinite(maxCandidates) || maxCandidates <= 0) { throw new Error("--max-candidates must be > 0"); } @@ -151,7 +154,10 @@ export async function modelsScanCommand( throw new Error("--timeout must be > 0"); } const concurrency = opts.concurrency ? Number(opts.concurrency) : undefined; - if (concurrency !== undefined && (!Number.isFinite(concurrency) || concurrency <= 0)) { + if ( + concurrency !== undefined && + (!Number.isFinite(concurrency) || concurrency <= 0) + ) { throw new Error("--concurrency must be > 0"); } @@ -226,9 +232,7 @@ export async function modelsScanCommand( const allowlist = buildAllowlistSet(updated); const allowlistMissing = - allowlist.size > 0 - ? selected.filter((entry) => !allowlist.has(entry)) - : []; + allowlist.size > 0 ? selected.filter((entry) => !allowlist.has(entry)) : []; if (opts.json) { runtime.log( diff --git a/src/commands/models/set.ts b/src/commands/models/set.ts index b3d8f4532..20e500519 100644 --- a/src/commands/models/set.ts +++ b/src/commands/models/set.ts @@ -1,11 +1,13 @@ import { CONFIG_PATH_CLAWDBOT } from "../../config/config.js"; import type { RuntimeEnv } from "../../runtime.js"; -import { buildAllowlistSet, modelKey, resolveModelTarget, updateConfig } from "./shared.js"; +import { + buildAllowlistSet, + modelKey, + resolveModelTarget, + updateConfig, +} from "./shared.js"; -export async function modelsSetCommand( - modelRaw: string, - runtime: RuntimeEnv, -) { +export async function modelsSetCommand(modelRaw: string, runtime: RuntimeEnv) { const updated = await updateConfig((cfg) => { const resolved = resolveModelTarget({ raw: modelRaw, cfg }); const allowlist = buildAllowlistSet(cfg); diff --git a/src/commands/models/shared.ts b/src/commands/models/shared.ts index fedf425b4..347ae06d0 100644 --- a/src/commands/models/shared.ts +++ b/src/commands/models/shared.ts @@ -1,7 +1,4 @@ -import { - DEFAULT_MODEL, - DEFAULT_PROVIDER, -} from "../../agents/defaults.js"; +import { DEFAULT_MODEL, DEFAULT_PROVIDER } from "../../agents/defaults.js"; import { buildModelAliasIndex, modelKey, @@ -9,9 +6,9 @@ import { resolveModelRefFromString, } from "../../agents/model-selection.js"; import { + type ClawdbotConfig, readConfigFileSnapshot, writeConfigFile, - type ClawdbotConfig, } from "../../config/config.js"; export const ensureFlagCompatibility = (opts: { diff --git a/src/cron/isolated-agent.ts b/src/cron/isolated-agent.ts index 0a7def723..66b26b390 100644 --- a/src/cron/isolated-agent.ts +++ b/src/cron/isolated-agent.ts @@ -6,11 +6,11 @@ import { DEFAULT_PROVIDER, } from "../agents/defaults.js"; import { loadModelCatalog } from "../agents/model-catalog.js"; +import { runWithModelFallback } from "../agents/model-fallback.js"; import { resolveConfiguredModelRef, resolveThinkingDefault, } from "../agents/model-selection.js"; -import { runWithModelFallback } from "../agents/model-fallback.js"; import { runEmbeddedPiAgent } from "../agents/pi-embedded.js"; import { buildWorkspaceSkillSnapshot } from "../agents/skills.js"; import { @@ -294,7 +294,10 @@ export async function runCronIsolatedAgentTurn(params: { model: modelOverride, thinkLevel, verboseLevel: - (cronSession.sessionEntry.verboseLevel as "on" | "off" | undefined) ?? + (cronSession.sessionEntry.verboseLevel as + | "on" + | "off" + | undefined) ?? (agentCfg?.verboseDefault as "on" | "off" | undefined), timeoutMs, runId: cronSession.sessionEntry.sessionId, @@ -312,8 +315,7 @@ export async function runCronIsolatedAgentTurn(params: { // Update token+model fields in the session store. { const usage = runResult.meta.agentMeta?.usage; - const modelUsed = - runResult.meta.agentMeta?.model ?? fallbackModel ?? model; + const modelUsed = runResult.meta.agentMeta?.model ?? fallbackModel ?? model; const providerUsed = runResult.meta.agentMeta?.provider ?? fallbackProvider ?? provider; const contextTokens =