agent-ecosystem/src/renderer/utils/agentMessageFormatting.ts
iliya 42171e239d feat: enhance task management and UI with new filters and line counting
- Implemented line counting for NotebookEdit and Bash commands in MemberStatsComputer to improve task tracking accuracy.
- Updated TeamDataService to include kanban column information for tasks, enhancing task visibility.
- Refactored Sidebar and GlobalTaskList components to support task filtering by status and team, improving user interaction.
- Introduced TaskFiltersPopover for better task filtering options, allowing users to filter tasks by status and unread comments.
- Enhanced UI components for better responsiveness and user experience in task management.

These changes aim to improve task management efficiency and enhance collaboration within teams.
2026-02-22 23:36:11 +02:00

127 lines
3.3 KiB
TypeScript

import { MESSAGE_REPLY_TAG } from '@shared/constants/agentBlocks';
type StructuredAgentMessage = Record<string, unknown>;
// ---------------------------------------------------------------------------
// Reply block parsing
// ---------------------------------------------------------------------------
export interface ParsedMessageReply {
agentName: string;
originalText: string;
replyText: string;
}
const REPLY_BLOCK_RE = new RegExp(
'```' +
MESSAGE_REPLY_TAG +
'\\nReply on @([\\w-]+) original message with text "([\\s\\S]*?)", here is answer: "([\\s\\S]*?)"\\n```'
);
/**
* Parses a message_reply_for_agent block from content.
* Returns null if no reply block is found.
*/
export function parseMessageReply(content: string): ParsedMessageReply | null {
const match = REPLY_BLOCK_RE.exec(content);
if (!match) return null;
return {
agentName: match[1],
originalText: match[2],
replyText: match[3],
};
}
/**
* Builds a reply block string for sending.
*/
export function buildReplyBlock(
agentName: string,
originalText: string,
replyText: string
): string {
const tag = MESSAGE_REPLY_TAG;
return [
'```' + tag,
`Reply on @${agentName} original message with text "${originalText}", here is answer: "${replyText}"`,
'```',
].join('\n');
}
const NOISE_TYPES = new Set([
'idle_notification',
'shutdown_approved',
'teammate_terminated',
'shutdown_request',
]);
const TYPE_LABELS: Record<string, string> = {
idle_notification: 'Idle',
shutdown_approved: 'Shutdown confirmed',
teammate_terminated: 'Terminated',
shutdown_request: 'Shutdown requested',
shutdown_response: 'Shutdown response',
message: 'Message',
broadcast: 'Broadcast',
};
export function parseStructuredAgentMessage(content: string): StructuredAgentMessage | null {
const trimmed = content.trim();
if (!trimmed.startsWith('{') && !trimmed.startsWith('[')) {
return null;
}
try {
const parsed = JSON.parse(trimmed) as unknown;
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
return parsed as StructuredAgentMessage;
}
} catch {
return null;
}
return null;
}
export function getMessageTypeLabel(type: string | null): string | null {
if (!type) {
return null;
}
return TYPE_LABELS[type] ?? type;
}
function getStringField(value: unknown): string | null {
return typeof value === 'string' && value.trim() !== '' ? value : null;
}
export function detectOperationalNoise(content: string, teammateId: string): string | null {
const trimmed = content.trim();
const parsed = parseStructuredAgentMessage(trimmed);
const type = getStringField(parsed?.type);
if (type && NOISE_TYPES.has(type)) {
const message = getStringField(parsed?.message);
return message ?? getMessageTypeLabel(type) ?? type;
}
if (teammateId === 'system') {
return trimmed.length < 200 ? trimmed : null;
}
return null;
}
export function getStructuredMessageSummary(parsed: StructuredAgentMessage): string {
const explicitSummary = getStringField(parsed.summary);
if (explicitSummary) {
return explicitSummary;
}
const message = getStringField(parsed.message);
if (message) {
return message;
}
const type = getStringField(parsed.type);
return getMessageTypeLabel(type) ?? 'Structured message';
}