From b99b204154054ca7d2c3ff40b283b7bbc2d631dd Mon Sep 17 00:00:00 2001 From: Aarjav Jain Date: Tue, 12 May 2026 22:56:20 +0530 Subject: [PATCH] feat(agent): display model name at end of completed chat session MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes #883 Users had no in-UI way to confirm which provider/model handled a given session, which made comparing model behaviour and debugging unexpected output harder than it needed to be. - New SessionCompletedFooter component shows "Session completed with " beneath the last assistant message once status transitions to 'ready' and there is no active chat or agent-URL error - formatSessionCompletedLabel resolves the best human-readable label: Provider.name for LLM targets, " · " for ACP targets, with graceful fallbacks when ACP metadata is partial - Unit tests cover undefined provider, llm targets, full and partial ACP targets, and both fallback paths --- .../entrypoints/sidepanel/index/Chat.tsx | 7 ++ .../index/SessionCompletedFooter.test.ts | 69 +++++++++++++++++++ .../index/SessionCompletedFooter.tsx | 35 ++++++++++ 3 files changed, 111 insertions(+) create mode 100644 packages/browseros-agent/apps/agent/entrypoints/sidepanel/index/SessionCompletedFooter.test.ts create mode 100644 packages/browseros-agent/apps/agent/entrypoints/sidepanel/index/SessionCompletedFooter.tsx diff --git a/packages/browseros-agent/apps/agent/entrypoints/sidepanel/index/Chat.tsx b/packages/browseros-agent/apps/agent/entrypoints/sidepanel/index/Chat.tsx index b860c9a97..3fe480b33 100644 --- a/packages/browseros-agent/apps/agent/entrypoints/sidepanel/index/Chat.tsx +++ b/packages/browseros-agent/apps/agent/entrypoints/sidepanel/index/Chat.tsx @@ -22,6 +22,7 @@ import { ChatError } from './ChatError' import { ChatFooter } from './ChatFooter' import { ChatMessages } from './ChatMessages' import type { ChatMode } from './chatTypes' +import { SessionCompletedFooter } from './SessionCompletedFooter' /** * @public @@ -240,6 +241,12 @@ export const Chat = () => { {chatError && ( )} + {messages.length > 0 && + status === 'ready' && + !chatError && + !agentUrlError && ( + + )} { + it('returns null when provider is undefined', () => { + expect(formatSessionCompletedLabel(undefined)).toBeNull() + }) + + it('returns the display name for an llm provider', () => { + const provider: Provider = { + id: 'p1', + name: 'OpenAI GPT-4', + type: 'openai', + kind: 'llm', + } + expect(formatSessionCompletedLabel(provider)).toBe('OpenAI GPT-4') + }) + + it('joins adapter name and model label for an acp target', () => { + const provider: Provider = { + id: 'a1', + name: 'Claude Code · Sonnet 3.5', + type: 'acp', + kind: 'acp', + agentId: 'a1', + adapterName: 'Claude Code', + modelLabel: 'Sonnet 3.5', + } + expect(formatSessionCompletedLabel(provider)).toBe( + 'Claude Code · Sonnet 3.5', + ) + }) + + it('falls back to adapter name when model label is missing for acp', () => { + const provider: Provider = { + id: 'a1', + name: 'Claude Code', + type: 'acp', + kind: 'acp', + agentId: 'a1', + adapterName: 'Claude Code', + } + expect(formatSessionCompletedLabel(provider)).toBe('Claude Code') + }) + + it('falls back to model label when adapter name is missing for acp', () => { + const provider: Provider = { + id: 'a1', + name: 'Sonnet 3.5', + type: 'acp', + kind: 'acp', + agentId: 'a1', + modelLabel: 'Sonnet 3.5', + } + expect(formatSessionCompletedLabel(provider)).toBe('Sonnet 3.5') + }) + + it('falls back to provider.name when both adapter name and model label are missing for acp', () => { + const provider: Provider = { + id: 'a1', + name: 'Some Agent', + type: 'acp', + kind: 'acp', + agentId: 'a1', + } + expect(formatSessionCompletedLabel(provider)).toBe('Some Agent') + }) +}) diff --git a/packages/browseros-agent/apps/agent/entrypoints/sidepanel/index/SessionCompletedFooter.tsx b/packages/browseros-agent/apps/agent/entrypoints/sidepanel/index/SessionCompletedFooter.tsx new file mode 100644 index 000000000..baef22d56 --- /dev/null +++ b/packages/browseros-agent/apps/agent/entrypoints/sidepanel/index/SessionCompletedFooter.tsx @@ -0,0 +1,35 @@ +import type { FC } from 'react' +import type { Provider } from '@/components/chat/chatComponentTypes' + +export function formatSessionCompletedLabel( + provider: Provider | undefined, +): string | null { + if (!provider) return null + if (provider.kind === 'acp') { + if (provider.adapterName && provider.modelLabel) { + return `${provider.adapterName} · ${provider.modelLabel}` + } + return provider.adapterName ?? provider.modelLabel ?? provider.name + } + return provider.name +} + +interface SessionCompletedFooterProps { + provider: Provider | undefined +} + +export const SessionCompletedFooter: FC = ({ + provider, +}) => { + const label = formatSessionCompletedLabel(provider) + if (!label) return null + + return ( +
+ + Session completed with{' '} + {label} + +
+ ) +}