Skip to content

Commit 0fc03f4

Browse files
committed
Overhaul best-of-n UI
1 parent fc7b2ea commit 0fc03f4

File tree

2 files changed

+102
-39
lines changed

2 files changed

+102
-39
lines changed

cli/src/components/message-block.tsx

Lines changed: 69 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,11 @@ import { TextAttributes } from '@opentui/core'
22
import { pluralize } from '@codebuff/common/util/string'
33
import React, { memo, useCallback, useMemo, type ReactNode } from 'react'
44

5-
import { shouldRenderAsSimpleText } from '../utils/constants'
5+
import {
6+
shouldRenderAsSimpleText,
7+
isImplementorAgent,
8+
getImplementorDisplayName,
9+
} from '../utils/constants'
610
import { AgentBranchItem } from './agent-branch-item'
711
import { ElapsedTimer } from './elapsed-timer'
812
import { FeedbackIconButton } from './feedback-icon-button'
@@ -584,21 +588,76 @@ const AgentBranchWrapper = memo(
584588
const theme = useTheme()
585589

586590
if (shouldRenderAsSimpleText(agentBlock.agentType)) {
591+
const isStreaming =
592+
agentBlock.status === 'running' ||
593+
streamingAgents.has(agentBlock.agentId)
594+
const isComplete = agentBlock.status === 'complete'
595+
const statusIndicator = isStreaming ? '●' : isComplete ? '✓' : '○'
596+
const statusColor = isStreaming
597+
? theme.primary
598+
: isComplete
599+
? theme.foreground
600+
: theme.muted
601+
587602
return (
588603
<box
589604
key={keyPrefix}
590605
style={{
591-
flexDirection: 'column',
606+
flexDirection: 'row',
607+
alignItems: 'center',
608+
width: '100%',
609+
marginTop: 1,
592610
}}
593611
>
594-
<text
595-
style={{
596-
wrapMode: 'word',
597-
fg: theme.muted,
598-
}}
599-
attributes={TextAttributes.ITALIC}
600-
>
601-
{BULLET_CHAR} Selecting the best implementation...
612+
<text style={{ wrapMode: 'word' }}>
613+
<span fg={statusColor}>{statusIndicator}</span>
614+
<span fg={theme.foreground} attributes={TextAttributes.BOLD}>
615+
{' '}
616+
Selecting best
617+
</span>
618+
</text>
619+
</box>
620+
)
621+
}
622+
623+
// Render implementor agents as simple tool calls
624+
if (isImplementorAgent(agentBlock.agentType)) {
625+
const isStreaming =
626+
agentBlock.status === 'running' ||
627+
streamingAgents.has(agentBlock.agentId)
628+
const isComplete = agentBlock.status === 'complete'
629+
const isFailed = agentBlock.status === 'failed'
630+
const displayName = getImplementorDisplayName(agentBlock.agentType)
631+
const statusIndicator = isStreaming
632+
? '●'
633+
: isFailed
634+
? '✗'
635+
: isComplete
636+
? '✓'
637+
: '○'
638+
const statusColor = isStreaming
639+
? theme.primary
640+
: isFailed
641+
? 'red'
642+
: isComplete
643+
? theme.foreground
644+
: theme.muted
645+
646+
return (
647+
<box
648+
key={keyPrefix}
649+
style={{
650+
flexDirection: 'row',
651+
alignItems: 'center',
652+
width: '100%',
653+
}}
654+
>
655+
<text style={{ wrapMode: 'word' }}>
656+
<span fg={statusColor}>{statusIndicator}</span>
657+
<span fg={theme.foreground} attributes={TextAttributes.BOLD}>
658+
{' '}
659+
{displayName}
660+
</span>
602661
</text>
603662
</box>
604663
)
@@ -648,17 +707,6 @@ const AgentBranchWrapper = memo(
648707
onToggleCollapsed(agentBlock.agentId)
649708
}, [onToggleCollapsed, agentBlock.agentId])
650709

651-
// Create a status message for editor-best-of-n, thinker-best-of-n, and code-reviewer-best-of-n agents
652-
const nParameterMessage =
653-
agentBlock.params?.n !== undefined &&
654-
(agentBlock.agentType.includes('editor-best-of-n')
655-
? `${BULLET_CHAR} Generating ${agentBlock.params.n} implementations...`
656-
: agentBlock.agentType.includes('thinker-best-of-n')
657-
? `${BULLET_CHAR} Generating ${agentBlock.params.n} deep thoughts...`
658-
: agentBlock.agentType.includes('code-reviewer-best-of-n')
659-
? `${BULLET_CHAR} Generating ${agentBlock.params.n} code reviews...`
660-
: undefined)
661-
662710
return (
663711
<box key={keyPrefix} style={{ flexDirection: 'column', gap: 0 }}>
664712
<AgentBranchItem
@@ -674,18 +722,6 @@ const AgentBranchWrapper = memo(
674722
statusIndicator={statusIndicator}
675723
onToggle={onToggle}
676724
>
677-
{nParameterMessage && (
678-
<text
679-
style={{
680-
wrapMode: 'word',
681-
fg: theme.muted,
682-
marginBottom: 1,
683-
}}
684-
attributes={TextAttributes.ITALIC}
685-
>
686-
{nParameterMessage}
687-
</text>
688-
)}
689725
<AgentBody
690726
agentBlock={agentBlock}
691727
indentLevel={indentLevel + 1}

cli/src/utils/constants.ts

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
11
// Agent IDs that should not be rendered in the CLI UI
2-
export const HIDDEN_AGENT_IDS = [
3-
'codebuff/context-pruner',
4-
'editor-implementor',
5-
'editor-implementor-gemini',
6-
'editor-implementor-gpt-5',
7-
] as const
2+
export const HIDDEN_AGENT_IDS = ['codebuff/context-pruner'] as const
83

94
/**
105
* Check if an agent ID should be hidden from rendering
@@ -45,6 +40,38 @@ export const shouldRenderAsSimpleText = (agentType: string): boolean => {
4540
)
4641
}
4742

43+
// Agent IDs that should render as simple tool calls (custom UI)
44+
export const IMPLEMENTOR_AGENT_IDS = [
45+
'editor-implementor',
46+
'editor-implementor-gemini',
47+
'editor-implementor-gpt-5',
48+
] as const
49+
50+
/**
51+
* Check if an agent is an implementor that should render as a simple tool call
52+
*/
53+
export const isImplementorAgent = (agentType: string): boolean => {
54+
return IMPLEMENTOR_AGENT_IDS.some((implementorId) =>
55+
agentType.includes(implementorId),
56+
)
57+
}
58+
59+
/**
60+
* Get the display name for an implementor agent
61+
*/
62+
export const getImplementorDisplayName = (agentType: string): string => {
63+
if (agentType.includes('editor-implementor-gemini')) {
64+
return 'Gemini'
65+
}
66+
if (agentType.includes('editor-implementor-gpt-5')) {
67+
return 'GPT-5'
68+
}
69+
if (agentType.includes('editor-implementor')) {
70+
return 'Sonnet'
71+
}
72+
return 'Implementor'
73+
}
74+
4875
/**
4976
* The parent agent ID for all root-level agents
5077
*/

0 commit comments

Comments
 (0)