mirror of
https://github.com/stablyai/orca.git
synced 2026-11-13 17:37:40 +00:00
fix(activity): name a resumed session by its own title in the agents list (#41127)
The Agents list titled a thread with the tab's generated title, the first prompt that tab saw. After `claude --resume` in a new tab, that is the message sent after the resume, so the list kept a status note as the thread's name while the tab and the agent row showed the session's own name. Rank a named provider title of the pane's own session above the generated title, as resolveTabNameSource does, and share the session ownership check with the agent row.
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { getActivityThreadTaskTitle } from './activity-thread-display'
|
||||
|
||||
const resumedTab = {
|
||||
customTitle: null,
|
||||
aiVaultTitle: {
|
||||
agent: 'claude' as const,
|
||||
sessionId: 'session-1',
|
||||
title: 'Triage lead handover',
|
||||
kind: 'named' as const
|
||||
},
|
||||
generatedTitle: 'Mac restarted at 15 01 so this tab stopped',
|
||||
title: 'Triage lead handover',
|
||||
defaultTitle: 'Terminal 2'
|
||||
}
|
||||
|
||||
const resumedEntry = {
|
||||
prompt: 'How is the rename going?',
|
||||
stateHistory: [],
|
||||
agentType: 'claude' as const,
|
||||
providerSession: { key: 'session_id' as const, id: 'session-1' }
|
||||
}
|
||||
|
||||
describe('getActivityThreadTaskTitle', () => {
|
||||
it("names a resumed session by its own name, not the tab's first prompt", () => {
|
||||
expect(
|
||||
getActivityThreadTaskTitle({
|
||||
entry: resumedEntry,
|
||||
tab: resumedTab,
|
||||
generatedTitlesEnabled: true
|
||||
})
|
||||
).toBe('Triage lead handover')
|
||||
})
|
||||
})
|
||||
@@ -4,6 +4,7 @@ import type {
|
||||
AgentStatusState
|
||||
} from '../../../../../shared/agent-status/contracts/agent-status-types'
|
||||
import type { TerminalTab } from '../../../../../shared/workspace-layout/terminal-tab-types'
|
||||
import { aiVaultTitleOfSession } from '../../../../../shared/ai-vault/contracts/ai-vault-session-title'
|
||||
import type { Worktree } from '../../../../../shared/worktree/types'
|
||||
import {
|
||||
getAgentRowPrimaryText,
|
||||
@@ -83,8 +84,14 @@ export function getActivityThreadWorkspaceTitle(
|
||||
}
|
||||
|
||||
export function getActivityThreadTaskTitle(args: {
|
||||
entry: Pick<AgentStatusEntry, 'orchestration' | 'prompt' | 'stateHistory'>
|
||||
tab: Pick<TerminalTab, 'customTitle' | 'generatedTitle' | 'title' | 'defaultTitle'>
|
||||
entry: Pick<
|
||||
AgentStatusEntry,
|
||||
'orchestration' | 'prompt' | 'stateHistory' | 'agentType' | 'providerSession'
|
||||
>
|
||||
tab: Pick<
|
||||
TerminalTab,
|
||||
'customTitle' | 'aiVaultTitle' | 'generatedTitle' | 'title' | 'defaultTitle'
|
||||
>
|
||||
generatedTitlesEnabled: boolean
|
||||
}): string {
|
||||
const customTitle = args.tab.customTitle?.trim()
|
||||
@@ -97,6 +104,17 @@ export function getActivityThreadTaskTitle(args: {
|
||||
return orchestrationLabel
|
||||
}
|
||||
|
||||
// Like the tab, a session's own name outranks the first prompt this tab saw, e.g. after a resume.
|
||||
const sessionTitle = aiVaultTitleOfSession(
|
||||
args.tab.aiVaultTitle,
|
||||
args.entry.agentType,
|
||||
args.entry.providerSession?.id
|
||||
)
|
||||
const sessionName = sessionTitle?.kind === 'named' ? sessionTitle.title.trim() : ''
|
||||
if (sessionName) {
|
||||
return sessionName
|
||||
}
|
||||
|
||||
// Respect tabAutoGenerateTitle here too so disabled generated titles cannot resurface.
|
||||
const generatedTitle = args.generatedTitlesEnabled ? args.tab.generatedTitle?.trim() : ''
|
||||
if (generatedTitle) {
|
||||
|
||||
@@ -7,6 +7,7 @@ import { isJcodeIdentityTerminalTitle } from '../title/providers/jcode-terminal-
|
||||
import { SYNTHETIC_AGENT_TITLE_PROFILES } from '../title/synthetic-agent-title'
|
||||
import type { TerminalTab } from '../../workspace-layout/terminal-tab-types'
|
||||
import { resolveTabNameSource } from '../../tab-title-resolution'
|
||||
import { aiVaultTitleOfSession } from '../../ai-vault/contracts/ai-vault-session-title'
|
||||
|
||||
export type ConversationNameTab = Pick<
|
||||
TerminalTab,
|
||||
@@ -111,19 +112,12 @@ export function getAgentRowConversationName(
|
||||
): string | null {
|
||||
const liveTitle =
|
||||
paneLiveTitle === undefined ? (tab.title?.trim() ?? '') : (paneLiveTitle?.trim() ?? '')
|
||||
// Provider titles belong to their session, not every pane in the tab.
|
||||
const aiVaultTitle = tab.aiVaultTitle
|
||||
const named = resolveTabNameSource(
|
||||
{
|
||||
customTitle: tab.customTitle,
|
||||
quickCommandLabel: tab.quickCommandLabel,
|
||||
liveTitle,
|
||||
sessionTitle:
|
||||
aiVaultTitle &&
|
||||
aiVaultTitle.agent === agentType &&
|
||||
aiVaultTitle.sessionId === providerSessionId
|
||||
? aiVaultTitle
|
||||
: null,
|
||||
sessionTitle: aiVaultTitleOfSession(tab.aiVaultTitle, agentType, providerSessionId),
|
||||
generatedTitle: tab.generatedTitle
|
||||
},
|
||||
generatedTitlesEnabled
|
||||
|
||||
@@ -11,6 +11,15 @@ export type AiVaultSessionTitle = {
|
||||
kind?: AiVaultSessionTitleKind
|
||||
}
|
||||
|
||||
/** A tab's provider title names only the pane running that session, not every pane in the tab. */
|
||||
export function aiVaultTitleOfSession(
|
||||
title: AiVaultSessionTitle | null | undefined,
|
||||
agent: string | null | undefined,
|
||||
sessionId: string | undefined
|
||||
): AiVaultSessionTitle | null {
|
||||
return title && title.agent === agent && title.sessionId === sessionId ? title : null
|
||||
}
|
||||
|
||||
export type AiVaultSessionTitleRequest = {
|
||||
agent: AiVaultSessionTitle['agent']
|
||||
sessionId: string
|
||||
|
||||
Reference in New Issue
Block a user