Skip to content

Commit 68d3240

Browse files
committed
Add progress-focused collapsed preview for editor-multi-prompt
1 parent 1fc13e4 commit 68d3240

File tree

8 files changed

+560
-42
lines changed

8 files changed

+560
-42
lines changed

agents/editor/best-of-n/editor-multi-prompt.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,13 +206,14 @@ function* handleStepsMultiPrompt({
206206
}
207207

208208
// Extract suggested improvements from selector output
209-
const { suggestedImprovements } = selectorOutput
209+
const { reason, suggestedImprovements } = selectorOutput
210210

211211
// Set output with the applied results and suggested improvements
212212
yield {
213213
toolName: 'set_output',
214214
input: {
215215
chosenStrategy: chosenImplementation.strategy,
216+
reason,
216217
toolResults: appliedToolResults,
217218
suggestedImprovements,
218219
},

cli/src/components/blocks/agent-branch-item.tsx

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import React, { memo, type ReactNode } from 'react'
44
import { useTheme } from '../../hooks/use-theme'
55
import { useWhyDidYouUpdateById } from '../../hooks/use-why-did-you-update'
66
import { getCliEnv } from '../../utils/env'
7+
import { MAX_COLLAPSED_LINES, truncateToLines } from '../../utils/strings'
78
import { BORDER_CHARS } from '../../utils/ui-constants'
89
import { Button } from '../button'
910
import { CollapseButton } from '../collapse-button'
@@ -15,8 +16,8 @@ interface AgentBranchItemProps {
1516
agentId?: string
1617
isCollapsed: boolean
1718
isStreaming: boolean
18-
streamingPreview: string
19-
finishedPreview: string
19+
/** Preview text shown when collapsed (empty string = no preview) */
20+
preview: string
2021
statusLabel?: string
2122
statusColor?: string
2223
statusIndicator?: string
@@ -32,8 +33,7 @@ export const AgentBranchItem = memo((props: AgentBranchItemProps) => {
3233
agentId,
3334
isCollapsed,
3435
isStreaming,
35-
streamingPreview,
36-
finishedPreview,
36+
preview,
3737
statusLabel,
3838
statusColor,
3939
statusIndicator = '●',
@@ -64,8 +64,7 @@ export const AgentBranchItem = memo((props: AgentBranchItemProps) => {
6464
? `${statusLabel} ${statusIndicator}`
6565
: `${statusIndicator} ${statusLabel}`
6666
: null
67-
const showCollapsedPreview =
68-
(isStreaming && !!streamingPreview) || (!isStreaming && !!finishedPreview)
67+
const showCollapsedPreview = preview.length > 0
6968

7069
const isTextRenderable = (value: ReactNode): boolean => {
7170
if (value === null || value === undefined || typeof value === 'boolean') {
@@ -234,7 +233,7 @@ export const AgentBranchItem = memo((props: AgentBranchItemProps) => {
234233
fg={isStreaming ? theme.foreground : theme.muted}
235234
attributes={getAttributes(TextAttributes.ITALIC)}
236235
>
237-
{isStreaming ? streamingPreview : finishedPreview}
236+
{truncateToLines(preview, MAX_COLLAPSED_LINES)}
238237
</text>
239238
</Button>
240239
) : null

cli/src/components/blocks/agent-branch-wrapper.tsx

Lines changed: 83 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import { TextAttributes } from '@opentui/core'
2-
import React, { memo, useCallback, useMemo, useRef, type ReactNode } from 'react'
2+
import React, {
3+
memo,
4+
useCallback,
5+
useMemo,
6+
useRef,
7+
type ReactNode,
8+
} from 'react'
39

410
import { AgentBlockGrid } from './agent-block-grid'
511
import { AgentBranchItem } from './agent-branch-item'
@@ -13,9 +19,16 @@ import { useChatStore } from '../../state/chat-store'
1319
import { isTextBlock } from '../../types/chat'
1420
import { getAgentStatusInfo } from '../../utils/agent-helpers'
1521
import { extractHtmlBlockMargins } from '../../utils/block-margins'
16-
import { processBlocks, type BlockProcessorHandlers } from '../../utils/block-processor'
17-
import { shouldRenderAsSimpleText } from '../../utils/constants'
18-
import { isImplementorAgent, getImplementorIndex } from '../../utils/implementor-helpers'
22+
import {
23+
processBlocks,
24+
type BlockProcessorHandlers,
25+
} from '../../utils/block-processor'
26+
import { shouldRenderAsSimpleText, isMultiPromptEditor } from '../../utils/constants'
27+
import {
28+
isImplementorAgent,
29+
getImplementorIndex,
30+
getMultiPromptPreview,
31+
} from '../../utils/implementor-helpers'
1932
import { AGENT_CONTENT_HORIZONTAL_PADDING } from '../../utils/layout-helpers'
2033

2134
import type {
@@ -26,6 +39,45 @@ import type {
2639
} from '../../types/chat'
2740
import type { MarkdownPalette } from '../../utils/markdown-renderer'
2841

42+
/**
43+
* Compute preview text for collapsed agent display.
44+
* Returns empty string when preview shouldn't be shown (expanded state).
45+
*/
46+
function getCollapsedPreview(
47+
agentBlock: AgentContentBlock,
48+
isStreaming: boolean,
49+
isCollapsed: boolean,
50+
): string {
51+
// No preview needed if expanded and not streaming
52+
if (!isStreaming && !isCollapsed) {
53+
return ''
54+
}
55+
56+
// For multi-prompt editors, try progress-focused preview first
57+
if (isMultiPromptEditor(agentBlock.agentType)) {
58+
const multiPromptPreview = getMultiPromptPreview(
59+
agentBlock.blocks,
60+
agentBlock.status === 'complete',
61+
)
62+
if (multiPromptPreview) {
63+
return multiPromptPreview
64+
}
65+
}
66+
67+
// Default preview: use initialPrompt or first line of text content
68+
if (agentBlock.initialPrompt) {
69+
return sanitizePreview(agentBlock.initialPrompt)
70+
}
71+
72+
const textContent =
73+
agentBlock.blocks
74+
?.filter(isTextBlock)
75+
.map((b) => b.content)
76+
.join('') || ''
77+
const firstLine = textContent.split('\n').find((line) => line.trim()) || ''
78+
return `${sanitizePreview(firstLine)}...`
79+
}
80+
2981
interface AgentBodyProps {
3082
agentBlock: Extract<ContentBlock, { type: 'agent' }>
3183
keyPrefix: string
@@ -51,7 +103,10 @@ interface AgentBodyPropsRef {
51103
onBuildMax: () => void
52104
isLastMessage?: boolean
53105
theme: ReturnType<typeof useTheme>
54-
getAgentMarkdownOptions: (indent: number) => { codeBlockWidth: number; palette: MarkdownPalette }
106+
getAgentMarkdownOptions: (indent: number) => {
107+
codeBlockWidth: number
108+
palette: MarkdownPalette
109+
}
55110
}
56111

57112
const AgentBody = memo(
@@ -75,7 +130,9 @@ const AgentBody = memo(
75130
return {
76131
codeBlockWidth: Math.max(
77132
10,
78-
availableWidth - AGENT_CONTENT_HORIZONTAL_PADDING - indentationOffset,
133+
availableWidth -
134+
AGENT_CONTENT_HORIZONTAL_PADDING -
135+
indentationOffset,
79136
),
80137
palette: {
81138
...markdownPalette,
@@ -110,7 +167,10 @@ const AgentBody = memo(
110167
const p = propsRef.current
111168
return (
112169
<ThinkingBlock
113-
key={reasoningBlocks[0]?.thinkingId ?? `${p.keyPrefix}-thinking-${startIndex}`}
170+
key={
171+
reasoningBlocks[0]?.thinkingId ??
172+
`${p.keyPrefix}-thinking-${startIndex}`
173+
}
114174
blocks={reasoningBlocks}
115175
onToggleCollapsed={p.onToggleCollapsed}
116176
availableWidth={p.availableWidth}
@@ -179,7 +239,8 @@ const AgentBody = memo(
179239
if (block.type === 'text') {
180240
const textBlock = block as TextContentBlock
181241
const nestedStatus = textBlock.status
182-
const isNestedStreamingText = p.parentIsStreaming || nestedStatus === 'running'
242+
const isNestedStreamingText =
243+
p.parentIsStreaming || nestedStatus === 'running'
183244
const filteredNestedContent = isNestedStreamingText
184245
? trimTrailingNewlines(textBlock.content)
185246
: textBlock.content.trim()
@@ -211,7 +272,8 @@ const AgentBody = memo(
211272

212273
if (block.type === 'html') {
213274
const htmlBlock = block as HtmlContentBlock
214-
const { marginTop, marginBottom } = extractHtmlBlockMargins(htmlBlock)
275+
const { marginTop, marginBottom } =
276+
extractHtmlBlockMargins(htmlBlock)
215277

216278
return (
217279
<box
@@ -268,7 +330,9 @@ export const AgentBranchWrapper = memo(
268330
}: AgentBranchWrapperProps) => {
269331
const theme = useTheme()
270332
// Derive streaming boolean for this specific agent to avoid re-renders when other agents change
271-
const agentIsStreaming = useChatStore((state) => state.streamingAgents.has(agentBlock.agentId))
333+
const agentIsStreaming = useChatStore((state) =>
334+
state.streamingAgents.has(agentBlock.agentId),
335+
)
272336

273337
if (shouldRenderAsSimpleText(agentBlock.agentType)) {
274338
const isStreaming = agentBlock.status === 'running' || agentIsStreaming
@@ -341,29 +405,15 @@ export const AgentBranchWrapper = memo(
341405
const isCollapsed = agentBlock.isCollapsed ?? false
342406
const isStreaming = agentBlock.status === 'running' || agentIsStreaming
343407

344-
const allTextContent =
345-
agentBlock.blocks
346-
?.filter(isTextBlock)
347-
.map((nested) => nested.content)
348-
.join('') || ''
349-
350-
const lines = allTextContent.split('\n').filter((line) => line.trim())
351-
const firstLine = lines[0] || ''
352-
353-
const streamingPreview = isStreaming
354-
? agentBlock.initialPrompt
355-
? sanitizePreview(agentBlock.initialPrompt)
356-
: `${sanitizePreview(firstLine)}...`
357-
: ''
358-
359-
const finishedPreview =
360-
!isStreaming && isCollapsed && agentBlock.initialPrompt
361-
? sanitizePreview(agentBlock.initialPrompt)
362-
: ''
408+
// Compute collapsed preview text
409+
const preview = getCollapsedPreview(agentBlock, isStreaming, isCollapsed)
363410

364-
const isActive = isStreaming || agentBlock.status === 'running'
365-
const { indicator: statusIndicator, label: statusLabel, color: statusColor } =
366-
getAgentStatusInfo(isActive ? 'running' : agentBlock.status, theme)
411+
const effectiveStatus = isStreaming ? 'running' : agentBlock.status
412+
const {
413+
indicator: statusIndicator,
414+
label: statusLabel,
415+
color: statusColor,
416+
} = getAgentStatusInfo(effectiveStatus, theme)
367417

368418
const onToggle = useCallback(() => {
369419
onToggleCollapsed(agentBlock.agentId)
@@ -377,8 +427,7 @@ export const AgentBranchWrapper = memo(
377427
agentId={agentBlock.agentId}
378428
isCollapsed={isCollapsed}
379429
isStreaming={isStreaming}
380-
streamingPreview={streamingPreview}
381-
finishedPreview={finishedPreview}
430+
preview={preview}
382431
statusLabel={statusLabel ?? undefined}
383432
statusColor={statusColor}
384433
statusIndicator={statusIndicator}

0 commit comments

Comments
 (0)