| 139 | * (reliable), not here on stop events (abort/error paths skip those). |
| 140 | */ |
| 141 | export function accumulateStreamEvents( |
| 142 | buffer: SDKPartialAssistantMessage[], |
| 143 | state: StreamAccumulatorState, |
| 144 | ): EventPayload[] { |
| 145 | const out: EventPayload[] = [] |
| 146 | // chunks[] → snapshot already in `out` this flush. Keyed by the chunks |
| 147 | // array reference (stable per {messageId, index}) so subsequent deltas |
| 148 | // rewrite the same entry instead of emitting one event per delta. |
| 149 | const touched = new Map<string[], CoalescedStreamEvent>() |
| 150 | for (const msg of buffer) { |
| 151 | switch (msg.event.type) { |
| 152 | case 'message_start': { |
| 153 | const id = msg.event.message.id |
| 154 | const prevId = state.scopeToMessage.get(scopeKey(msg)) |
| 155 | if (prevId) state.byMessage.delete(prevId) |
| 156 | state.scopeToMessage.set(scopeKey(msg), id) |
| 157 | state.byMessage.set(id, []) |
| 158 | out.push(msg) |
| 159 | break |
| 160 | } |
| 161 | case 'content_block_delta': { |
| 162 | if (msg.event.delta.type !== 'text_delta') { |
| 163 | out.push(msg) |
| 164 | break |
| 165 | } |
| 166 | const messageId = state.scopeToMessage.get(scopeKey(msg)) |
| 167 | const blocks = messageId ? state.byMessage.get(messageId) : undefined |
| 168 | if (!blocks) { |
| 169 | // Delta without a preceding message_start (reconnect mid-stream, |
| 170 | // or message_start was in a prior buffer that got dropped). Pass |
| 171 | // through raw — can't produce a full-so-far snapshot without the |
| 172 | // prior chunks anyway. |
| 173 | out.push(msg) |
| 174 | break |
| 175 | } |
| 176 | const chunks = (blocks[msg.event.index] ??= []) |
| 177 | chunks.push(msg.event.delta.text) |
| 178 | const existing = touched.get(chunks) |
| 179 | if (existing) { |
| 180 | existing.event.delta.text = chunks.join('') |
| 181 | break |
| 182 | } |
| 183 | const snapshot: CoalescedStreamEvent = { |
| 184 | type: 'stream_event', |
| 185 | uuid: msg.uuid, |
| 186 | session_id: msg.session_id, |
| 187 | parent_tool_use_id: msg.parent_tool_use_id, |
| 188 | event: { |
| 189 | type: 'content_block_delta', |
| 190 | index: msg.event.index, |
| 191 | delta: { type: 'text_delta', text: chunks.join('') }, |
| 192 | }, |
| 193 | } |
| 194 | touched.set(chunks, snapshot) |
| 195 | out.push(snapshot) |
| 196 | break |
| 197 | } |
| 198 | default: |