209 lines
6.4 KiB
TypeScript
209 lines
6.4 KiB
TypeScript
import {
|
|
resolveAgentDir,
|
|
resolveAgentWorkspaceDir,
|
|
resolveDefaultAgentId,
|
|
} from "../agents/agent-scope.js";
|
|
import type { AgentIdentityFile } from "../agents/identity-file.js";
|
|
import {
|
|
identityHasValues,
|
|
loadAgentIdentityFromWorkspace,
|
|
parseIdentityMarkdown as parseIdentityMarkdownFile,
|
|
} from "../agents/identity-file.js";
|
|
import type { ClawdbotConfig } from "../config/config.js";
|
|
import { normalizeAgentId } from "../routing/session-key.js";
|
|
|
|
export type AgentSummary = {
|
|
id: string;
|
|
name?: string;
|
|
identityName?: string;
|
|
identityEmoji?: string;
|
|
identitySource?: "identity" | "config";
|
|
workspace: string;
|
|
agentDir: string;
|
|
model?: string;
|
|
bindings: number;
|
|
bindingDetails?: string[];
|
|
routes?: string[];
|
|
providers?: string[];
|
|
isDefault: boolean;
|
|
};
|
|
|
|
type AgentEntry = NonNullable<NonNullable<ClawdbotConfig["agents"]>["list"]>[number];
|
|
|
|
export type AgentIdentity = AgentIdentityFile;
|
|
|
|
export function listAgentEntries(cfg: ClawdbotConfig): AgentEntry[] {
|
|
const list = cfg.agents?.list;
|
|
if (!Array.isArray(list)) return [];
|
|
return list.filter((entry): entry is AgentEntry => Boolean(entry && typeof entry === "object"));
|
|
}
|
|
|
|
export function findAgentEntryIndex(list: AgentEntry[], agentId: string): number {
|
|
const id = normalizeAgentId(agentId);
|
|
return list.findIndex((entry) => normalizeAgentId(entry.id) === id);
|
|
}
|
|
|
|
function resolveAgentName(cfg: ClawdbotConfig, agentId: string) {
|
|
const entry = listAgentEntries(cfg).find(
|
|
(agent) => normalizeAgentId(agent.id) === normalizeAgentId(agentId),
|
|
);
|
|
return entry?.name?.trim() || undefined;
|
|
}
|
|
|
|
function resolveAgentModel(cfg: ClawdbotConfig, agentId: string) {
|
|
const entry = listAgentEntries(cfg).find(
|
|
(agent) => normalizeAgentId(agent.id) === normalizeAgentId(agentId),
|
|
);
|
|
if (entry?.model) {
|
|
if (typeof entry.model === "string" && entry.model.trim()) {
|
|
return entry.model.trim();
|
|
}
|
|
if (typeof entry.model === "object") {
|
|
const primary = entry.model.primary?.trim();
|
|
if (primary) return primary;
|
|
}
|
|
}
|
|
const raw = cfg.agents?.defaults?.model;
|
|
if (typeof raw === "string") return raw;
|
|
return raw?.primary?.trim() || undefined;
|
|
}
|
|
|
|
export function parseIdentityMarkdown(content: string): AgentIdentity {
|
|
return parseIdentityMarkdownFile(content);
|
|
}
|
|
|
|
export function loadAgentIdentity(workspace: string): AgentIdentity | null {
|
|
const parsed = loadAgentIdentityFromWorkspace(workspace);
|
|
if (!parsed) return null;
|
|
return identityHasValues(parsed) ? parsed : null;
|
|
}
|
|
|
|
export function buildAgentSummaries(cfg: ClawdbotConfig): AgentSummary[] {
|
|
const defaultAgentId = normalizeAgentId(resolveDefaultAgentId(cfg));
|
|
const configuredAgents = listAgentEntries(cfg);
|
|
const orderedIds =
|
|
configuredAgents.length > 0
|
|
? configuredAgents.map((agent) => normalizeAgentId(agent.id))
|
|
: [defaultAgentId];
|
|
const bindingCounts = new Map<string, number>();
|
|
for (const binding of cfg.bindings ?? []) {
|
|
const agentId = normalizeAgentId(binding.agentId);
|
|
bindingCounts.set(agentId, (bindingCounts.get(agentId) ?? 0) + 1);
|
|
}
|
|
|
|
const ordered = orderedIds.filter((id, index) => orderedIds.indexOf(id) === index);
|
|
|
|
return ordered.map((id) => {
|
|
const workspace = resolveAgentWorkspaceDir(cfg, id);
|
|
const identity = loadAgentIdentity(workspace);
|
|
const configIdentity = configuredAgents.find(
|
|
(agent) => normalizeAgentId(agent.id) === id,
|
|
)?.identity;
|
|
const identityName = identity?.name ?? configIdentity?.name?.trim();
|
|
const identityEmoji = identity?.emoji ?? configIdentity?.emoji?.trim();
|
|
const identitySource = identity
|
|
? "identity"
|
|
: configIdentity && (identityName || identityEmoji)
|
|
? "config"
|
|
: undefined;
|
|
return {
|
|
id,
|
|
name: resolveAgentName(cfg, id),
|
|
identityName,
|
|
identityEmoji,
|
|
identitySource,
|
|
workspace,
|
|
agentDir: resolveAgentDir(cfg, id),
|
|
model: resolveAgentModel(cfg, id),
|
|
bindings: bindingCounts.get(id) ?? 0,
|
|
isDefault: id === defaultAgentId,
|
|
};
|
|
});
|
|
}
|
|
|
|
export function applyAgentConfig(
|
|
cfg: ClawdbotConfig,
|
|
params: {
|
|
agentId: string;
|
|
name?: string;
|
|
workspace?: string;
|
|
agentDir?: string;
|
|
model?: string;
|
|
},
|
|
): ClawdbotConfig {
|
|
const agentId = normalizeAgentId(params.agentId);
|
|
const name = params.name?.trim();
|
|
const list = listAgentEntries(cfg);
|
|
const index = findAgentEntryIndex(list, agentId);
|
|
const base = index >= 0 ? list[index] : { id: agentId };
|
|
const nextEntry: AgentEntry = {
|
|
...base,
|
|
...(name ? { name } : {}),
|
|
...(params.workspace ? { workspace: params.workspace } : {}),
|
|
...(params.agentDir ? { agentDir: params.agentDir } : {}),
|
|
...(params.model ? { model: params.model } : {}),
|
|
};
|
|
const nextList = [...list];
|
|
if (index >= 0) {
|
|
nextList[index] = nextEntry;
|
|
} else {
|
|
if (nextList.length === 0 && agentId !== normalizeAgentId(resolveDefaultAgentId(cfg))) {
|
|
nextList.push({ id: resolveDefaultAgentId(cfg) });
|
|
}
|
|
nextList.push(nextEntry);
|
|
}
|
|
return {
|
|
...cfg,
|
|
agents: {
|
|
...cfg.agents,
|
|
list: nextList,
|
|
},
|
|
};
|
|
}
|
|
|
|
export function pruneAgentConfig(
|
|
cfg: ClawdbotConfig,
|
|
agentId: string,
|
|
): {
|
|
config: ClawdbotConfig;
|
|
removedBindings: number;
|
|
removedAllow: number;
|
|
} {
|
|
const id = normalizeAgentId(agentId);
|
|
const agents = listAgentEntries(cfg);
|
|
const nextAgentsList = agents.filter((entry) => normalizeAgentId(entry.id) !== id);
|
|
const nextAgents = nextAgentsList.length > 0 ? nextAgentsList : undefined;
|
|
|
|
const bindings = cfg.bindings ?? [];
|
|
const filteredBindings = bindings.filter((binding) => normalizeAgentId(binding.agentId) !== id);
|
|
|
|
const allow = cfg.tools?.agentToAgent?.allow ?? [];
|
|
const filteredAllow = allow.filter((entry) => entry !== id);
|
|
|
|
const nextAgentsConfig = cfg.agents
|
|
? { ...cfg.agents, list: nextAgents }
|
|
: nextAgents
|
|
? { list: nextAgents }
|
|
: undefined;
|
|
const nextTools = cfg.tools?.agentToAgent
|
|
? {
|
|
...cfg.tools,
|
|
agentToAgent: {
|
|
...cfg.tools.agentToAgent,
|
|
allow: filteredAllow.length > 0 ? filteredAllow : undefined,
|
|
},
|
|
}
|
|
: cfg.tools;
|
|
|
|
return {
|
|
config: {
|
|
...cfg,
|
|
agents: nextAgentsConfig,
|
|
bindings: filteredBindings.length > 0 ? filteredBindings : undefined,
|
|
tools: nextTools,
|
|
},
|
|
removedBindings: bindings.length - filteredBindings.length,
|
|
removedAllow: allow.length - filteredAllow.length,
|
|
};
|
|
}
|