Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions frontend/src/v2/__tests__/V2Inspector.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,43 @@ describe('V2Inspector', () => {
expect(screen.queryByRole('tab')).not.toBeInTheDocument();
});

test('uses the resolved agent label while matching attention under an old profile ID', () => {
renderInspector({ detail: { ...detail, agents: [{
agentName: 'scout', instanceId: 'default', displayName: 'Scout',
profile: { displayName: 'u1a9e5ecede' },
}] } as any, attentionItems: [{
id: 'legacy-request', kind: 'decision', title: 'Legacy request',
actorName: 'u1a9e5ecede', podId: 'pod-1',
}] });

expect(screen.getAllByText('Scout')).toHaveLength(2);
expect(screen.getByRole('button', { name: /Scout needs you · Legacy request/ })).toBeInTheDocument();
expect(screen.queryByText('u1a9e5ecede')).not.toBeInTheDocument();
});

test('does not assign anonymous attention to an agent without a profile name', () => {
renderInspector({ detail: { ...detail, agents: [{
agentName: 'scout', instanceId: 'default', displayName: 'Scout',
}] } as any, attentionItems: [{
id: 'human-question', kind: 'decision', title: 'Human question', podId: 'pod-1',
}] });

expect(screen.getByText('Human question')).toBeInTheDocument();
expect(screen.getByRole('button', { name: /Scout idle/ })).toBeInTheDocument();
});

test('does not assign an unowned active task to an agent without a profile name', async () => {
mockGet.mockImplementation((url: string) => Promise.resolve(url === '/api/v1/tasks/pod-1'
? { tasks: [{ taskId: 'TASK-1', title: 'Unowned work', status: 'claimed' }] }
: { items: [], total: 0 }));
renderInspector({ detail: { ...detail, agents: [{
agentName: 'scout', instanceId: 'default', displayName: 'Scout',
}] } as any, attentionCount: 0, attentionItems: [] });

await screen.findByText('no files yet');
expect(screen.getByRole('button', { name: /Scout idle/ })).toBeInTheDocument();
});

test('uses the short lowercase room name in the single-line agents label', () => {
renderInspector({ detail: { ...detail, pod: { ...detail.pod, name: 'Sharpen · decision loop' } } as any });

Expand Down
17 changes: 14 additions & 3 deletions frontend/src/v2/components/V2Inspector.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,9 @@ const isWorkingTask = (task: TaskItem): boolean => (


const labelForAgent = (agent: V2Agent): string => (
agent.profile?.displayName || agent.displayName || agent.agentName
// The roster's displayName is resolved from the agent User identity. A
// profile name may be an older generated ID, so it is only a fallback.
agent.displayName || agent.profile?.displayName || agent.agentName
);

// Pods often carry a longer descriptive title, while the compact inspector
Expand All @@ -58,12 +60,21 @@ const shortRoomName = (name: string): string => (
);

const matchesAgent = (agent: V2Agent, value?: string): boolean => {
const candidate = (value || '').toLowerCase();
const candidate = (value || '').trim().toLowerCase();
if (!candidate) return false;
return candidate === (agent.agentName || '').toLowerCase()
|| candidate === labelForAgent(agent).toLowerCase()
|| candidate === (agent.profile?.displayName || '').toLowerCase()
|| candidate === (agent.instanceId || '').toLowerCase();
};

const labelForAttentionActor = (agents: V2Agent[], actorName: string): string => {
const legacyAgent = agents.find((agent) => (
agent.profile?.displayName?.toLowerCase() === actorName.toLowerCase()
));
return legacyAgent ? labelForAgent(legacyAgent) : actorName;
};

const agentState = (
agent: V2Agent,
attention: V2AttentionItem[],
Expand Down Expand Up @@ -185,7 +196,7 @@ const V2Inspector: React.FC<V2InspectorProps> = ({ detail, currentUserId, onPodC
{attention.map((item) => (
<button key={`${item.kind}:${item.id}`} type="button" className="v2-workspace-inspector__attention" onClick={() => openAttention(item)}>
<span>{item.title}</span>
{item.actorName && <span className="v2-workspace-inspector__actor">{item.actorName}</span>}
{item.actorName && <span className="v2-workspace-inspector__actor">{labelForAttentionActor(agents, item.actorName)}</span>}
</button>
))}
</div>
Expand Down
Loading