Fix worker
This commit is contained in:
@@ -21,7 +21,7 @@ type ActivityFilter = 'all' | 'chat' | 'activity' | 'files' | 'errors';
|
||||
const filters: { value: ActivityFilter; label: string }[] = [
|
||||
{ value: 'all', label: 'All' },
|
||||
{ value: 'chat', label: 'Chat' },
|
||||
{ value: 'activity', label: 'Activity' },
|
||||
{ value: 'activity', label: 'Tools' },
|
||||
{ value: 'files', label: 'Files' },
|
||||
{ value: 'errors', label: 'Errors' },
|
||||
];
|
||||
@@ -68,22 +68,42 @@ export const AgentThread = ({
|
||||
const [replying, setReplying] = useState<string>();
|
||||
const [filter, setFilter] = useState<ActivityFilter>('all');
|
||||
const scrollRef = useRef<HTMLDivElement>(null);
|
||||
const failedMessages = useMemo(
|
||||
() => messages.filter((message) => message.status === 'failed'),
|
||||
const chatMessages = useMemo(
|
||||
() =>
|
||||
messages.filter((message) => {
|
||||
if (message.role === 'system') return false;
|
||||
if (message.role === 'tool') return false;
|
||||
if (message.role === 'assistant' && !message.content.trim()) {
|
||||
return message.status === 'streaming' && agentTurnActive;
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
[agentTurnActive, messages],
|
||||
);
|
||||
const toolMessages = useMemo(
|
||||
() =>
|
||||
messages.filter(
|
||||
(message) => message.role === 'tool' && message.content.trim(),
|
||||
),
|
||||
[messages],
|
||||
);
|
||||
const failedMessages = useMemo(
|
||||
() => chatMessages.filter((message) => message.status === 'failed'),
|
||||
[chatMessages],
|
||||
);
|
||||
const errorEvents = useMemo(
|
||||
() => events.filter((event) => event.level === 'error'),
|
||||
[events],
|
||||
);
|
||||
const visibleMessages =
|
||||
filter === 'activity' || filter === 'files' || filter === 'errors'
|
||||
? filter === 'errors'
|
||||
? failedMessages
|
||||
: []
|
||||
: messages;
|
||||
const visibleEvents =
|
||||
filter === 'chat' || filter === 'files'
|
||||
? []
|
||||
: filter === 'errors'
|
||||
? events.filter((event) => event.level === 'error')
|
||||
: events;
|
||||
: chatMessages;
|
||||
const visibleToolMessages =
|
||||
filter === 'all' || filter === 'activity' ? toolMessages : [];
|
||||
const visibleEvents = filter === 'errors' ? errorEvents : [];
|
||||
const visibleChanges =
|
||||
filter === 'chat' || filter === 'activity' || filter === 'errors'
|
||||
? []
|
||||
@@ -260,15 +280,19 @@ export const AgentThread = ({
|
||||
}
|
||||
>
|
||||
<div className='mb-2 flex items-center justify-between gap-2'>
|
||||
<span className='font-medium capitalize'>{message.role}</span>
|
||||
<Badge
|
||||
variant={
|
||||
message.status === 'failed' ? 'destructive' : 'outline'
|
||||
}
|
||||
className='capitalize'
|
||||
>
|
||||
{message.status}
|
||||
</Badge>
|
||||
<span className='font-medium'>
|
||||
{message.role === 'assistant' ? 'Agent' : 'You'}
|
||||
</span>
|
||||
{message.status === 'failed' || message.status === 'streaming' ? (
|
||||
<Badge
|
||||
variant={
|
||||
message.status === 'failed' ? 'destructive' : 'outline'
|
||||
}
|
||||
className='capitalize'
|
||||
>
|
||||
{message.status === 'streaming' ? 'Working' : 'Failed'}
|
||||
</Badge>
|
||||
) : null}
|
||||
</div>
|
||||
<p className='whitespace-pre-wrap'>
|
||||
{message.content ||
|
||||
@@ -276,6 +300,23 @@ export const AgentThread = ({
|
||||
</p>
|
||||
</article>
|
||||
))}
|
||||
{visibleToolMessages.map((message) => (
|
||||
<article
|
||||
key={message._id}
|
||||
className='border-border bg-background rounded-md border p-3 text-sm'
|
||||
>
|
||||
<div className='mb-2 flex items-center gap-2'>
|
||||
<Terminal className='text-primary size-4' />
|
||||
<span className='font-medium'>Tool</span>
|
||||
{message.status === 'streaming' ? (
|
||||
<Badge variant='outline'>Running</Badge>
|
||||
) : null}
|
||||
</div>
|
||||
<pre className='text-muted-foreground max-h-56 overflow-auto text-xs whitespace-pre-wrap'>
|
||||
{message.content}
|
||||
</pre>
|
||||
</article>
|
||||
))}
|
||||
{visibleChanges.map((change) => (
|
||||
<article
|
||||
key={change._id}
|
||||
@@ -356,6 +397,7 @@ export const AgentThread = ({
|
||||
</article>
|
||||
))}
|
||||
{visibleMessages.length === 0 &&
|
||||
visibleToolMessages.length === 0 &&
|
||||
visibleEvents.length === 0 &&
|
||||
visibleChanges.length === 0 &&
|
||||
(filter !== 'chat' || interactions.length === 0) ? (
|
||||
|
||||
@@ -114,9 +114,14 @@ export const AgentWorkspaceShell = ({ jobId }: { jobId: Id<'agentJobs'> }) => {
|
||||
const response = await fetch(`/api/agent-jobs/${jobId}/agent/status`);
|
||||
if (!response.ok) {
|
||||
setAgentTurnActive(false);
|
||||
const body = await response.text();
|
||||
if (body.includes('workspace is not active')) {
|
||||
setWorkspaceError(body);
|
||||
}
|
||||
return;
|
||||
}
|
||||
const data = (await response.json()) as { active?: boolean };
|
||||
setWorkspaceError(undefined);
|
||||
setAgentTurnActive(Boolean(data.active));
|
||||
}, [jobId]);
|
||||
|
||||
|
||||
@@ -115,6 +115,106 @@ describe('component test harness', () => {
|
||||
expect(onOpenFile).toHaveBeenCalledWith('apps/web/auth.ts');
|
||||
});
|
||||
|
||||
it('keeps the workspace thread focused on user, agent, and tool content', () => {
|
||||
render(
|
||||
<AgentThread
|
||||
jobId='job-1'
|
||||
messages={[
|
||||
{
|
||||
_id: 'message-system',
|
||||
_creationTime: 1,
|
||||
jobId: 'job-1',
|
||||
spoonId: 'spoon-1',
|
||||
ownerId: 'user-1',
|
||||
role: 'system',
|
||||
content: 'Workspace is ready.',
|
||||
status: 'completed',
|
||||
createdAt: 1,
|
||||
updatedAt: 1,
|
||||
} as never,
|
||||
{
|
||||
_id: 'message-empty-assistant',
|
||||
_creationTime: 2,
|
||||
jobId: 'job-1',
|
||||
spoonId: 'spoon-1',
|
||||
ownerId: 'user-1',
|
||||
role: 'assistant',
|
||||
content: '',
|
||||
status: 'completed',
|
||||
createdAt: 2,
|
||||
updatedAt: 2,
|
||||
} as never,
|
||||
{
|
||||
_id: 'message-user',
|
||||
_creationTime: 3,
|
||||
jobId: 'job-1',
|
||||
spoonId: 'spoon-1',
|
||||
ownerId: 'user-1',
|
||||
role: 'user',
|
||||
content: 'Use Authentik as the only provider.',
|
||||
status: 'completed',
|
||||
createdAt: 3,
|
||||
updatedAt: 3,
|
||||
} as never,
|
||||
{
|
||||
_id: 'message-assistant',
|
||||
_creationTime: 4,
|
||||
jobId: 'job-1',
|
||||
spoonId: 'spoon-1',
|
||||
ownerId: 'user-1',
|
||||
role: 'assistant',
|
||||
content: 'I found the Auth.js provider configuration.',
|
||||
status: 'completed',
|
||||
createdAt: 4,
|
||||
updatedAt: 4,
|
||||
} as never,
|
||||
{
|
||||
_id: 'message-tool',
|
||||
_creationTime: 5,
|
||||
jobId: 'job-1',
|
||||
spoonId: 'spoon-1',
|
||||
ownerId: 'user-1',
|
||||
role: 'tool',
|
||||
content: 'rg Authentik',
|
||||
status: 'completed',
|
||||
createdAt: 5,
|
||||
updatedAt: 5,
|
||||
} as never,
|
||||
]}
|
||||
events={[
|
||||
{
|
||||
_id: 'event-info',
|
||||
_creationTime: 1,
|
||||
jobId: 'job-1',
|
||||
level: 'info',
|
||||
phase: 'plan',
|
||||
message: 'Sending message to agent.',
|
||||
createdAt: 1,
|
||||
} as never,
|
||||
]}
|
||||
interactions={[]}
|
||||
workspaceChanges={[]}
|
||||
disabled={false}
|
||||
agentTurnActive={false}
|
||||
onOpenFile={vi.fn()}
|
||||
onOpenDiff={vi.fn()}
|
||||
/>,
|
||||
);
|
||||
|
||||
expect(screen.queryByText('Workspace is ready.')).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.queryByText('Sending message to agent.'),
|
||||
).not.toBeInTheDocument();
|
||||
expect(screen.queryByText('Assistant')).not.toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText('Use Authentik as the only provider.'),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
screen.getByText('I found the Auth.js provider configuration.'),
|
||||
).toBeInTheDocument();
|
||||
expect(screen.getByText('rg Authentik')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders thread workspaces on the canonical thread route', () => {
|
||||
mockUseParams.mockReturnValue({ threadId: 'thread-1' });
|
||||
mockUseQuery.mockReturnValue({
|
||||
|
||||
Reference in New Issue
Block a user