feat(tui): Esc interrupts turn + configurable display.interrupt_key - #21720
feat(tui): Esc interrupts turn + configurable display.interrupt_key#21720rxdxxxx wants to merge 1 commit into
Conversation
2da5dae to
0f3312d
Compare
0f3312d to
83ca8dd
Compare
teknium1
left a comment
There was a problem hiding this comment.
Thanks for the configurable-keybinding implementation. The feature is still needed on current main, but this branch needs a few changes before it can provide the advertised behavior.
Problems
- The new global guard in
ui-tui/src/app/useInputHandlers.ts:429does not make custom bindings reachable from the focused composer.TextInputreturns early unlessshouldPassThroughToGlobalHandler()permits the event (ui-tui/src/components/textInput.tsx:924-937); that predicate only knows Ctrl+C/Ctrl+X, navigation, Escape, and the voice key (:1344-1356). Thus the documentedctrl+gexample is consumed by the composer rather than interrupting. display.interrupt_keyis not registered inhermes_cli/config.py'sDEFAULT_CONFIG. The dashboard config schema is generated from that object athermes_cli/web_server.py:813, so the setting is not a canonical/defaulted config field.ui-tui/src/components/appLayout.tsxandui-tui/src/content/hotkeys.tsstill display Esc after a custom binding is selected.
Suggested changes
- Thread
interruptKeythroughTextInputand its pass-through predicate, then add focused-composer, parser, and config-reload tests alongside the existing TUI keybinding suites. - Add the default configuration entry and document the TUI-only keybinding; render the configured key in user-facing hints.
Automated hermes-sweeper review.
Addresses sweeper review on NousResearch#21707 and NousResearch#21720: PR NousResearch#21707 fixes: - Expose clearCompletions() from useCompletion hook - First Esc dismisses completions, second Esc interrupts the turn - Add Esc to /hotkeys table with full precedence description - Update busy placeholder to 'Esc / Ctrl+C to interrupt…' - Add interruptKey.test.ts with parseInterruptKey/isInterruptKey tests PR NousResearch#21720 fixes: - Add parseInterruptKey + isInterruptKey + formatInterruptKey to platform.ts - Add interrupt_key to ConfigDisplayConfig (gatewayTypes.ts) - Add interrupt_key default to hermes_cli/config_defaults.py - Thread interruptKey through useConfigSync → useMainApp → useInputHandlers - Add interruptKey to shouldPassThroughToGlobalHandler so custom keys (e.g. ctrl+g) are not consumed by the focused TextInput composer - Pass interruptKey prop to TextInput component - Dynamic placeholder renders the configured key label via formatInterruptKey - Add pass-through tests for custom interrupt key in textInputPassThrough.test Guards preserved (overlay > voice chord > queue-edit > selection > completions > interrupt): - Blocked overlays own their Esc handlers — interrupt unreachable while blocked - Voice-toggle chord (ctrl/alt+escape) wins before generic Esc handlers - Queue-edit cancel and selection-clear take precedence over interrupt - Completions dismiss on first Esc; interrupt fires only when list is empty - Ctrl+C behavior entirely unchanged
Addresses sweeper review on NousResearch#21707 and NousResearch#21720: PR NousResearch#21707 fixes: - Expose clearCompletions() from useCompletion hook - First Esc dismisses completions, second Esc interrupts the turn - Add Esc to /hotkeys table with full precedence description - Update busy placeholder to 'Esc / Ctrl+C to interrupt…' - Add interruptKey.test.ts with parseInterruptKey/isInterruptKey tests PR NousResearch#21720 fixes: - Add parseInterruptKey + isInterruptKey + formatInterruptKey to platform.ts - Add interrupt_key to ConfigDisplayConfig (gatewayTypes.ts) - Add interrupt_key default to hermes_cli/config_defaults.py - Thread interruptKey through useConfigSync → useMainApp → useInputHandlers - Add interruptKey to shouldPassThroughToGlobalHandler so custom keys (e.g. ctrl+g) are not consumed by the focused TextInput composer - Pass interruptKey prop to TextInput component - Dynamic placeholder renders the configured key label via formatInterruptKey - Add pass-through tests for custom interrupt key in textInputPassThrough.test Guards preserved (overlay > voice chord > queue-edit > selection > completions > interrupt): - Blocked overlays own their Esc handlers — interrupt unreachable while blocked - Voice-toggle chord (ctrl/alt+escape) wins before generic Esc handlers - Queue-edit cancel and selection-clear take precedence over interrupt - Completions dismiss on first Esc; interrupt fires only when list is empty - Ctrl+C behavior entirely unchanged
83ca8dd to
b85752a
Compare
|
Thanks for the detailed review! All 3 issues have been addressed in the force-push ( 1. Custom bindings unreachable from focused composer
2. Missing
|
Summary
Pressing Esc now interrupts a running agent turn in the TUI — no configuration needed. First press dismisses the completion list; a second press interrupts the turn.
Supersedes #21707 (base feature) + addresses all review feedback from hermes-sweeper.
Scope: TUI only. CLI (prompt_toolkit) interrupt support is out of scope, tracked separately. Partially addresses #65303 (TUI only). Closes #21707.
Optionally configurable
The interrupt key defaults to Esc but can be changed:
Hot-reloads on config save — no restart needed.
Changes (14 files, +332/-33)
platform.tsParsedInterruptKeytype,parseInterruptKey,isInterruptKey,formatInterruptKey— independent from voice key logic (no Cmd+B fallback leak, no voice-specific reserved-key rejection)gatewayTypes.tsinterrupt_key?: unknownonConfigDisplayConfigconfig_defaults.py"interrupt_key": "escape"inDEFAULT_CONFIG["display"]— dashboard schema can now discover this settinguseConfigSync.tsapplyDisplayreads/propagates interrupt_key + collision detection (falls back to Esc if same as voice key)interfaces.tsinterruptKeyonInputHandlerContext,ComposerActions.clearCompletions,AppLayoutComposerProps.interruptKeyuseMainApp.tsuseConfigSync/useInputHandlers/ composer propsuseInputHandlers.tsisInterruptKey)useCompletion.tsclearCompletionsexported as stableuseCallback, single source of truthuseComposerState.tsclearCompletionsinComposerActionstextInput.tsxshouldPassThroughToGlobalHandleracceptsinterruptKeyas 4th param — custom keys (e.g.ctrl+g) pass through the composerappLayout.tsxformatInterruptKeyhotkeys.tsEsc (or interrupt_key)interruptKey.test.tstextInputPassThrough.test.tsReview feedback addressed
interruptKeythreaded intoshouldPassThroughToGlobalHandlersoctrl+getc. are not swallowed by TextInputconfig_defaults.pynow includes"interrupt_key": "escape"Design decisions
parseInterruptKeyis standalone (not delegating toparseVoiceRecordKey) — only rejectsctrl+c, not voice-reserved keys likectrl+lisInterruptKeyis standalone (not delegating toisVoiceToggleKey) — no macOS Cmd+B muscle-memory fallbackapplyDisplay: if interrupt_key matches voice record_key, silently fall back to EscCtrl+Cbehavior is unchanged and hardcodedKey priority (high to low)