opentui(phase3): launcher integration — HERMES_TUI_ENGINE dual-engine

hermes --tui launches the native OpenTUI engine (Bun) when
HERMES_TUI_ENGINE=opentui (env) or display.tui_engine=opentui (config);
Ink stays the default and the shipping path is untouched.

- _resolve_tui_engine() (env > config > ink); refuses opentui on
  Windows/Termux (no Bun) -> falls back to ink with a notice.
- _make_opentui_argv() -> [bun, src/entry.real.tsx] (no build step).
- _bun_bin() with HERMES_BUN override.
- Branch at top of _make_tui_argv BEFORE _ensure_tui_node (Bun-only host
  must not bootstrap Node).
- Gate _launch_tui NODE_OPTIONS/--max-old-space-size on engine==ink (Bun
  is JSC; the V8 flag errors/ignores).

Verified end-to-end via tmux: real hermes --tui -> Bun -> OpenTUI ->
real Python gateway streamed a real reply. No-flag default still ink.
This commit is contained in:
alt-glitch 2026-06-08 11:11:54 +00:00
parent 24f74eb888
commit 2bd9c9b881
741 changed files with 17733 additions and 79889 deletions

View file

@ -365,40 +365,6 @@ class TestPluginDiscovery:
}
assert len(non_bundled) == 1
def test_failed_discovery_is_not_cached(self, tmp_path, monkeypatch):
"""A sweep that raises must not cache 'discovered' with no plugins.
Regression for the stranded-empty-registry class of failures: callers
(e.g. tools.web_tools._ensure_web_plugins_loaded) swallow discovery
exceptions as warnings, so if a failed sweep flipped ``_discovered``
permanently, every later call would early-return against an empty
registry ("No web provider configured") for the process lifetime.
"""
plugins_dir = tmp_path / "hermes_test" / "plugins"
_make_plugin_dir(plugins_dir, "retry_plugin")
monkeypatch.setenv("HERMES_HOME", str(tmp_path / "hermes_test"))
mgr = PluginManager()
def _boom(self_inner):
raise RuntimeError("sweep failed")
monkeypatch.setattr(PluginManager, "_discover_and_load_inner", _boom)
with pytest.raises(RuntimeError, match="sweep failed"):
mgr.discover_and_load()
assert mgr._discovered is False, "failed sweep was cached as discovered"
# A later call (with discovery healthy again) must do the real scan.
monkeypatch.undo()
monkeypatch.setenv("HERMES_HOME", str(tmp_path / "hermes_test"))
mgr.discover_and_load()
assert mgr._discovered is True
non_bundled = {
n: p for n, p in mgr._plugins.items()
if p.manifest.source != "bundled"
}
assert len(non_bundled) == 1
def test_discover_skips_dir_without_manifest(self, tmp_path, monkeypatch):
"""Directories without plugin.yaml are silently skipped."""
plugins_dir = tmp_path / "hermes_test" / "plugins"