mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-17 14:42:06 +00:00
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:
parent
24f74eb888
commit
2bd9c9b881
741 changed files with 17733 additions and 79889 deletions
|
|
@ -822,75 +822,6 @@ def _remove_file(name: str, file_path: str) -> Dict[str, Any]:
|
|||
# Main entry point
|
||||
# =============================================================================
|
||||
|
||||
# ContextVar bypass: set while replaying an already-approved staged skill write
|
||||
# so skill_manage() does not re-gate (and re-stage) it.
|
||||
import contextvars as _ctxvars
|
||||
_skill_gate_bypass: "_ctxvars.ContextVar[bool]" = _ctxvars.ContextVar(
|
||||
"skill_gate_bypass", default=False
|
||||
)
|
||||
|
||||
|
||||
def _apply_skill_write_gate(action, name, **payload_kwargs):
|
||||
"""Evaluate the skill write gate. Returns a JSON tool-result string when the
|
||||
write should NOT proceed (blocked or staged), or None to perform the real
|
||||
write. Bypassed during approved-pending replay.
|
||||
"""
|
||||
if action not in {"create", "edit", "patch", "delete", "write_file", "remove_file"}:
|
||||
return None
|
||||
if _skill_gate_bypass.get():
|
||||
return None
|
||||
|
||||
try:
|
||||
from tools import write_approval as wa
|
||||
except Exception:
|
||||
return None # fail open
|
||||
|
||||
decision = wa.evaluate_gate(wa.SKILLS)
|
||||
if decision.allow:
|
||||
return None
|
||||
if decision.blocked:
|
||||
return tool_error(decision.message, success=False)
|
||||
|
||||
# stage — record the full skill_manage kwargs so approval can replay it.
|
||||
payload = {"action": action, "name": name}
|
||||
payload.update({k: v for k, v in payload_kwargs.items() if v is not None})
|
||||
gist = wa.skill_gist(
|
||||
action, name,
|
||||
content=payload_kwargs.get("content") or "",
|
||||
file_path=payload_kwargs.get("file_path") or "",
|
||||
old_string=payload_kwargs.get("old_string") or "",
|
||||
new_string=payload_kwargs.get("new_string") or "",
|
||||
)
|
||||
record = wa.stage_write(wa.SKILLS, payload, summary=gist, origin=wa.current_origin())
|
||||
return json.dumps(
|
||||
{"success": True, "staged": True, "pending_id": record["id"],
|
||||
"gist": gist, "message": decision.message},
|
||||
ensure_ascii=False,
|
||||
)
|
||||
|
||||
|
||||
def apply_skill_pending(payload: Dict[str, Any]) -> str:
|
||||
"""Replay a staged skill write, bypassing the gate. Returns the tool result
|
||||
JSON string. Called by the /skills approve handler.
|
||||
"""
|
||||
token = _skill_gate_bypass.set(True)
|
||||
try:
|
||||
return skill_manage(
|
||||
action=payload.get("action", ""),
|
||||
name=payload.get("name", ""),
|
||||
content=payload.get("content"),
|
||||
category=payload.get("category"),
|
||||
file_path=payload.get("file_path"),
|
||||
file_content=payload.get("file_content"),
|
||||
old_string=payload.get("old_string"),
|
||||
new_string=payload.get("new_string"),
|
||||
replace_all=payload.get("replace_all", False),
|
||||
absorbed_into=payload.get("absorbed_into"),
|
||||
)
|
||||
finally:
|
||||
_skill_gate_bypass.reset(token)
|
||||
|
||||
|
||||
def skill_manage(
|
||||
action: str,
|
||||
name: str,
|
||||
|
|
@ -908,19 +839,6 @@ def skill_manage(
|
|||
|
||||
Returns JSON string with results.
|
||||
"""
|
||||
# Approval gate: when on, stages the write for review (skills are too large
|
||||
# to review inline, so they always stage regardless of origin); when off
|
||||
# (default) passes straight through. The gate is bypassed when this call is
|
||||
# itself replaying an already-approved staged write (_skill_apply_pending).
|
||||
gate_result = _apply_skill_write_gate(
|
||||
action, name, content=content, category=category,
|
||||
file_path=file_path, file_content=file_content,
|
||||
old_string=old_string, new_string=new_string,
|
||||
replace_all=replace_all, absorbed_into=absorbed_into,
|
||||
)
|
||||
if gate_result is not None:
|
||||
return gate_result
|
||||
|
||||
if action == "create":
|
||||
if not content:
|
||||
return tool_error("content is required for 'create'. Provide the full SKILL.md text (frontmatter + body).", success=False)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue