| 497 | 497 | |
| 498 | 498 | // The direction is consumed by exactly one generation. GENERATION_ENDED also fires when the |
| 499 | 499 | // nested option request finishes, which is why clearing is skipped while a roll is in flight. |
| 500 | | - const onGenerationDone = () => { |
| 500 | + // With streaming, the UI unlock that emits GENERATION_ENDED happens BEFORE MESSAGE_RECEIVED, |
| 501 | + // so only the injection may be cleared here — the pending stamp must survive until the |
| 502 | + // message events consume it (the interceptor resets it at the start of every next turn). |
| 503 | + eventSource.on(event_types.GENERATION_ENDED, () => { |
| 504 | + if (isRolling) { |
| 505 | + return; |
| 506 | + } |
| 507 | + clearDirectionInjection(); |
| 508 | + }); |
| 509 | + eventSource.on(event_types.GENERATION_STOPPED, () => { |
| 501 | 510 | if (isRolling) { |
| 502 | 511 | return; |
| 503 | 512 | } |
| 504 | 513 | pendingStamp = null; |
| 505 | 514 | clearDirectionInjection(); |
| 506 | 515 | }); |
| 507 | | - eventSource.on(event_types.GENERATION_ENDED, onGenerationDone); |
| 508 | | - eventSource.on(event_types.GENERATION_STOPPED, onGenerationDone); |
| 509 | 516 | } |