[Haik]: cleanup: remove dead code and tighten TypeScript types across frontend — delete unused AutoRunLog.tsx, LogEntry.tsx, and clearClipboard(); remove SendMessagePayload, GenerateGroupMetaPayload, SearchHistoryParams, and summarizeToolInput from shared types and agentCardUtils; replace Record<string, any> with Record<string, unknown> in agentsTypes.ts and dashboardClipboard.ts; type getStatusColors param as ClaudeTokens; relocate richEditorUtils.ts from app/components to RichPromptEditor and update imports; narrow summarizeMessage export to module-private in BrowserAgentOverlay; wrap session-change state resets in queueMicrotask to avoid render-during-render warnings

This commit is contained in:
haikdc
2026-04-18 17:58:25 -07:00
parent 89d81dd0aa
commit 4ac8fa2c12
8 changed files with 15 additions and 264 deletions
@@ -1,5 +1,6 @@
import { AgentSession } from '@/shared/state/agentsSlice';
import { parseMcpToolName } from '@/app/pages/AgentChat/toolkit/approvalToolkit/utils';
import { ClaudeTokens } from '@/shared/styles/claudeTokens';
import { getToolDisplayName } from './AgentCardCollapsed/components/getToolDisplayName';
export function formatDuration(createdAt: string, closedAt?: string | null, status?: string): string {
const start = new Date(createdAt).getTime();
@@ -13,55 +14,7 @@ export function formatDuration(createdAt: string, closedAt?: string | null, stat
return `${hours}h ${minutes % 60}m`;
}
export function summarizeToolInput(toolName: string, toolInput: Record<string, any>): string {
const mcp = parseMcpToolName(toolName);
if (mcp.isMcp) {
const keys = Object.keys(toolInput || {});
if (keys.length === 0) return '';
if (keys.length === 1) {
const v = toolInput[keys[0]];
const s = typeof v === 'string' ? v : JSON.stringify(v);
return s.length > 60 ? s.slice(0, 60) + '…' : s;
}
return keys.slice(0, 3).map((k) => {
const v = toolInput[k];
const s = typeof v === 'string' ? v : JSON.stringify(v);
return `${k}: ${s.length > 30 ? s.slice(0, 30) + '…' : s}`;
}).join(' ');
}
switch (toolName) {
case 'Bash':
return toolInput.command || '(command)';
case 'Read':
return toolInput.file_path || toolInput.path || '(file)';
case 'Write':
case 'Edit':
return toolInput.file_path || toolInput.path || '(file)';
case 'Grep':
return `/${toolInput.pattern || ''}/${toolInput.path ? ` in ${toolInput.path}` : ''}`;
case 'Glob':
return toolInput.glob_pattern || toolInput.pattern || '(pattern)';
case 'AskUserQuestion': {
const questions = toolInput.questions;
if (Array.isArray(questions) && questions.length > 0) {
return questions[0].question || questions[0].prompt || questions[0].text || 'Question pending';
}
return 'Question pending';
}
default: {
return toolInput.command || toolInput.file_path || toolInput.path || toolInput.query
|| JSON.stringify(toolInput).slice(0, 60);
}
}
}
export function getToolDisplayName(toolName: string): string {
const mcp = parseMcpToolName(toolName);
if (mcp.isMcp) return mcp.displayName;
return toolName;
}
export function getStatusColors(c: Record<string, any>): Record<string, { color: string; bg: string }> {
export function getStatusColors(c: ClaudeTokens): Record<string, { color: string; bg: string }> {
return {
running: { color: c.status.success, bg: c.status.successBg },
waiting_approval: { color: c.status.warning, bg: c.status.warningBg },
@@ -17,7 +17,7 @@ import { useClaudeTokens } from '@/shared/styles/ThemeContext';
import { OverlayEntry } from './OverlayEntry';
import OverlayActionLog from './OverlayActionLog';
export function summarizeMessage(msg: AgentMessage): OverlayEntry {
function summarizeMessage(msg: AgentMessage): OverlayEntry {
if (msg.role === 'assistant' && typeof msg.content === 'string') {
const trimmed = msg.content.trim();
if (!trimmed) return { type: 'skip', text: '' };
@@ -80,9 +80,11 @@ const BrowserAgentOverlay: React.FC<Props> = ({ session, browserWidth, browserHe
useEffect(() => {
if (session.session_id !== prevSessionId.current) {
prevSessionId.current = session.session_id;
setFadeOut(false);
setHidden(false);
setConfirmStop(false);
queueMicrotask(() => {
setFadeOut(false);
setHidden(false);
setConfirmStop(false);
});
}
}, [session.session_id]);
@@ -1,5 +1,5 @@
import React, { useState, useRef, useCallback, useEffect } from 'react';
import { CommandPickerItem } from '@/app/components/CommandPicker';
import { CommandPickerItem } from './CommandPicker/components/commandPickerTypes';
import {
SKILL_PILL_ATTR,
AttachedSkill,
@@ -9,7 +9,7 @@ import {
detectEditorTrigger,
TriggerState,
EMPTY_TRIGGER,
} from '@/app/components/richEditorUtils';
} from './richEditorUtils';
import { useAppSelector } from '@/shared/hooks';
import { useClaudeTokens } from '@/shared/styles/ThemeContext';
import { RichPromptEditorProps, LINE_HEIGHT, FONT_SIZE } from './richPromptEditorTypes';
@@ -1,61 +0,0 @@
import React from 'react';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import CircularProgress from '@mui/material/CircularProgress';
import CheckCircleOutlineIcon from '@mui/icons-material/CheckCircleOutline';
import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline';
import { AgentMessage } from '@/shared/state/agentsSlice';
import { useClaudeTokens } from '@/shared/styles/ThemeContext';
import { LogEntry } from './LogEntry';
interface AutoRunLogProps {
messages: AgentMessage[];
status: string | null;
logEndRef: React.RefObject<HTMLDivElement | null>;
c: ReturnType<typeof useClaudeTokens>;
}
export const AutoRunLog: React.FC<AutoRunLogProps> = ({ messages, status, logEndRef, c }) => {
const isRunning = status === 'running' || status === 'waiting_approval';
const isDone = status === 'completed' || status === 'stopped';
const isError = status === 'error';
return (
<Box sx={{
flex: 1,
minHeight: 0,
display: 'flex',
flexDirection: 'column',
border: `1px solid ${c.border.subtle}`,
borderRadius: 1,
overflow: 'hidden',
}}>
<Box sx={{
display: 'flex',
alignItems: 'center',
gap: 1,
px: 1.5,
py: 0.75,
bgcolor: c.bg.secondary,
borderBottom: `1px solid ${c.border.subtle}`,
flexShrink: 0,
}}>
{isRunning && <CircularProgress size={12} sx={{ color: '#f59e0b' }} />}
{isDone && <CheckCircleOutlineIcon sx={{ fontSize: 14, color: c.accent.primary }} />}
{isError && <ErrorOutlineIcon sx={{ fontSize: 14, color: '#ef4444' }} />}
<Typography sx={{ fontSize: '0.72rem', fontWeight: 600, color: c.text.muted }}>
{isRunning ? 'Agent running…' : isDone ? 'Agent completed' : isError ? 'Agent error' : 'Execution log'}
</Typography>
<Typography sx={{ fontSize: '0.68rem', color: c.text.ghost, ml: 'auto' }}>
{messages.length} messages
</Typography>
</Box>
<Box sx={{ flex: 1, overflow: 'auto', px: 1.5, py: 1 }}>
{messages.map((msg) => (
<LogEntry key={msg.id} msg={msg} c={c} />
))}
<div ref={logEndRef} />
</Box>
</Box>
);
};
-110
View File
@@ -1,110 +0,0 @@
import React, { useState } from 'react';
import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import Collapse from '@mui/material/Collapse';
import Chip from '@mui/material/Chip';
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
import { AgentMessage } from '@/shared/state/agentsSlice';
import { useClaudeTokens } from '@/shared/styles/ThemeContext';
interface LogEntryProps {
msg: AgentMessage;
c: ReturnType<typeof useClaudeTokens>;
}
export const LogEntry: React.FC<LogEntryProps> = ({ msg, c }) => {
const [open, setOpen] = useState(false);
if (msg.role === 'user') return null;
if (msg.role === 'assistant') {
const text = typeof msg.content === 'string'
? msg.content
: Array.isArray(msg.content)
? msg.content.filter((b: any) => b.type === 'text').map((b: any) => b.text).join('')
: JSON.stringify(msg.content);
if (!text.trim()) return null;
return (
<Box sx={{ mb: 0.5 }}>
<Box
onClick={() => setOpen(!open)}
sx={{ display: 'flex', alignItems: 'center', gap: 0.5, cursor: 'pointer', '&:hover': { opacity: 0.8 } }}
>
<ExpandMoreIcon sx={{ fontSize: 14, color: c.text.ghost, transform: open ? 'rotate(0deg)' : 'rotate(-90deg)', transition: '0.15s' }} />
<Typography sx={{ fontSize: '0.72rem', color: c.text.muted, fontStyle: 'italic', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{text.slice(0, 120)}{text.length > 120 ? '…' : ''}
</Typography>
</Box>
<Collapse in={open}>
<Typography sx={{ fontSize: '0.72rem', color: c.text.secondary, whiteSpace: 'pre-wrap', pl: 2.5, pt: 0.5, fontFamily: c.font.mono, lineHeight: 1.5 }}>
{text}
</Typography>
</Collapse>
</Box>
);
}
if (msg.role === 'tool_call') {
const tc = typeof msg.content === 'object' ? msg.content as Record<string, any> : {};
return (
<Box sx={{ mb: 0.5 }}>
<Box
onClick={() => setOpen(!open)}
sx={{ display: 'flex', alignItems: 'center', gap: 0.5, cursor: 'pointer', '&:hover': { opacity: 0.8 } }}
>
<ExpandMoreIcon sx={{ fontSize: 14, color: c.text.ghost, transform: open ? 'rotate(0deg)' : 'rotate(-90deg)', transition: '0.15s' }} />
<Chip
label={tc.tool || 'tool'}
size="small"
sx={{ height: 18, fontSize: '0.68rem', fontWeight: 600, fontFamily: c.font.mono, bgcolor: c.accent.primary + '20', color: c.accent.primary }}
/>
{tc.input && (
<Typography sx={{ fontSize: '0.68rem', color: c.text.ghost, ml: 0.5, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>
{JSON.stringify(tc.input).slice(0, 80)}
</Typography>
)}
</Box>
<Collapse in={open}>
<Box sx={{ pl: 2.5, pt: 0.5 }}>
<Typography component="pre" sx={{ fontSize: '0.68rem', color: c.text.secondary, fontFamily: c.font.mono, whiteSpace: 'pre-wrap', maxHeight: 200, overflow: 'auto' }}>
{JSON.stringify(tc.input, null, 2)}
</Typography>
</Box>
</Collapse>
</Box>
);
}
if (msg.role === 'tool_result') {
const content = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content);
return (
<Box sx={{ mb: 0.5 }}>
<Box
onClick={() => setOpen(!open)}
sx={{ display: 'flex', alignItems: 'center', gap: 0.5, cursor: 'pointer', '&:hover': { opacity: 0.8 } }}
>
<ExpandMoreIcon sx={{ fontSize: 14, color: c.text.ghost, transform: open ? 'rotate(0deg)' : 'rotate(-90deg)', transition: '0.15s' }} />
<Typography sx={{ fontSize: '0.68rem', color: c.text.ghost }}>
result ({content.length > 60 ? `${content.length} chars` : content.slice(0, 60)})
</Typography>
</Box>
<Collapse in={open}>
<Typography component="pre" sx={{ fontSize: '0.68rem', color: c.text.secondary, fontFamily: c.font.mono, whiteSpace: 'pre-wrap', pl: 2.5, pt: 0.5, maxHeight: 200, overflow: 'auto' }}>
{content}
</Typography>
</Collapse>
</Box>
);
}
if (msg.role === 'system') {
const text = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content);
return (
<Typography sx={{ fontSize: '0.68rem', color: c.text.ghost, fontStyle: 'italic', mb: 0.5 }}>
{text}
</Typography>
);
}
return null;
};
+2 -6
View File
@@ -4,7 +4,7 @@ export interface ClipboardCard {
type: CardType;
id: string;
name: string;
meta: Record<string, any>;
meta: Record<string, unknown>;
x: number;
y: number;
width: number;
@@ -20,8 +20,4 @@ export function setClipboardCards(cards: ClipboardCard[]): void {
export function getClipboardCards(): ClipboardCard[] {
return clipboardCards;
}
export function clearClipboard(): void {
clipboardCards = [];
}
}
+2 -31
View File
@@ -1,7 +1,7 @@
export interface AgentMessage {
id: string;
role: 'user' | 'assistant' | 'tool_call' | 'tool_result' | 'system';
content: any;
content: string | Record<string, unknown>;
timestamp: string;
branch_id: string;
parent_id: string | null;
@@ -16,7 +16,7 @@ export interface ApprovalRequest {
id: string;
session_id: string;
tool_name: string;
tool_input: Record<string, any>;
tool_input: Record<string, unknown>;
created_at: string;
}
@@ -86,20 +86,6 @@ export interface ContextPath {
type: 'file' | 'directory';
}
export interface SendMessagePayload {
sessionId: string;
prompt: string;
mode?: string;
model?: string;
provider?: string;
images?: Array<{ data: string; media_type: string }>;
contextPaths?: Array<ContextPath>;
forcedTools?: string[];
attachedSkills?: Array<{ id: string; name: string; content: string }>;
hidden?: boolean;
selectedBrowserIds?: string[];
}
export interface LaunchAndSendPayload {
draftId: string;
config: AgentConfig;
@@ -115,21 +101,6 @@ export interface LaunchAndSendPayload {
selectedBrowserIds?: string[];
}
export interface GenerateGroupMetaPayload {
sessionId: string;
groupId: string;
toolCalls: Array<{ tool: string; input_summary: string }>;
resultsSummary?: string[];
isRefinement?: boolean;
}
export interface SearchHistoryParams {
q?: string;
limit?: number;
offset?: number;
dashboardId?: string;
}
export interface HistorySession {
id: string;
name: string;