mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-04-28 06:51:16 +08:00
fix(tui): keep todo pinned outside transcript
This commit is contained in:
@@ -16,6 +16,7 @@ import type {
|
|||||||
} from '../gatewayTypes.js'
|
} from '../gatewayTypes.js'
|
||||||
import { useGitBranch } from '../hooks/useGitBranch.js'
|
import { useGitBranch } from '../hooks/useGitBranch.js'
|
||||||
import { useVirtualHistory } from '../hooks/useVirtualHistory.js'
|
import { useVirtualHistory } from '../hooks/useVirtualHistory.js'
|
||||||
|
import { appendTranscriptMessage } from '../lib/messages.js'
|
||||||
import { asRpcResult, rpcErrorMessage } from '../lib/rpc.js'
|
import { asRpcResult, rpcErrorMessage } from '../lib/rpc.js'
|
||||||
import { terminalParityHints } from '../lib/terminalParity.js'
|
import { terminalParityHints } from '../lib/terminalParity.js'
|
||||||
import { buildToolTrailLine, sameToolTrailGroup, toolTrailLabel } from '../lib/text.js'
|
import { buildToolTrailLine, sameToolTrailGroup, toolTrailLabel } from '../lib/text.js'
|
||||||
@@ -198,7 +199,10 @@ export function useMainApp(gw: GatewayClient) {
|
|||||||
[selection]
|
[selection]
|
||||||
)
|
)
|
||||||
|
|
||||||
const appendMessage = useCallback((msg: Msg) => setHistoryItems(prev => capHistory([...prev, msg])), [])
|
const appendMessage = useCallback(
|
||||||
|
(msg: Msg) => setHistoryItems(prev => capHistory(appendTranscriptMessage(prev, msg))),
|
||||||
|
[]
|
||||||
|
)
|
||||||
|
|
||||||
const sys = useCallback((text: string) => appendMessage({ role: 'system', text }), [appendMessage])
|
const sys = useCallback((text: string) => appendMessage({ role: 'system', text }), [appendMessage])
|
||||||
|
|
||||||
|
|||||||
@@ -28,10 +28,10 @@ const TranscriptPane = memo(function TranscriptPane({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<LiveTodoPanel />
|
||||||
|
|
||||||
<ScrollBox flexDirection="column" flexGrow={1} flexShrink={1} ref={transcript.scrollRef} stickyScroll>
|
<ScrollBox flexDirection="column" flexGrow={1} flexShrink={1} ref={transcript.scrollRef} stickyScroll>
|
||||||
<Box flexDirection="column" paddingX={1}>
|
<Box flexDirection="column" paddingX={1}>
|
||||||
<LiveTodoPanel />
|
|
||||||
|
|
||||||
{transcript.virtualHistory.topSpacer > 0 ? <Box height={transcript.virtualHistory.topSpacer} /> : null}
|
{transcript.virtualHistory.topSpacer > 0 ? <Box height={transcript.virtualHistory.topSpacer} /> : null}
|
||||||
|
|
||||||
{transcript.virtualRows.slice(transcript.virtualHistory.start, transcript.virtualHistory.end).map(row => (
|
{transcript.virtualRows.slice(transcript.virtualHistory.start, transcript.virtualHistory.end).map(row => (
|
||||||
|
|||||||
@@ -4,6 +4,6 @@ import { liveTailOrder } from './liveLayout.js'
|
|||||||
|
|
||||||
describe('liveTailOrder', () => {
|
describe('liveTailOrder', () => {
|
||||||
it('keeps todo before transcript and assistant live output', () => {
|
it('keeps todo before transcript and assistant live output', () => {
|
||||||
expect(liveTailOrder()).toEqual(['todo', 'history', 'assistant'])
|
expect(liveTailOrder()).toEqual(['todo', 'scroll-history', 'assistant'])
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export const liveTailOrder = () => ['todo', 'history', 'assistant'] as const
|
export const liveTailOrder = () => ['todo', 'scroll-history', 'assistant'] as const
|
||||||
|
|||||||
23
ui-tui/src/lib/messages.test.ts
Normal file
23
ui-tui/src/lib/messages.test.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { describe, expect, it } from 'vitest'
|
||||||
|
|
||||||
|
import { appendTranscriptMessage } from './messages.js'
|
||||||
|
|
||||||
|
describe('appendTranscriptMessage', () => {
|
||||||
|
it('merges adjacent tool-only shelves into one transcript row', () => {
|
||||||
|
const out = appendTranscriptMessage(
|
||||||
|
[{ kind: 'trail', role: 'system', text: '', tools: ['Terminal("one") ✓'] }],
|
||||||
|
{ kind: 'trail', role: 'system', text: '', tools: ['Terminal("two") ✓'] }
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(out).toEqual([{ kind: 'trail', role: 'system', text: '', tools: ['Terminal("one") ✓', 'Terminal("two") ✓'] }])
|
||||||
|
})
|
||||||
|
|
||||||
|
it('does not merge tool shelves across thinking text', () => {
|
||||||
|
const out = appendTranscriptMessage(
|
||||||
|
[{ kind: 'trail', role: 'system', text: '', thinking: 'plan', tools: ['Terminal("one") ✓'] }],
|
||||||
|
{ kind: 'trail', role: 'system', text: '', tools: ['Terminal("two") ✓'] }
|
||||||
|
)
|
||||||
|
|
||||||
|
expect(out).toHaveLength(2)
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -1,4 +1,17 @@
|
|||||||
import type { Msg, Role } from '../types.js'
|
import type { Msg, Role } from '../types.js'
|
||||||
|
|
||||||
|
const isToolShelf = (msg: Msg | undefined) =>
|
||||||
|
Boolean(msg?.kind === 'trail' && !msg.text && !msg.thinking?.trim() && msg.tools?.length)
|
||||||
|
|
||||||
|
export const appendTranscriptMessage = (prev: Msg[], msg: Msg): Msg[] => {
|
||||||
|
if (isToolShelf(msg) && isToolShelf(prev.at(-1))) {
|
||||||
|
const last = prev.at(-1)!
|
||||||
|
|
||||||
|
return [...prev.slice(0, -1), { ...last, tools: [...(last.tools ?? []), ...(msg.tools ?? [])] }]
|
||||||
|
}
|
||||||
|
|
||||||
|
return [...prev, msg]
|
||||||
|
}
|
||||||
|
|
||||||
export const upsert = (prev: Msg[], role: Role, text: string): Msg[] =>
|
export const upsert = (prev: Msg[], role: Role, text: string): Msg[] =>
|
||||||
prev.at(-1)?.role === role ? [...prev.slice(0, -1), { role, text }] : [...prev, { role, text }]
|
prev.at(-1)?.role === role ? [...prev.slice(0, -1), { role, text }] : [...prev, { role, text }]
|
||||||
|
|||||||
Reference in New Issue
Block a user