ESC Interrupt Plan for REPL Streaming/Tool Execution¶
Goal¶
Enable users to interrupt an active LLM stream or in-progress tool execution by pressing esc, without exiting the REPL.
Current state (relevant locations)¶
- Stream starts from
handleEnterKeywithcontext.Background(): internal/cli/repl/repl.go- Key handling is centralized in
handleKeyMsg: internal/cli/repl/handlers.go- Stream lifecycle/UI updates are driven by:
handleLLMChunk,handleLLMDone,handleLLMError,handleToolStart,handleToolEnd- Stream active state is tracked by
StreamHandler.
Because stream startup uses a non-cancelable context, there is no current mechanism for keyboard interruption.
Implementation plan¶
1) Add in-flight cancellation state to replModel¶
- Add
streamCancel context.CancelFunctoreplModel. - Add small helpers to set/clear this cancel func cleanly.
2) Start stream with cancelable context¶
- In
handleEnterKey, replacecontext.Background()withcontext.WithCancel(...). - Store the cancel func in
replModelbefore callingStreamChat.
3) Add esc key handling in handleKeyMsg¶
- Add key constant for
escinhandlers.go. - In
handleKeyMsg, onesc: - If stream is active (
m.streamHandler.IsActive()):- call
m.streamCancel()if non-nil, - stop spinner,
- interrupt/reset stream handler state,
- append muted status line like
Interrupted (Esc), - refresh viewport.
- call
- If stream is not active: no-op.
4) Add explicit stream interruption/reset path¶
- Add
Interrupt()(or equivalent) toStreamHandlerfor immediate state reset. - Use it from ESC path so UI updates instantly.
5) Handle cancellation errors gracefully¶
- In
handleLLMError, detect context cancellation and treat it as expected interruption. - Avoid showing cancellation as a red failure error.
- Avoid duplicate interruption messages.
6) Ensure cancellation cleanup¶
- Clear stored cancel func on all terminal paths:
- normal done,
- stream error,
- ESC interruption.
Granular TODO list¶
- [ ] Add
streamCancel context.CancelFunctoreplModel. - [ ] Add helper methods to set/clear stream cancel state.
- [ ] Update
handleEnterKeyto createcontext.WithCanceland persist cancel func. - [ ] Add
keyEscconstant inhandlers.go. - [ ] Add
escbranch tohandleKeyMsg. - [ ] Gate ESC interruption with
m.streamHandler.IsActive(). - [ ] In ESC branch: call cancel func, set
showSpinner=false. - [ ] Add
StreamHandler.Interrupt()(or equivalent reset) and call it. - [ ] Add muted interruption line to output and update viewport.
- [ ] In
handleLLMError, detect/suppress expected context-canceled error UX. - [ ] Clear cancel func in done/error/interrupt paths.
- [ ] Add tests:
- [ ]
Escduring active stream interrupts and does not quit. - [ ]
Escwhile idle is no-op. - [ ] Cancellation path does not render failure-style error.
- [ ] Existing
ctrl+cbehavior remains intact.
Suggested validation checklist¶
- Start a prompt that streams tokens; press
escmid-stream. - Stream stops quickly.
- REPL remains interactive.
- Trigger a long-running tool call; press
esc. - Tool flow is interrupted.
- No stuck spinner.
- Confirm no duplicate interruption/error lines.
- Confirm normal completion path still works unchanged.