Files
paseo/.tasks/9b83b90a.md
T

93 lines
2.5 KiB
Markdown

---
id: 9b83b90a
title: Refactor stream buffering to head/tail model
status: open
deps: []
created: 2026-01-11T08:48:35.444Z
---
## Goal
Replace the current special-case buffering logic with a unified head/tail model where:
- **Tail**: Committed StreamItem[] (history)
- **Head**: Active StreamItem[] (being streamed, same type as tail)
Both use the same `reduceStreamUpdate()` function. The only difference is that head items get flushed to tail when a stream item completes.
## Current Problems
1. `applyStreamEventWithBuffer()` has hardcoded assistant_message buffering
2. `StreamingBufferEntry` is a different type than `StreamItem`
3. Reasoning chunks go through reducer directly (no buffering)
4. Duplicate logic between `appendAssistantMessage()` and buffer handling
## Design
### Types
```typescript
// Head is just StreamItem[] - no special type needed
// Tail is just StreamItem[] - same as before
interface StreamState {
tail: StreamItem[]; // committed history
head: StreamItem[]; // active streaming items
}
```
### Core Function
```typescript
function applyStreamEvent(params: {
tail: StreamItem[];
head: StreamItem[];
event: AgentStreamEventPayload;
timestamp: Date;
}): {
tail: StreamItem[];
head: StreamItem[];
changedTail: boolean;
changedHead: boolean;
}
```
### Flush Logic
Head items are flushed to tail when:
1. A different item kind arrives (thought → tool_call flushes the thought)
2. Turn completion events (turn_completed, turn_failed, turn_canceled)
3. User message arrives (new turn starting)
### Implementation Steps
1. Remove `StreamingBufferEntry` type
2. Remove `StreamingHead` type (or repurpose)
3. Modify `applyStreamEventWithBuffer` → `applyStreamEvent` to work with head/tail
4. Head uses same `reduceStreamUpdate` as tail
5. Add flush logic based on item kind transitions
6. Update session-context.tsx to use new API
7. Update agent-stream-view.tsx to render head items
8. Update session-store.ts types
## Flush Conditions
```typescript
function shouldFlushHead(head: StreamItem[], incomingKind: StreamItemKind): boolean {
if (head.length === 0) return false;
const lastHeadKind = head[head.length - 1].kind;
// Different kind arriving = flush
if (lastHeadKind !== incomingKind) return true;
return false;
}
```
## Rendering
In agent-stream-view.tsx:
- Tail is rendered in FlatList (reversed, virtualized)
- Head is rendered in ListHeaderComponent (always visible, not virtualized since it's small)
This is similar to current approach but head can now contain any StreamItem types.