93 lines
2.5 KiB
Markdown
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.
|