mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-15 14:22:43 +00:00
fix(kanban): no-TTY gate in _wants_tui_early — the actual worker-crash fix
The earlier fix gated _resolve_use_tui, but the EARLY launcher (_wants_tui_early) decides TUI from display.interface before cmd_chat runs — so a `display.interface: tui` default still booted the Ink UI for headless spawns (kanban workers), whose no-TTY bail-out exits 0 → "protocol violation". Gate the early resolver on a real TTY: headless stdio never boots the TUI regardless of config; explicit --tui still does.
This commit is contained in:
parent
77db9d6bf3
commit
b06e2f846c
2 changed files with 31 additions and 2 deletions
|
|
@ -148,7 +148,17 @@ def _wants_tui_early(argv: "list[str] | None" = None) -> bool:
|
|||
"""Earliest TUI decision, usable before argparse/config imports.
|
||||
|
||||
Precedence: explicit ``--cli`` wins (forces classic REPL), then
|
||||
``--tui``/``HERMES_TUI=1``, then ``display.interface`` in config.
|
||||
explicit ``--tui``/``HERMES_TUI=1``, then a real-TTY gate (a
|
||||
non-interactive stdio can't host the Ink UI, so ambient config never
|
||||
boots it there), then ``display.interface`` in config.
|
||||
|
||||
The TTY gate is load-bearing for headless spawners — kanban workers,
|
||||
cron jobs, pipes run ``hermes … chat -q`` with stdio on a pipe. This
|
||||
is the earliest launch decision (it runs before ``cmd_chat`` /
|
||||
``_resolve_use_tui``), so a ``display.interface: tui`` default used to
|
||||
boot the TUI here — whose no-TTY bail-out exits 0 without doing the
|
||||
task → "protocol violation" on every attempt. An explicit ``--tui``
|
||||
still reaches the informative bail-out.
|
||||
"""
|
||||
if argv is None:
|
||||
argv = sys.argv[1:]
|
||||
|
|
@ -156,6 +166,11 @@ def _wants_tui_early(argv: "list[str] | None" = None) -> bool:
|
|||
return False
|
||||
if os.environ.get("HERMES_TUI") == "1" or "--tui" in argv:
|
||||
return True
|
||||
try:
|
||||
if not (sys.stdin.isatty() and sys.stdout.isatty()):
|
||||
return False
|
||||
except Exception:
|
||||
return False
|
||||
return _config_default_interface_early() == "tui"
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -153,10 +153,24 @@ class TestWantsTuiEarly:
|
|||
|
||||
return _make
|
||||
|
||||
def test_config_tui_bare_argv(self, home_with_interface):
|
||||
def test_config_tui_bare_argv(self, home_with_interface, monkeypatch):
|
||||
home_with_interface("tui")
|
||||
_fake_tty(monkeypatch, True) # config-tui only applies on a real TTY
|
||||
assert m._wants_tui_early([]) is True
|
||||
|
||||
def test_no_tty_blocks_config_tui(self, home_with_interface, monkeypatch):
|
||||
# Headless (worker/cron/pipe): ambient config-tui must not boot the
|
||||
# Ink UI in the earliest launch decision — that's the crash the
|
||||
# kanban worker hit before the gate existed.
|
||||
home_with_interface("tui")
|
||||
_fake_tty(monkeypatch, False)
|
||||
assert m._wants_tui_early([]) is False
|
||||
|
||||
def test_explicit_tui_flag_survives_no_tty(self, home_with_interface, monkeypatch):
|
||||
home_with_interface("cli")
|
||||
_fake_tty(monkeypatch, False)
|
||||
assert m._wants_tui_early(["--tui"]) is True
|
||||
|
||||
def test_cli_flag_overrides_config_tui(self, home_with_interface):
|
||||
home_with_interface("tui")
|
||||
assert m._wants_tui_early(["--cli"]) is False
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue