Files
orca/src/shared/agent-row-conversation-name.test.ts
T
Brennan Benson 34e0233cf5 feat(agent-rows): label sidebar and dashboard rows with conversation names (#9989)
* feat(agent-rows): opt-in conversation-name labels for agent rows

Sidebar worktree-card and dashboard agent rows always show the last
message sent to the agent, so rows relabel on every turn and a 'continue'
prompt becomes the row's name. Add an opt-in Agents setting that labels
rows with the conversation name instead, resolved with the tab bar's
precedence: manual rename, quick-command label, OpenCode session title,
generated title (behind its existing setting), then the agent-set live
title. Live titles count only when they carry a real name - status
decoration is stripped, and pure status, identity-echo, spinner+cwd, and
placeholder titles fall back to the last-message label. Subagent child
rows keep their own descriptions.

Locale note: sync:localization-catalog also restored parity for keys
already missing on main (add-host, sleep-worktree copy).

* fix(agent-rows): read the live tab so late renames and titles surface

Row data patches live entries in place and keeps the tab snapshot from
row creation, so a rename or agent-set title landing after the row was
built never reached the conversation-name resolver. Select the current
tab from the store in the hook; retained rows without a live tab keep
the snapshot fallback.

* fix(agent-rows): reject status labels and bound lookup work

Reject native provider/status titles and Windows/UNC cwd frames so conversation-name mode falls back to the user's last message instead of relabeling rows with identity or path text. Keep default-off and subagent rows off the tab map, and share a WeakMap-backed tab index across mounted rows to avoid repeated linear scans on store writes.

* feat(agent-rows): make conversation names the default

Always prefer a usable conversation name for sidebar and dashboard agent rows, falling back to the last message when the resolver rejects a title. Remove the preference, settings UI, search copy, and catalog entries. Keep subagent labels unchanged and reject generic Terminal N placeholders so partial tab snapshots cannot hide meaningful prompts.

* chore(i18n): sync catalog after main merge

* fix(agent-rows): preserve same-tab child labels
2026-07-23 17:07:25 -07:00

131 lines
4.8 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import {
getAgentRowConversationName,
type ConversationNameTab
} from './agent-row-conversation-name'
function makeTab(overrides: Partial<ConversationNameTab> = {}): ConversationNameTab {
return { customTitle: null, title: '', ...overrides }
}
describe('getAgentRowConversationName', () => {
it('prefers the manual tab rename over every other source', () => {
const tab = makeTab({
customTitle: 'Patient sync spike',
quickCommandLabel: 'Run tests',
generatedTitle: 'Fix intake flow',
title: '✳ Investigate replay bug'
})
expect(getAgentRowConversationName(tab, 'claude', true)).toBe('Patient sync spike')
})
it('falls back to the quick-command label before titles', () => {
const tab = makeTab({ quickCommandLabel: 'Run tests', title: '✳ Investigate replay bug' })
expect(getAgentRowConversationName(tab, 'claude', true)).toBe('Run tests')
})
it('keeps OpenCode semantic session titles whole', () => {
const tab = makeTab({ title: 'OC | build the release pipeline' })
expect(getAgentRowConversationName(tab, 'opencode', false)).toBe(
'OC | build the release pipeline'
)
})
it('uses the generated title only when generated titles are enabled', () => {
const tab = makeTab({ generatedTitle: 'Fix intake flow', title: '✳ Investigate replay bug' })
expect(getAgentRowConversationName(tab, 'claude', true)).toBe('Fix intake flow')
expect(getAgentRowConversationName(tab, 'claude', false)).toBe('Investigate replay bug')
})
it('strips leading status decoration from agent-set titles', () => {
expect(
getAgentRowConversationName(makeTab({ title: '✳ Fix patient intake flow' }), 'claude', false)
).toBe('Fix patient intake flow')
expect(
getAgentRowConversationName(makeTab({ title: '⠋ Refactor replay guard' }), 'codex', false)
).toBe('Refactor replay guard')
})
it('rejects spinner+cwd titles instead of surfacing paths as names', () => {
expect(
getAgentRowConversationName(makeTab({ title: '⠋ ~/orca/workspaces' }), 'codex', false)
).toBeNull()
expect(
getAgentRowConversationName(makeTab({ title: '/Users/dev/repo' }), 'codex', false)
).toBeNull()
expect(
getAgentRowConversationName(makeTab({ title: 'C:\\repos\\orca' }), 'codex', false)
).toBeNull()
expect(
getAgentRowConversationName(makeTab({ title: 'orca/workspaces' }), 'codex', false)
).toBeNull()
expect(
getAgentRowConversationName(
makeTab({ title: '\\\\wsl.localhost\\Ubuntu\\home\\dev\\orca' }),
'codex',
false
)
).toBeNull()
expect(
getAgentRowConversationName(makeTab({ title: 'repos\\orca' }), 'codex', false)
).toBeNull()
})
it('accepts multi-word titles that merely contain a slash', () => {
expect(
getAgentRowConversationName(makeTab({ title: 'Fix a/b toggle in settings' }), 'codex', false)
).toBe('Fix a/b toggle in settings')
})
it('rejects synthetic status titles', () => {
expect(
getAgentRowConversationName(makeTab({ title: 'Codex ready' }), 'codex', false)
).toBeNull()
expect(
getAgentRowConversationName(makeTab({ title: 'Codex - action required' }), 'codex', false)
).toBeNull()
expect(
getAgentRowConversationName(makeTab({ title: 'Cursor Agent' }), 'cursor', false)
).toBeNull()
})
it('rejects identity-echo, management, and placeholder titles', () => {
expect(getAgentRowConversationName(makeTab({ title: 'Claude' }), 'claude', false)).toBeNull()
expect(
getAgentRowConversationName(makeTab({ title: '✳ Claude Code' }), 'claude', false)
).toBeNull()
expect(
getAgentRowConversationName(
makeTab({ title: 'Claude Code - action required' }),
'claude',
false
)
).toBeNull()
expect(
getAgentRowConversationName(makeTab({ title: '✦ Gemini CLI' }), 'gemini', false)
).toBeNull()
expect(
getAgentRowConversationName(makeTab({ title: '◇ Ready (orca)' }), 'gemini', false)
).toBeNull()
expect(
getAgentRowConversationName(makeTab({ title: 'claude agents' }), 'claude', false)
).toBeNull()
expect(getAgentRowConversationName(makeTab({ title: 'Agent' }), 'claude', false)).toBeNull()
})
it('rejects empty, glyph-only, and default terminal titles', () => {
expect(getAgentRowConversationName(makeTab(), 'claude', false)).toBeNull()
expect(getAgentRowConversationName(makeTab({ title: '✳' }), 'claude', false)).toBeNull()
expect(
getAgentRowConversationName(makeTab({ title: 'Terminal 1' }), 'claude', false)
).toBeNull()
expect(
getAgentRowConversationName(
makeTab({ title: 'Terminal 2', defaultTitle: 'Terminal 2' }),
'claude',
false
)
).toBeNull()
})
})