import type { AgentBinding } from "../config/types.js"; import { normalizeAgentId } from "../routing/session-key.js"; import type { RuntimeEnv } from "../runtime.js"; import { defaultRuntime } from "../runtime.js"; import { formatCliCommand } from "../cli/command-format.js"; import { shortenHomePath } from "../utils.js"; import { describeBinding } from "./agents.bindings.js"; import { requireValidConfig } from "./agents.command-shared.js"; import type { AgentSummary } from "./agents.config.js"; import { buildAgentSummaries } from "./agents.config.js"; import { buildProviderStatusIndex, listProvidersForAgent, summarizeBindings, } from "./agents.providers.js"; type AgentsListOptions = { json?: boolean; bindings?: boolean; }; function formatSummary(summary: AgentSummary) { const defaultTag = summary.isDefault ? " (default)" : ""; const header = summary.name && summary.name !== summary.id ? `${summary.id}${defaultTag} (${summary.name})` : `${summary.id}${defaultTag}`; const identityParts = []; if (summary.identityEmoji) identityParts.push(summary.identityEmoji); if (summary.identityName) identityParts.push(summary.identityName); const identityLine = identityParts.length > 0 ? identityParts.join(" ") : null; const identitySource = summary.identitySource === "identity" ? "IDENTITY.md" : summary.identitySource === "config" ? "config" : null; const lines = [`- ${header}`]; if (identityLine) { lines.push(` Identity: ${identityLine}${identitySource ? ` (${identitySource})` : ""}`); } lines.push(` Workspace: ${shortenHomePath(summary.workspace)}`); lines.push(` Agent dir: ${shortenHomePath(summary.agentDir)}`); if (summary.model) lines.push(` Model: ${summary.model}`); lines.push(` Routing rules: ${summary.bindings}`); if (summary.routes?.length) { lines.push(` Routing: ${summary.routes.join(", ")}`); } if (summary.providers?.length) { lines.push(" Providers:"); for (const provider of summary.providers) { lines.push(` - ${provider}`); } } if (summary.bindingDetails?.length) { lines.push(" Routing rules:"); for (const binding of summary.bindingDetails) { lines.push(` - ${binding}`); } } return lines.join("\n"); } export async function agentsListCommand( opts: AgentsListOptions, runtime: RuntimeEnv = defaultRuntime, ) { const cfg = await requireValidConfig(runtime); if (!cfg) return; const summaries = buildAgentSummaries(cfg); const bindingMap = new Map(); for (const binding of cfg.bindings ?? []) { const agentId = normalizeAgentId(binding.agentId); const list = bindingMap.get(agentId) ?? []; list.push(binding as AgentBinding); bindingMap.set(agentId, list); } if (opts.bindings) { for (const summary of summaries) { const bindings = bindingMap.get(summary.id) ?? []; if (bindings.length > 0) { summary.bindingDetails = bindings.map((binding) => describeBinding(binding)); } } } const providerStatus = await buildProviderStatusIndex(cfg); for (const summary of summaries) { const bindings = bindingMap.get(summary.id) ?? []; const routes = summarizeBindings(cfg, bindings); if (routes.length > 0) { summary.routes = routes; } else if (summary.isDefault) { summary.routes = ["default (no explicit rules)"]; } const providerLines = listProvidersForAgent({ summaryIsDefault: summary.isDefault, cfg, bindings, providerStatus, }); if (providerLines.length > 0) summary.providers = providerLines; } if (opts.json) { runtime.log(JSON.stringify(summaries, null, 2)); return; } const lines = ["Agents:", ...summaries.map(formatSummary)]; lines.push("Routing rules map channel/account/peer to an agent. Use --bindings for full rules."); lines.push( `Channel status reflects local config/creds. For live health: ${formatCliCommand("clawdbot channels status --probe")}.`, ); runtime.log(lines.join("\n")); }