Files
tutti/packages/agent/gui/shared/utils/agentSessionMentionText.ts
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

118 lines
3.5 KiB
TypeScript

import type { UiLanguage } from "../../contexts/settings/domain/agentSettings";
import { normalizeAgentTitleText } from "./agentTitleText.ts";
import {
isRichTextMentionHref,
parseRichTextMentionHref
} from "@tutti-os/ui-rich-text/core";
const MARKDOWN_LINK_PATTERN = /\[((?:\\.|[^\]\\])*)\]\(([^)\s]+)\)/g;
const MARKDOWN_LABEL_ESCAPE_PATTERN = /\\([\\[\]()])/g;
const SESSION_MENTION_DISPLAY_PREFIX_BY_LANGUAGE: Record<UiLanguage, string> = {
"zh-CN": "@会话 · ",
en: "@session · "
};
const PLAIN_SESSION_AGENT_LABELS = [
"Claude Code",
"Hermes Agent",
"OpenClaw",
"Nexight",
"Hermes",
"Codex"
] as const;
export interface FormatAgentSessionMentionTextOptions {
language?: UiLanguage;
}
export function formatAgentSessionMentionText(
value: string | null | undefined,
options: FormatAgentSessionMentionTextOptions = {}
): string {
const trimmed = value?.trim() ?? "";
if (!trimmed) {
return "";
}
const sessionMentionDisplayPrefix = sessionMentionDisplayPrefixForLanguage(
options.language
);
const withSessionMentionsNormalized = trimmed.replace(
MARKDOWN_LINK_PATTERN,
(fullMatch, rawLabel: string, href: string) => {
const label = unescapeMarkdownLabel(rawLabel);
const mention = parseRichTextMentionHref(href, label);
if (!mention) {
return isRichTextMentionHref(href) ? label : fullMatch;
}
const providerId = mention.providerId.trim().toLowerCase();
const displayLabel = label || mention.label;
if (providerId === "agent-session") {
return formatSessionLabel(displayLabel, sessionMentionDisplayPrefix);
}
if (providerId === "workspace-issue") {
return formatIssueLabel(displayLabel);
}
return displayLabel;
}
);
const normalized = normalizeAgentTitleText(withSessionMentionsNormalized);
if (isPlainSessionMentionText(normalized, sessionMentionDisplayPrefix)) {
return formatSessionLabel(normalized, sessionMentionDisplayPrefix);
}
return normalized;
}
function isPlainSessionMentionText(
value: string,
sessionMentionDisplayPrefix: string
): boolean {
const trimmed = value.trim();
if (
!trimmed.startsWith("@") ||
trimmed.startsWith(sessionMentionDisplayPrefix)
) {
return false;
}
return PLAIN_SESSION_AGENT_LABELS.some((agentLabel) => {
const ampersandSeparator = ` & ${agentLabel}`;
const ampersandIndex = trimmed.indexOf(ampersandSeparator);
if (ampersandIndex > 1) {
const trailing = trimmed.slice(
ampersandIndex + ampersandSeparator.length
);
if (trailing === "" || trailing.startsWith(" ")) {
return true;
}
}
const dottedSeparator = ` · ${agentLabel} · `;
return trimmed.indexOf(dottedSeparator) > 1;
});
}
function sessionMentionDisplayPrefixForLanguage(
language: UiLanguage | undefined
): string {
return SESSION_MENTION_DISPLAY_PREFIX_BY_LANGUAGE[language ?? "en"];
}
function formatSessionLabel(
label: string,
sessionMentionDisplayPrefix: string
): string {
const normalizedLabel = label.trim().replace(/^@+/, "").trim();
return normalizedLabel
? `${sessionMentionDisplayPrefix}${normalizedLabel}`
: label.trim();
}
function formatIssueLabel(label: string): string {
const normalizedLabel = label.trim().replace(/^@+/, "").trim();
return normalizedLabel ? `@Task · ${normalizedLabel}` : label.trim();
}
function unescapeMarkdownLabel(label: string): string {
return label.replace(MARKDOWN_LABEL_ESCAPE_PATTERN, "$1");
}