Files
clawdbot/src/agents/agent-scope.ts
2026-01-12 11:23:44 +00:00

141 lines
4.6 KiB
TypeScript

import os from "node:os";
import path from "node:path";
import type { ClawdbotConfig } from "../config/config.js";
import { resolveStateDir } from "../config/paths.js";
import {
DEFAULT_AGENT_ID,
normalizeAgentId,
parseAgentSessionKey,
} from "../routing/session-key.js";
import { resolveUserPath } from "../utils.js";
import { DEFAULT_AGENT_WORKSPACE_DIR } from "./workspace.js";
export { resolveAgentIdFromSessionKey } from "../routing/session-key.js";
type AgentEntry = NonNullable<
NonNullable<ClawdbotConfig["agents"]>["list"]
>[number];
type ResolvedAgentConfig = {
name?: string;
workspace?: string;
agentDir?: string;
model?: string;
memorySearch?: AgentEntry["memorySearch"];
humanDelay?: AgentEntry["humanDelay"];
identity?: AgentEntry["identity"];
groupChat?: AgentEntry["groupChat"];
subagents?: AgentEntry["subagents"];
sandbox?: AgentEntry["sandbox"];
tools?: AgentEntry["tools"];
};
let defaultAgentWarned = false;
function listAgents(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 resolveDefaultAgentId(cfg: ClawdbotConfig): string {
const agents = listAgents(cfg);
if (agents.length === 0) return DEFAULT_AGENT_ID;
const defaults = agents.filter((agent) => agent?.default);
if (defaults.length > 1 && !defaultAgentWarned) {
defaultAgentWarned = true;
console.warn(
"Multiple agents marked default=true; using the first entry as default.",
);
}
const chosen = (defaults[0] ?? agents[0])?.id?.trim();
return normalizeAgentId(chosen || DEFAULT_AGENT_ID);
}
export function resolveSessionAgentIds(params: {
sessionKey?: string;
config?: ClawdbotConfig;
}): { defaultAgentId: string; sessionAgentId: string } {
const defaultAgentId = resolveDefaultAgentId(params.config ?? {});
const sessionKey = params.sessionKey?.trim();
const parsed = sessionKey ? parseAgentSessionKey(sessionKey) : null;
const sessionAgentId = parsed?.agentId
? normalizeAgentId(parsed.agentId)
: defaultAgentId;
return { defaultAgentId, sessionAgentId };
}
export function resolveSessionAgentId(params: {
sessionKey?: string;
config?: ClawdbotConfig;
}): string {
return resolveSessionAgentIds(params).sessionAgentId;
}
function resolveAgentEntry(
cfg: ClawdbotConfig,
agentId: string,
): AgentEntry | undefined {
const id = normalizeAgentId(agentId);
return listAgents(cfg).find((entry) => normalizeAgentId(entry.id) === id);
}
export function resolveAgentConfig(
cfg: ClawdbotConfig,
agentId: string,
): ResolvedAgentConfig | undefined {
const id = normalizeAgentId(agentId);
const entry = resolveAgentEntry(cfg, id);
if (!entry) return undefined;
return {
name: typeof entry.name === "string" ? entry.name : undefined,
workspace:
typeof entry.workspace === "string" ? entry.workspace : undefined,
agentDir: typeof entry.agentDir === "string" ? entry.agentDir : undefined,
model: typeof entry.model === "string" ? entry.model : undefined,
memorySearch: entry.memorySearch,
humanDelay: entry.humanDelay,
identity: entry.identity,
groupChat: entry.groupChat,
subagents:
typeof entry.subagents === "object" && entry.subagents
? entry.subagents
: undefined,
sandbox: entry.sandbox,
tools: entry.tools,
};
}
export function resolveAgentWorkspaceDir(cfg: ClawdbotConfig, agentId: string) {
const id = normalizeAgentId(agentId);
const configured = resolveAgentConfig(cfg, id)?.workspace?.trim();
if (configured) return resolveUserPath(configured);
const defaultAgentId = resolveDefaultAgentId(cfg);
if (id === defaultAgentId) {
const fallback = cfg.agents?.defaults?.workspace?.trim();
if (fallback) return resolveUserPath(fallback);
return DEFAULT_AGENT_WORKSPACE_DIR;
}
return path.join(os.homedir(), `clawd-${id}`);
}
export function resolveAgentDir(cfg: ClawdbotConfig, agentId: string) {
const id = normalizeAgentId(agentId);
const configured = resolveAgentConfig(cfg, id)?.agentDir?.trim();
if (configured) return resolveUserPath(configured);
const root = resolveStateDir(process.env, os.homedir);
return path.join(root, "agents", id, "agent");
}
/**
* Resolve the agent directory for the default agent without requiring config.
* Used by onboarding when writing auth profiles before config is fully set up.
*/
export function resolveDefaultAgentDir(): string {
const root = resolveStateDir(process.env, os.homedir);
return path.join(root, "agents", DEFAULT_AGENT_ID, "agent");
}