diff --git a/src/renderer/src/components/tab-bar/SortableTab.rename-shortcut.test.tsx b/src/renderer/src/components/tab-bar/SortableTab.rename-shortcut.test.tsx index 561fd9e0cc95..3885b09578e0 100644 --- a/src/renderer/src/components/tab-bar/SortableTab.rename-shortcut.test.tsx +++ b/src/renderer/src/components/tab-bar/SortableTab.rename-shortcut.test.tsx @@ -264,6 +264,7 @@ async function renderSortableTab({ const module = await import('./SortableTab') return module.default({ tab: makeTerminalTab() as never, + agent: null, unifiedTabId: 'terminal-tab-1', groupId: 'group-1', tabCount: 1, diff --git a/src/renderer/src/components/tab-bar/SortableTab.tsx b/src/renderer/src/components/tab-bar/SortableTab.tsx index 746fe031e4e3..1c5a9df5e4c4 100644 --- a/src/renderer/src/components/tab-bar/SortableTab.tsx +++ b/src/renderer/src/components/tab-bar/SortableTab.tsx @@ -2,13 +2,13 @@ import { useCallback, useEffect, useState } from 'react' import { useSortable } from '@dnd-kit/sortable' import { X, Minimize2, Pin } from 'lucide-react' import { stripLeadingAgentTitleDecoration } from '../../../../shared/agent-title-decoration' -import { useTabAgent } from '@/lib/use-tab-agent' import { getAgentLabel } from '@/lib/agent-catalog' import { basename } from '@/lib/path' import { isImeCompositionKeyDown } from '@/lib/ime-composition-keyboard-event' import { Input } from '@/components/ui/input' import { TabHoverCard } from './TabHoverCard' import type { TerminalTab } from '../../../../shared/terminal-tab-types' +import type { TerminalAgent } from '../../../../shared/terminal-agent' import type { TabDragItemData } from '../tab-group/useTabDragSplit' import { useAppStore } from '../../store' import { @@ -33,9 +33,18 @@ import { terminalTabHasUnreadActivity } from './terminal-tab-activity-status' import { CLOSE_ALL_CONTEXT_MENUS_EVENT } from '@/lib/close-all-context-menus' +import type { NativeChatTabSession } from './tab-session-history-switch' -type SortableTabProps = { - tab: TerminalTab +/** What the strip draws for a terminal or native chat tab; never a source of agent identity. */ +type SortableTabModel = Pick< + TerminalTab, + 'id' | 'worktreeId' | 'title' | 'customTitle' | 'color' | 'shellOverride' | 'launchAgent' +> + +export type SortableTabProps = { + tab: SortableTabModel + /** The tab's agent, resolved by the caller from its own source of truth (pane signals or the chat's provider). */ + agent: TerminalAgent | null unifiedTabId: string groupId: string tabCount: number @@ -63,12 +72,13 @@ type SortableTabProps = { /** Toggle the tab between terminal and native chat view. */ onToggleViewMode?: () => void canSplitTerminal?: boolean - /** Set only for a native chat tab: the chat session it shows. */ - structuredSessionId?: string + /** Set only for a native chat tab. */ + nativeChat?: NativeChatTabSession } export default function SortableTab({ tab, + agent: tabAgent, unifiedTabId, groupId, tabCount, @@ -93,7 +103,7 @@ export default function SortableTab({ isChatView = false, onToggleViewMode, canSplitTerminal = true, - structuredSessionId + nativeChat }: SortableTabProps): React.JSX.Element { // Why: agent-completion unread exists even with terminal-attention off; collapse both sources to one primitive so unrelated tabs don't re-render. const hasUnreadActivity = useAppStore((s) => @@ -118,9 +128,6 @@ export default function SortableTab({ // Why: shellOverride is stamped at create time, so changing the default shell later won't repaint existing tabs. const shellForIcon = tab.shellOverride - // Why: use hook status + title evidence so the icon reflects the harness running now, not just the launch command. - const tabAgent = useTabAgent(tab) - // Why: with a provider icon shown, strip the agent's own leading glyph so the tab doesn't show two icons for one agent. const displayTitle = tab.customTitle ?? (tabAgent ? stripLeadingAgentTitleDecoration(tab.title) : tab.title) @@ -412,7 +419,7 @@ export default function SortableTab({ isChatView={isChatView} onToggleViewMode={onToggleViewMode} canSplitTerminal={canSplitTerminal} - structuredSessionId={structuredSessionId} + nativeChat={nativeChat} /> ) diff --git a/src/renderer/src/components/tab-bar/SortableTab.update-depth-probe.test.tsx b/src/renderer/src/components/tab-bar/SortableTab.update-depth-probe.test.tsx index 0f9ad5e767f9..779f177a3170 100644 --- a/src/renderer/src/components/tab-bar/SortableTab.update-depth-probe.test.tsx +++ b/src/renderer/src/components/tab-bar/SortableTab.update-depth-probe.test.tsx @@ -68,8 +68,6 @@ vi.mock('@dnd-kit/sortable', () => ({ }) })) -vi.mock('@/lib/use-tab-agent', () => ({ useTabAgent: () => null })) - vi.mock('@/components/ui/tooltip', () => ({ TooltipProvider: ({ children }: { children: ReactNode }) => <>{children}, Tooltip: ({ children }: { children: ReactNode }) =>
{children}
, @@ -144,6 +142,7 @@ function Harness({ tab }: { tab: TerminalTab }): ReactElement { return ( ({ toast: { success: vi.fn(), error: vi.fn() } })) vi.mock('./TabSessionSurfaceSwitchMenuItems', () => ({ TabSessionSurfaceSwitchMenuItems: ({ tab, - structuredSessionId, + nativeChat, leadingSeparator }: { tab: { id: string } - structuredSessionId?: string + nativeChat?: { sessionId: string } leadingSeparator: boolean }) => (
) @@ -116,16 +116,7 @@ function renderMenu(overrides: Partial { root.render( { }) it('offers the session-history chat/CLI move right above Pin Tab', () => { - const { container } = renderMenu({ structuredSessionId: 'orca-chat-1' }) + const { container } = renderMenu({ nativeChat: { sessionId: 'orca-chat-1', agent: 'codex' } }) const marker = getSurfaceSwitchMarker(container) expect(marker.getAttribute('data-tab-id')).toBe('term-1') diff --git a/src/renderer/src/components/tab-bar/SortableTabContextMenu.tsx b/src/renderer/src/components/tab-bar/SortableTabContextMenu.tsx index 94b715d18389..d77345a17c61 100644 --- a/src/renderer/src/components/tab-bar/SortableTabContextMenu.tsx +++ b/src/renderer/src/components/tab-bar/SortableTabContextMenu.tsx @@ -22,6 +22,7 @@ import { formatShortcutLabel, useOptionalShortcutLabel } from '@/hooks/useShortc import { translate } from '@/i18n/i18n' import { TerminalTabSplitMenuSection } from './TerminalTabSplitMenuSection' import { TabSessionSurfaceSwitchMenuItems } from './TabSessionSurfaceSwitchMenuItems' +import type { NativeChatTabSession } from './tab-session-history-switch' import { TAB_CONTEXT_MENU_CONTENT_CLASS } from './tab-context-menu-sizing' import { CopyTabIdMenuItem } from './CopyTabIdMenuItem' @@ -89,7 +90,7 @@ const TAB_COLORS = [ ] as const type SortableTabContextMenuProps = { - tab: TerminalTab + tab: Pick unifiedTabId: string groupId: string isActive: boolean @@ -118,8 +119,8 @@ type SortableTabContextMenuProps = { /** Toggle the tab between terminal and native chat view. */ onToggleViewMode?: () => void canSplitTerminal?: boolean - /** Set only for a native chat tab: the chat session it shows. */ - structuredSessionId?: string + /** Set only for a native chat tab. */ + nativeChat?: NativeChatTabSession } export function SortableTabContextMenu({ @@ -146,7 +147,7 @@ export function SortableTabContextMenu({ isChatView = false, onToggleViewMode, canSplitTerminal = true, - structuredSessionId + nativeChat }: SortableTabContextMenuProps): React.JSX.Element { const keybindings = useAppStore((state) => state.keybindings) const splitRightShortcut = formatShortcutLabel('terminal.splitRight', keybindings) @@ -193,7 +194,7 @@ export function SortableTabContextMenu({ ) : null} diff --git a/src/renderer/src/components/tab-bar/TabBar.context-menu.test.ts b/src/renderer/src/components/tab-bar/TabBar.context-menu.test.ts index e83f32a48521..8c5b649ba749 100644 --- a/src/renderer/src/components/tab-bar/TabBar.context-menu.test.ts +++ b/src/renderer/src/components/tab-bar/TabBar.context-menu.test.ts @@ -120,6 +120,12 @@ vi.mock('./SortableTab', () => ({ } })) +vi.mock('./TerminalSortableTab', () => ({ + default: function SortableTab(props: Record) { + return { type: 'SortableTab', props } + } +})) + vi.mock('./EditorFileTab', () => ({ default: function EditorFileTab(props: Record) { return { type: 'EditorFileTab', props } diff --git a/src/renderer/src/components/tab-bar/TabBarItemRow.test.tsx b/src/renderer/src/components/tab-bar/TabBarItemRow.test.tsx index 90fbfc385f46..7cef72ac4783 100644 --- a/src/renderer/src/components/tab-bar/TabBarItemRow.test.tsx +++ b/src/renderer/src/components/tab-bar/TabBarItemRow.test.tsx @@ -11,6 +11,7 @@ import { import { i18n } from '@/i18n/i18n' import type { TabBarItem } from './tab-bar-item-model' import type { WorkspaceVisibleTabType } from '../../../../shared/tab-types' +import type { AgentType } from '../../../../shared/agent-status-types' import { renderTabBarItems, type TabBarItemSurfaceProps, @@ -22,6 +23,8 @@ globalThis.IS_REACT_ACT_ENVIRONMENT = true type TabProps = { tab?: { id: string; title: string } + agent?: string | null + nativeChat?: { sessionId: string; agent: string | undefined } isActive: boolean isPinned: boolean onActivate: (id: string) => void @@ -53,7 +56,17 @@ vi.mock('./EditorFileTab', () => ({ })) /** Rebuilt on every call, like the strip's projections: equal content, never the same objects. */ -function buildItems({ terminalPinned = false, browserTitle = 'Example' } = {}): TabBarItem[] { +function buildItems({ + terminalPinned = false, + browserTitle = 'Example', + sessionLabel = 'Session', + sessionAgent +}: { + terminalPinned?: boolean + browserTitle?: string + sessionLabel?: string + sessionAgent?: AgentType +} = {}): TabBarItem[] { return [ { type: 'terminal', @@ -117,7 +130,8 @@ function buildItems({ terminalPinned = false, browserTitle = 'Example' } = {}): groupId: 'group-1', worktreeId: 'wt-1', contentType: 'agent-session', - label: 'Session', + ...(sessionAgent ? { agentSessionAgent: sessionAgent } : {}), + label: sessionLabel, customLabel: null, color: null, sortOrder: 3, @@ -133,6 +147,8 @@ type StripInputs = { managedBrowserCreationEnabled?: boolean terminalPinned?: boolean browserTitle?: string + sessionLabel?: string + sessionAgent?: AgentType activeTabType?: WorkspaceVisibleTabType activeClientHostedBrowserRowId?: string | null statusByRelativePath?: TabBarItemSurfaceRuntime['statusByRelativePath'] @@ -144,6 +160,8 @@ function Strip({ managedBrowserCreationEnabled = false, terminalPinned, browserTitle, + sessionLabel, + sessionAgent, activeTabType = 'terminal', activeClientHostedBrowserRowId = null, statusByRelativePath = STATUS_BY_RELATIVE_PATH @@ -187,7 +205,7 @@ function Strip({ return ( <> {renderTabBarItems({ - items: buildItems({ terminalPinned, browserTitle }), + items: buildItems({ terminalPinned, browserTitle, sessionLabel, sessionAgent }), props, runtime, actions, @@ -338,6 +356,25 @@ describe('tab strip rows', () => { expect(tabRenders).toHaveLength(TAB_IDS.length + 1) }) + it.each([ + 'Hermes automation deprecation', + 'Claude Code review', + 'Review Grok usage', + 'OpenCode launch-flow design' + ])('shows a native chat with its own provider when its name is "%s"', (sessionLabel) => { + renderStrip({ sessionLabel, sessionAgent: 'codex' }) + + expect(lastRender('session-1').tab?.title).toBe(sessionLabel) + expect(lastRender('session-1').agent).toBe('codex') + expect(lastRender('session-1').nativeChat).toEqual({ sessionId: 'session-1', agent: 'codex' }) + }) + + it('shows no agent for a native chat whose provider is not recorded', () => { + renderStrip({ sessionLabel: 'Hermes automation deprecation' }) + + expect(lastRender('session-1').agent).toBeNull() + }) + it.each(TAB_IDS)('retires a client-hosted row selection when %s is activated', (tabId) => { renderStrip() selectClientHostedBrowserRow({ diff --git a/src/renderer/src/components/tab-bar/TabBarItemRow.tsx b/src/renderer/src/components/tab-bar/TabBarItemRow.tsx index f2d1fc324c67..04b010cc1230 100644 --- a/src/renderer/src/components/tab-bar/TabBarItemRow.tsx +++ b/src/renderer/src/components/tab-bar/TabBarItemRow.tsx @@ -2,10 +2,9 @@ import { memo } from 'react' import { useTranslation } from 'react-i18next' import { shallow } from 'zustand/shallow' import type { GitFileStatus } from '../../../../shared/git-status-types' -import type { TerminalTab } from '../../../../shared/terminal-tab-types' -import { isTuiAgent } from '../../../../shared/tui-agent-config' import type { OpenFile } from '../../store/slices/editor' import SortableTab from './SortableTab' +import TerminalSortableTab from './TerminalSortableTab' import EditorFileTab from './EditorFileTab' import BrowserTab from './BrowserTab' import type { DropIndicator } from './drop-indicator' @@ -13,6 +12,7 @@ import type { TabDragItemData } from '../tab-group/useTabDragSplit' import { getTabDragLabel, resolveTerminalItemTab, type TabBarItem } from './tab-bar-item-model' import type { TabBarItemActions } from './use-tab-bar-item-actions' import { useStructuredChatTabConversationName } from '@/runtime/structured-conversation-name' +import { agentTypeToIconAgent } from '@/lib/agent-status' // Why only values and `actions`: anything a tab draws must be a compared prop, or a skipped render shows it stale. type TabBarItemRowProps = { @@ -96,7 +96,7 @@ function TabBarItemRow({ } if (item.type === 'terminal') { return ( - {}} canSplitTerminal={false} - structuredSessionId={item.data.entityId} + nativeChat={nativeChat} /> ) } diff --git a/src/renderer/src/components/tab-bar/TabSessionSurfaceSwitchMenuItems.test.tsx b/src/renderer/src/components/tab-bar/TabSessionSurfaceSwitchMenuItems.test.tsx index 1c9dc908b105..ee50c45b2cbe 100644 --- a/src/renderer/src/components/tab-bar/TabSessionSurfaceSwitchMenuItems.test.tsx +++ b/src/renderer/src/components/tab-bar/TabSessionSurfaceSwitchMenuItems.test.tsx @@ -134,8 +134,10 @@ function renderItemsNow(structuredSessionId?: string): void { Open diff --git a/src/renderer/src/components/tab-bar/TabSessionSurfaceSwitchMenuItems.tsx b/src/renderer/src/components/tab-bar/TabSessionSurfaceSwitchMenuItems.tsx index 10c31b0f9c06..5daf01e0399d 100644 --- a/src/renderer/src/components/tab-bar/TabSessionSurfaceSwitchMenuItems.tsx +++ b/src/renderer/src/components/tab-bar/TabSessionSurfaceSwitchMenuItems.tsx @@ -11,6 +11,7 @@ import { lookupTabSessionHistoryRow, resolveTabSessionHistorySubject, resolveTabSessionSwitch, + type NativeChatTabSession, type TabSessionHistorySubject, type TabSessionSwitch } from './tab-session-history-switch' @@ -30,13 +31,13 @@ function resolveMove( // Mounted only while the menu is open, so the history lookup runs per right-click, not per tab. function useTabSessionSwitch( - tab: Pick, - structuredSessionId: string | undefined + tab: Pick, + nativeChat: NativeChatTabSession | undefined ): ResolvedTabSessionSwitch | null { const [subject] = useState(() => resolveTabSessionHistorySubject(useAppStore.getState(), { - tab: { id: tab.id, worktreeId: tab.worktreeId, launchAgent: tab.launchAgent }, - structuredSessionId + tab: { id: tab.id, worktreeId: tab.worktreeId }, + nativeChat }) ) // Only a fresh reply carries current chat ownership, so nothing is offered before it lands. @@ -70,14 +71,14 @@ function useTabSessionSwitch( * that shows that session and hidden wherever the row would not offer it. */ export function TabSessionSurfaceSwitchMenuItems({ tab, - structuredSessionId, + nativeChat, leadingSeparator }: { - tab: Pick - structuredSessionId?: string + tab: Pick + nativeChat?: NativeChatTabSession leadingSeparator: boolean }): React.JSX.Element | null { - const resolved = useTabSessionSwitch(tab, structuredSessionId) + const resolved = useTabSessionSwitch(tab, nativeChat) const targetState = useAppStore( useShallow((state) => ({ projects: state.projects, diff --git a/src/renderer/src/components/tab-bar/TerminalSortableTab.tsx b/src/renderer/src/components/tab-bar/TerminalSortableTab.tsx new file mode 100644 index 000000000000..ec53f4754585 --- /dev/null +++ b/src/renderer/src/components/tab-bar/TerminalSortableTab.tsx @@ -0,0 +1,11 @@ +import { useTabAgent } from '@/lib/use-tab-agent' +import type { TerminalTab } from '../../../../shared/terminal-tab-types' +import SortableTab, { type SortableTabProps } from './SortableTab' + +type TerminalSortableTabProps = Omit & { tab: TerminalTab } + +/** A terminal pane can change agents, so its identity is inferred from live pane signals. */ +export default function TerminalSortableTab(props: TerminalSortableTabProps): React.JSX.Element { + const agent = useTabAgent(props.tab) + return +} diff --git a/src/renderer/src/components/tab-bar/tab-session-history-switch.test.ts b/src/renderer/src/components/tab-bar/tab-session-history-switch.test.ts index 8fb3188fb134..bcb2c770f51b 100644 --- a/src/renderer/src/components/tab-bar/tab-session-history-switch.test.ts +++ b/src/renderer/src/components/tab-bar/tab-session-history-switch.test.ts @@ -175,8 +175,8 @@ describe('resolveTabSessionHistorySubject', () => { it('finds a native chat tab by the chat it shows', () => { expect( resolveTabSessionHistorySubject(makeState(), { - tab: { id: 'unified-1', worktreeId: WORKTREE_ID, launchAgent: 'codex' }, - structuredSessionId: 'orca-chat-1' + tab: { id: 'unified-1', worktreeId: WORKTREE_ID }, + nativeChat: { sessionId: 'orca-chat-1', agent: 'codex' } }) ).toEqual({ ...chatSubject }) }) @@ -184,19 +184,19 @@ describe('resolveTabSessionHistorySubject', () => { it('finds a chat of any agent whose CLI can open a copy', () => { expect( resolveTabSessionHistorySubject(makeState(), { - tab: { id: 'unified-1', worktreeId: WORKTREE_ID, launchAgent: 'grok' }, - structuredSessionId: 'orca-chat-1' + tab: { id: 'unified-1', worktreeId: WORKTREE_ID }, + nativeChat: { sessionId: 'orca-chat-1', agent: 'grok' } }) ).toEqual({ ...chatSubject }) }) it.each(['opencode', 'omp'] as const)( 'skips a %s chat, whose CLI cannot open a copy', - (launchAgent) => { + (agent) => { expect( resolveTabSessionHistorySubject(makeState(), { - tab: { id: 'unified-1', worktreeId: WORKTREE_ID, launchAgent }, - structuredSessionId: 'orca-chat-1' + tab: { id: 'unified-1', worktreeId: WORKTREE_ID }, + nativeChat: { sessionId: 'orca-chat-1', agent } }) ).toBeNull() } @@ -285,12 +285,8 @@ describe('resolveTabSessionHistorySubject', () => { ).toBeNull() expect( resolveTabSessionHistorySubject(state, { - tab: { - id: 'unified-1', - worktreeId: WORKTREE_ID, - launchAgent: 'claude' - }, - structuredSessionId: 'orca-chat-1' + tab: { id: 'unified-1', worktreeId: WORKTREE_ID }, + nativeChat: { sessionId: 'orca-chat-1', agent: 'claude' } }) ).toBeNull() }) @@ -301,12 +297,8 @@ describe('resolveTabSessionHistorySubject', () => { }) expect( resolveTabSessionHistorySubject(state, { - tab: { - id: 'unified-1', - worktreeId: WORKTREE_ID, - launchAgent: 'claude' - }, - structuredSessionId: 'orca-chat-1' + tab: { id: 'unified-1', worktreeId: WORKTREE_ID }, + nativeChat: { sessionId: 'orca-chat-1', agent: 'claude' } }) ).toMatchObject({ kind: 'chat', diff --git a/src/renderer/src/components/tab-bar/tab-session-history-switch.ts b/src/renderer/src/components/tab-bar/tab-session-history-switch.ts index 7eac50cd99c8..72f996eff4e4 100644 --- a/src/renderer/src/components/tab-bar/tab-session-history-switch.ts +++ b/src/renderer/src/components/tab-bar/tab-session-history-switch.ts @@ -21,6 +21,7 @@ import { import type { AgentSessionHandleProvider } from '../../../../shared/agent-session-provider-handle' import { agentForksConversations } from '../../../../shared/agent-resume-argv' import type { TerminalTab } from '../../../../shared/terminal-tab-types' +import type { AgentType } from '../../../../shared/agent-status-types' import { resolveAiVaultSessionSurfaceSwitchTargets } from '../right-sidebar/ai-vault-session-surface-switch' import { resolveAiVaultHistoryRowResume } from '../right-sidebar/ai-vault-session-resume-in-chat-workspace' import { resolveAiVaultTargetWorkspacePath } from '../right-sidebar/ai-vault-session-launch-target' @@ -100,12 +101,18 @@ export function canPaneOfferResumeInNewChat( ) } +/** A native chat tab's session and the provider recorded when it was created. */ +export type NativeChatTabSession = { + sessionId: string + agent: AgentType | undefined +} + export function resolveTabSessionHistorySubject( state: AppState, args: { - tab: Pick - /** Set only for a native chat tab: the chat session it shows. */ - structuredSessionId?: string + tab: Pick + /** Set only for a native chat tab. */ + nativeChat?: NativeChatTabSession /** Narrows a terminal tab to one pane's own conversation instead of the tab's best one. */ paneKey?: string } @@ -119,13 +126,13 @@ export function resolveTabSessionHistorySubject( ? { workspaceId, request } : null } - if (args.structuredSessionId !== undefined) { + if (args.nativeChat) { // A chat tab's only move is opening a copy in its CLI. const target = - args.tab.launchAgent && agentForksConversations(args.tab.launchAgent) + args.nativeChat.agent && agentForksConversations(args.nativeChat.agent) ? workspace(args.tab.worktreeId, 'chat') : null - return target ? { ...target, kind: 'chat', sessionId: args.structuredSessionId } : null + return target ? { ...target, kind: 'chat', sessionId: args.nativeChat.sessionId } : null } // The pane-to-conversation mapping tab titles use, limited to agents still running: once the agent // exits, the tab title keeps its conversation but the move is no longer offered. diff --git a/src/renderer/src/components/tab-bar/tab-title-tooltip.test.tsx b/src/renderer/src/components/tab-bar/tab-title-tooltip.test.tsx index 3522861dd0f6..08547cc98439 100644 --- a/src/renderer/src/components/tab-bar/tab-title-tooltip.test.tsx +++ b/src/renderer/src/components/tab-bar/tab-title-tooltip.test.tsx @@ -1,17 +1,14 @@ import { cloneElement, isValidElement, type ReactElement, type ReactNode } from 'react' import { renderToStaticMarkup } from 'react-dom/server' -import { beforeEach, describe, expect, it, vi } from 'vitest' +import { describe, expect, it, vi } from 'vitest' import type { BrowserTab as BrowserTabState } from '../../../../shared/browser-workspace-types' import type { TerminalTab } from '../../../../shared/terminal-tab-types' -import type { TuiAgent } from '../../../../shared/tui-agent' import type { OpenFile } from '../../store/slices/editor' import type { TabDragItemData } from '../tab-group/useTabDragSplit' import BrowserTab from './BrowserTab' import EditorFileTab from './EditorFileTab' import SortableTab from './SortableTab' -let mockTabAgent: TuiAgent | null = null - vi.mock('@dnd-kit/sortable', () => ({ useSortable: ({ id }: { id: string }) => ({ attributes: { @@ -97,10 +94,6 @@ vi.mock('../../../../shared/agent-title-decoration', () => ({ title.replace(/^(?:[✳✦⏲◇✋⠀-⣿]+|[.*]\s)\s*/, '').trimStart() || title })) -vi.mock('@/lib/use-tab-agent', () => ({ - useTabAgent: () => mockTabAgent -})) - vi.mock('../../store', () => ({ useAppStore: (selector: (state: { unreadTerminalTabs: Record }) => unknown) => selector({ unreadTerminalTabs: {} }) @@ -251,14 +244,11 @@ function makeEditorFile(overrides: Partial = {}): } describe('tab title tooltips', () => { - beforeEach(() => { - mockTabAgent = null - }) - it('uses the terminal custom title for the visible label and tooltip trigger content', () => { const markup = renderToStaticMarkup( { }) it("shows the provider icon while stripping the agent's leading status glyph from the label", () => { - mockTabAgent = 'claude' const markup = renderToStaticMarkup( state.tabsByWorktree[WORKTREE_ID]?.[0]) as TerminalTab