mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-09 07:29:51 +00:00
feat(web): group consecutive tool-use cards (#604)
* feat(web): group consecutive tool-use cards Add a web-only visible projection that groups consecutive root-level execution tools into expandable cards. Keep approval and question tools standalone, reuse older-history loading on expand, and add regression coverage for grouping and UI behavior. * fix(web): hydrate oldest visible tool group Mark needsOlderHistory on the first visible grouped tool run even when earlier visible blocks are non-tool content, and add regression coverage for the boundary. * fix(web): continue grouped history hydration Decouple ToolGroupCard older-history chaining from the shared loading flag, invalidate stale hydration runs safely, and add regression coverage for multi-page hydration. * fix(web): harden grouped tool hydration - retry incomplete group hydration after transient pagination contention\n- keep approved and denied permissioned tool cards eligible for grouping\n- cover both regressions with targeted web tests * fix(web): keep Codex permission cards standalone - treat CodexPermission as a semantic grouping boundary even after approval\n- keep permissioned execution tools groupable while preserving permission milestones\n- add regression coverage for Codex permission eligibility and boundary behavior * fix(web): narrow incomplete tool-group hydration - only mark groups at the oldest visible boundary as needing older history\n- avoid auto-paginating complete groups behind text, standalone tools, or permission milestones\n- add regression coverage for the adjacent boundary cases
This commit is contained in:
@@ -16,6 +16,7 @@ import { normalizeDecryptedMessage } from '@/chat/normalize'
|
||||
import { reduceChatBlocks } from '@/chat/reducer'
|
||||
import { reconcileChatBlocks } from '@/chat/reconcile'
|
||||
import { buildConversationOutline } from '@/chat/outline'
|
||||
import { buildVisibleChatBlocks, isToolGroupBlock, type ToolGroupBlock } from '@/chat/toolGroups'
|
||||
import { isQueuedForInvocation } from '@/lib/messages'
|
||||
import { HappyComposer } from '@/components/AssistantChat/HappyComposer'
|
||||
import { HappyThread } from '@/components/AssistantChat/HappyThread'
|
||||
@@ -74,6 +75,7 @@ export function SessionChat(props: {
|
||||
const terminalSupported = isRemoteTerminalSupported(props.session.metadata)
|
||||
const normalizedCacheRef = useRef<Map<string, { source: DecryptedMessage; normalized: NormalizedMessage | null }>>(new Map())
|
||||
const blocksByIdRef = useRef<Map<string, ChatBlock>>(new Map())
|
||||
const visibleGroupsRef = useRef<ToolGroupBlock[]>([])
|
||||
const [forceScrollToken, setForceScrollToken] = useState(0)
|
||||
const [outlineOpen, setOutlineOpen] = useState(false)
|
||||
const agentFlavor = props.session.metadata?.flavor ?? null
|
||||
@@ -224,6 +226,7 @@ export function SessionChat(props: {
|
||||
useEffect(() => {
|
||||
normalizedCacheRef.current.clear()
|
||||
blocksByIdRef.current.clear()
|
||||
visibleGroupsRef.current = []
|
||||
setOutlineOpen(false)
|
||||
}, [props.session.id])
|
||||
|
||||
@@ -241,6 +244,7 @@ export function SessionChat(props: {
|
||||
if (prevSessionIdRef.current !== null && prevSessionIdRef.current !== props.session.id) {
|
||||
normalizedCacheRef.current.clear()
|
||||
blocksByIdRef.current.clear()
|
||||
visibleGroupsRef.current = []
|
||||
}
|
||||
prevSessionIdRef.current = props.session.id
|
||||
|
||||
@@ -279,6 +283,18 @@ export function SessionChat(props: {
|
||||
blocksByIdRef.current = reconciled.byId
|
||||
}, [reconciled.byId])
|
||||
|
||||
const visibleBlocks = useMemo(
|
||||
() => buildVisibleChatBlocks(reconciled.blocks, {
|
||||
hasMoreMessages: props.hasMoreMessages,
|
||||
previousGroups: visibleGroupsRef.current
|
||||
}),
|
||||
[reconciled.blocks, props.hasMoreMessages]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
visibleGroupsRef.current = visibleBlocks.filter(isToolGroupBlock)
|
||||
}, [visibleBlocks])
|
||||
|
||||
const outlineItems = useMemo(
|
||||
() => buildConversationOutline(reconciled.blocks),
|
||||
[reconciled.blocks]
|
||||
@@ -386,7 +402,7 @@ export function SessionChat(props: {
|
||||
|
||||
const runtime = useHappyRuntime({
|
||||
session: props.session,
|
||||
blocks: reconciled.blocks,
|
||||
blocks: visibleBlocks,
|
||||
isSending: props.isSending,
|
||||
onSendMessage: handleSend,
|
||||
onAbort: handleAbort,
|
||||
|
||||
Reference in New Issue
Block a user