feat: add opencode provider and refactor stream model to head/tail architecture

This commit is contained in:
Mohamed Boudra
2026-01-11 16:00:25 +07:00
parent 7fe10f9b4a
commit ebabcc09e5
42 changed files with 4416 additions and 2926 deletions
+92
View File
@@ -0,0 +1,92 @@
---
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.