File size: 5,323 Bytes
79b2fcc
 
 
 
 
 
 
 
 
 
 
9d1a532
5ddd727
9d1a532
79b2fcc
 
 
 
 
fff300b
79b2fcc
 
901a405
 
 
 
 
 
 
 
 
 
 
 
 
 
d81613f
901a405
 
 
d81613f
 
 
 
 
901a405
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d81613f
901a405
 
 
 
 
d81613f
 
 
901a405
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79b2fcc
 
 
 
5ddd727
901a405
 
 
5ddd727
 
 
 
 
 
79b2fcc
3b6a2ca
79b2fcc
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3b6a2ca
79b2fcc
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
import { Box, Typography } from '@mui/material';
import { keyframes } from '@mui/system';
import { useAgentStore, type ActivityStatus } from '@/store/agentStore';

const shimmer = keyframes`
  0% { background-position: -100% center; }
  50% { background-position: 200% center; }
  100% { background-position: -100% center; }
`;

const TOOL_LABELS: Record<string, string> = {
  sandbox_create: 'Creating sandbox for code development, this might take 1-2 minutes',
  bash: 'Running command in sandbox',
  hf_jobs: 'Running a GPU job, this might take a while',
  hf_repo_files: 'Uploading file',
  hf_repo_git: 'Git operation',
  hf_inspect_dataset: 'Inspecting dataset',
  hf_search: 'Searching',
  plan_tool: 'Planning',
  research: 'Researching',
};

/** Format raw research log into a clean status label. */
function formatResearchStatus(raw: string): string {
  const s = raw.replace(/^▸\s*/, '');
  const jsonStart = s.indexOf('{');
  const toolName = jsonStart > 0 ? s.slice(0, jsonStart).trim() : s.trim();
  let args: Record<string, string> = {};
  if (jsonStart > 0) {
    const jsonStr = s.slice(jsonStart);
    try {
      const parsed = JSON.parse(jsonStr);
      for (const [k, v] of Object.entries(parsed)) {
        if (typeof v === 'string') args[k] = v;
      }
    } catch {
      // JSON is likely truncated — extract complete "key": "value" pairs
      for (const m of jsonStr.matchAll(/"(\w+)":\s*"([^"]*)"/g)) {
        args[m[1]] = m[2];
      }
      // Also try to extract a truncated value for known keys if not found yet
      if (!args.query && !args.arxiv_id) {
        const partial = jsonStr.match(/"(query|arxiv_id)":\s*"([^"]*)/);
        if (partial) args[partial[1]] = partial[2];
      }
    }
  }

  if (toolName === 'github_find_examples') {
    const d = (args.keyword) || (args.repo);
    return d ? `Finding examples: ${d}` : 'Finding examples';
  }
  if (toolName === 'github_read_file') {
    const f = ((args.path) || '').split('/').pop();
    return f ? `Reading ${f}` : 'Reading file';
  }
  if (toolName === 'explore_hf_docs') {
    const d = (args.endpoint) || (args.query);
    return d ? `Exploring docs: ${d}` : 'Exploring docs';
  }
  if (toolName === 'fetch_hf_docs') {
    const p = ((args.url) || '').split('/').pop()?.replace(/\.md$/, '');
    return p ? `Reading docs: ${p}` : 'Fetching docs';
  }
  if (toolName === 'hf_inspect_dataset') {
    const d = args.dataset as string;
    return d ? `Inspecting dataset: ${d}` : 'Inspecting dataset';
  }
  if (toolName === 'hf_papers') {
    const op = args.operation as string;
    const detail = (args.query) || (args.arxiv_id) || (args.positive_ids);
    const opLabels: Record<string, string> = {
      trending: 'Browsing trending papers',
      search: 'Searching papers',
      paper_details: 'Reading paper details',
      read_paper: 'Reading paper',
      citation_graph: 'Tracing citations',
      snippet_search: 'Searching paper passages',
      recommend: 'Finding similar papers',
      find_datasets: 'Finding paper datasets',
      find_models: 'Finding paper models',
      find_collections: 'Finding paper collections',
      find_all_resources: 'Finding paper resources',
    };
    const base = (op && opLabels[op]) || 'Searching papers';
    return detail ? `${base}: ${detail}` : base;
  }
  if (toolName === 'find_hf_api') {
    const d = (args.query) || (args.tag);
    return d ? `Finding API: ${d}` : 'Finding API endpoints';
  }
  if (toolName === 'hf_repo_files') {
    const d = (args.repo_id) || (args.repo);
    return d ? `Reading ${d} files` : 'Reading repo files';
  }
  return 'Researching';
}

function statusLabel(status: ActivityStatus): string {
  switch (status.type) {
    case 'thinking': return 'Thinking';
    case 'streaming': return 'Writing';
    case 'tool': {
      if (status.toolName === 'research' && status.description) {
        return formatResearchStatus(status.description);
      }
      const base = status.description || TOOL_LABELS[status.toolName] || `Running ${status.toolName}`;
      if (status.toolName === 'bash' && status.description && /install/i.test(status.description)) {
        return `${base} — this can take a few minutes, sit tight`;
      }
      return base;
    }
    case 'waiting-approval': return 'Waiting for approval';
    case 'cancelled': return 'What should the agent do instead?';
    default: return '';
  }
}

export default function ActivityStatusBar() {
  const activityStatus = useAgentStore(s => s.activityStatus);

  if (activityStatus.type === 'idle') return null;

  const label = statusLabel(activityStatus);

  return (
    <Box sx={{ px: 2, py: 0.5, minHeight: 28, display: 'flex', alignItems: 'center' }}>
      <Typography
        sx={{
          fontFamily: 'monospace',
          fontSize: '0.72rem',
          fontWeight: 500,
          letterSpacing: '0.02em',
          background: 'linear-gradient(90deg, var(--muted-text) 30%, var(--text) 50%, var(--muted-text) 70%)',
          backgroundSize: '250% 100%',
          backgroundClip: 'text',
          WebkitBackgroundClip: 'text',
          WebkitTextFillColor: 'transparent',
          animation: `${shimmer} 4s ease-in-out infinite`,
        }}
      >
        {label}{activityStatus.type !== 'cancelled' && '…'}
      </Typography>
    </Box>
  );
}