Repository navigation
Implement useSDKEvents hook for event stream integration #11771
Copy link
Copy link
Closed as not planned
Description
Parent Epic
Part of #11762 (Ink-based TUI Rewrite)
Objective
Create React hook that subscribes to SDK events and dispatches state updates. This is the critical integration point between the backend and UI.
Architecture
function useSDKEvents(sessionId: string | null, dispatch: Dispatch<Action>) {
useEffect(() => {
if (!sessionId) return
const abortController = new AbortController()
;(async () => {
const client = createOpencodeClient({
baseURL: `http://localhost:${port}`,
// auth headers...
})
const events = await client.event.subscribe({
signal: abortController.signal
})
for await (const event of events.stream) {
// Dispatch actions based on event type
switch (event.type) {
case 'part.updated':
handlePartUpdated(event, dispatch)
break
case 'message.created':
handleMessageCreated(event, dispatch)
break
// etc.
}
}
})()
return () => abortController.abort()
}, [sessionId, dispatch])
}Event Types to Handle
| SDK Event | Action | Purpose |
|---|---|---|
part.updated (tool) |
TOOL_START / TOOL_END |
Track tool status |
part.updated (task) |
TASK_START / TASK_UPDATE_CHILD / TASK_END |
Track task + child |
message.created |
MESSAGE_COMPLETE |
Finalize message |
session.updated |
SET_SESSION |
Session metadata |
Child Session Tracking
The hardest part - mapping child session events to parent tasks:
function handlePartUpdated(event: PartUpdatedEvent, dispatch: Dispatch<Action>) {
const part = event.properties.part
// If this is a task tool, extract child session ID from metadata
if (part.tool === 'task' && part.state.metadata?.sessionId) {
dispatch({
type: 'TASK_REGISTER_CHILD_SESSION',
taskId: part.callID,
childSessionId: part.state.metadata.sessionId
})
}
// If this is from a child session, find parent task
if (part.sessionID !== currentSessionId) {
const parentTaskId = childSessionToTask.get(part.sessionID)
if (parentTaskId) {
dispatch({
type: 'TASK_UPDATE_CHILD',
taskId: parentTaskId,
childTool: {
name: part.tool,
summary: part.state.title || 'running',
status: part.state.status
}
})
}
}
}Tasks
- Create
src/cli/ink/hooks/useSDKEvents.ts - Implement SDK client setup with auth
- Implement event stream subscription
- Handle tool part updates
- Handle task part updates with child session mapping
- Handle message completion
- Implement cleanup on unmount
- Handle reconnection on disconnect
- Add error handling for stream failures
State Maps Needed
// In reducer or context
const childSessionToTask = new Map<string, string>() // child session ID -> task call ID
const taskToChildSession = new Map<string, string>() // task call ID -> child session IDAcceptance Criteria
- Events flow from SDK to UI state
- Tools display and update correctly
- Tasks display with child tool status
- Stream reconnects on failure
- Cleanup happens on unmount
Estimated Effort
2.5 days
Risk
HIGH - This is the core complexity. The SDK event handling is the same regardless of UI framework.
Dependencies
- Setup Ink dependencies and verify Bun compatibility #11763 (Setup Ink dependencies)
- Implement core App component and state management #11764 (App component with reducer)
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels
Activity
randomm commentedon Feb 2, 2026
SDK Event Types Research
Source
/packages/sdk/js/src/v2/gen/types.gen.tsCore Events for TUI
message.part.updatedsession.statussession.errorquestion.askedpermission.askedtui.toast.showMessage Part Types (discriminated union)
text- Streaming content with optionaldeltatool- Tool with state:pending|running|completed|errorreasoning- AI thinkingsubtask- Subagent tasksstep-start/step-finish- Step markersImplementation Notes
deltafield enables incremental text updates (efficient)randomm commentedon Feb 3, 2026
Reopening - Phase 1 & 2 components merged successfully. Proceeding with hook implementation.