mirror of https://github.com/openclaw/openclaw.git
119 lines
3.7 KiB
TypeScript
119 lines
3.7 KiB
TypeScript
import { resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agents/agent-scope.js";
|
|
import { resolveAgentIdentity } from "../agents/identity.js";
|
|
import { loadAgentIdentity } from "../commands/agents.config.js";
|
|
import type { OpenClawConfig } from "../config/config.js";
|
|
import { normalizeAgentId } from "../routing/session-key.js";
|
|
import { coerceIdentityValue } from "../shared/assistant-identity-values.js";
|
|
import {
|
|
isAvatarHttpUrl,
|
|
isAvatarImageDataUrl,
|
|
looksLikeAvatarPath,
|
|
} from "../shared/avatar-policy.js";
|
|
|
|
const MAX_ASSISTANT_NAME = 50;
|
|
const MAX_ASSISTANT_AVATAR = 200;
|
|
const MAX_ASSISTANT_EMOJI = 16;
|
|
|
|
export const DEFAULT_ASSISTANT_IDENTITY: AssistantIdentity = {
|
|
agentId: "main",
|
|
name: "Assistant",
|
|
avatar: "A",
|
|
};
|
|
|
|
export type AssistantIdentity = {
|
|
agentId: string;
|
|
name: string;
|
|
avatar: string;
|
|
emoji?: string;
|
|
};
|
|
|
|
function isAvatarUrl(value: string): boolean {
|
|
return isAvatarHttpUrl(value) || isAvatarImageDataUrl(value);
|
|
}
|
|
|
|
function normalizeAvatarValue(value: string | undefined): string | undefined {
|
|
if (!value) {
|
|
return undefined;
|
|
}
|
|
const trimmed = value.trim();
|
|
if (!trimmed) {
|
|
return undefined;
|
|
}
|
|
if (isAvatarUrl(trimmed)) {
|
|
return trimmed;
|
|
}
|
|
if (looksLikeAvatarPath(trimmed)) {
|
|
return trimmed;
|
|
}
|
|
if (!/\s/.test(trimmed) && trimmed.length <= 4) {
|
|
return trimmed;
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
function normalizeEmojiValue(value: string | undefined): string | undefined {
|
|
if (!value) {
|
|
return undefined;
|
|
}
|
|
const trimmed = value.trim();
|
|
if (!trimmed) {
|
|
return undefined;
|
|
}
|
|
if (trimmed.length > MAX_ASSISTANT_EMOJI) {
|
|
return undefined;
|
|
}
|
|
let hasNonAscii = false;
|
|
for (let i = 0; i < trimmed.length; i += 1) {
|
|
if (trimmed.charCodeAt(i) > 127) {
|
|
hasNonAscii = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!hasNonAscii) {
|
|
return undefined;
|
|
}
|
|
if (isAvatarUrl(trimmed) || looksLikeAvatarPath(trimmed)) {
|
|
return undefined;
|
|
}
|
|
return trimmed;
|
|
}
|
|
|
|
export function resolveAssistantIdentity(params: {
|
|
cfg: OpenClawConfig;
|
|
agentId?: string | null;
|
|
workspaceDir?: string | null;
|
|
}): AssistantIdentity {
|
|
const agentId = normalizeAgentId(params.agentId ?? resolveDefaultAgentId(params.cfg));
|
|
const workspaceDir = params.workspaceDir ?? resolveAgentWorkspaceDir(params.cfg, agentId);
|
|
const configAssistant = params.cfg.ui?.assistant;
|
|
const agentIdentity = resolveAgentIdentity(params.cfg, agentId);
|
|
const fileIdentity = workspaceDir ? loadAgentIdentity(workspaceDir) : null;
|
|
|
|
const name =
|
|
coerceIdentityValue(configAssistant?.name, MAX_ASSISTANT_NAME) ??
|
|
coerceIdentityValue(agentIdentity?.name, MAX_ASSISTANT_NAME) ??
|
|
coerceIdentityValue(fileIdentity?.name, MAX_ASSISTANT_NAME) ??
|
|
DEFAULT_ASSISTANT_IDENTITY.name;
|
|
|
|
const avatarCandidates = [
|
|
coerceIdentityValue(configAssistant?.avatar, MAX_ASSISTANT_AVATAR),
|
|
coerceIdentityValue(agentIdentity?.avatar, MAX_ASSISTANT_AVATAR),
|
|
coerceIdentityValue(agentIdentity?.emoji, MAX_ASSISTANT_AVATAR),
|
|
coerceIdentityValue(fileIdentity?.avatar, MAX_ASSISTANT_AVATAR),
|
|
coerceIdentityValue(fileIdentity?.emoji, MAX_ASSISTANT_AVATAR),
|
|
];
|
|
const avatar =
|
|
avatarCandidates.map((candidate) => normalizeAvatarValue(candidate)).find(Boolean) ??
|
|
DEFAULT_ASSISTANT_IDENTITY.avatar;
|
|
|
|
const emojiCandidates = [
|
|
coerceIdentityValue(agentIdentity?.emoji, MAX_ASSISTANT_EMOJI),
|
|
coerceIdentityValue(fileIdentity?.emoji, MAX_ASSISTANT_EMOJI),
|
|
coerceIdentityValue(agentIdentity?.avatar, MAX_ASSISTANT_EMOJI),
|
|
coerceIdentityValue(fileIdentity?.avatar, MAX_ASSISTANT_EMOJI),
|
|
];
|
|
const emoji = emojiCandidates.map((candidate) => normalizeEmojiValue(candidate)).find(Boolean);
|
|
|
|
return { agentId, name, avatar, emoji };
|
|
}
|