mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-05-05 02:07:34 +08:00
Compare commits
1 Commits
fix/plugin
...
hermes/her
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
20b0e62f72 |
@@ -5,6 +5,7 @@ Used by AIAgent._execute_tool_calls for CLI feedback.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import logging
|
||||||
import os
|
import os
|
||||||
import random
|
import random
|
||||||
import sys
|
import sys
|
||||||
@@ -15,6 +16,8 @@ import time
|
|||||||
_RED = "\033[31m"
|
_RED = "\033[31m"
|
||||||
_RESET = "\033[0m"
|
_RESET = "\033[0m"
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
# =========================================================================
|
# =========================================================================
|
||||||
# Skin-aware helpers (lazy import to avoid circular deps)
|
# Skin-aware helpers (lazy import to avoid circular deps)
|
||||||
@@ -363,7 +366,7 @@ def _detect_tool_failure(tool_name: str, result: str | None) -> tuple[bool, str]
|
|||||||
if exit_code is not None and exit_code != 0:
|
if exit_code is not None and exit_code != 0:
|
||||||
return True, f" [exit {exit_code}]"
|
return True, f" [exit {exit_code}]"
|
||||||
except (json.JSONDecodeError, TypeError, AttributeError):
|
except (json.JSONDecodeError, TypeError, AttributeError):
|
||||||
pass
|
logger.debug("Could not parse terminal result as JSON for exit code check")
|
||||||
return False, ""
|
return False, ""
|
||||||
|
|
||||||
# Memory-specific: distinguish "full" from real errors
|
# Memory-specific: distinguish "full" from real errors
|
||||||
@@ -373,7 +376,7 @@ def _detect_tool_failure(tool_name: str, result: str | None) -> tuple[bool, str]
|
|||||||
if data.get("success") is False and "exceed the limit" in data.get("error", ""):
|
if data.get("success") is False and "exceed the limit" in data.get("error", ""):
|
||||||
return True, " [full]"
|
return True, " [full]"
|
||||||
except (json.JSONDecodeError, TypeError, AttributeError):
|
except (json.JSONDecodeError, TypeError, AttributeError):
|
||||||
pass
|
logger.debug("Could not parse memory result as JSON for capacity check")
|
||||||
|
|
||||||
# Generic heuristic for non-terminal tools
|
# Generic heuristic for non-terminal tools
|
||||||
lower = result[:500].lower()
|
lower = result[:500].lower()
|
||||||
|
|||||||
@@ -159,8 +159,8 @@ def _read_skill_description(skill_file: Path, max_chars: int = 60) -> str:
|
|||||||
if len(desc) > max_chars:
|
if len(desc) > max_chars:
|
||||||
desc = desc[:max_chars - 3] + "..."
|
desc = desc[:max_chars - 3] + "..."
|
||||||
return desc
|
return desc
|
||||||
except Exception:
|
except Exception as e:
|
||||||
pass
|
logger.debug("Failed to read skill description from %s: %s", skill_file, e)
|
||||||
return ""
|
return ""
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1613,12 +1613,12 @@ def _cleanup_old_screenshots(screenshots_dir, max_age_hours=24):
|
|||||||
cutoff = time.time() - (max_age_hours * 3600)
|
cutoff = time.time() - (max_age_hours * 3600)
|
||||||
for f in screenshots_dir.glob("browser_screenshot_*.png"):
|
for f in screenshots_dir.glob("browser_screenshot_*.png"):
|
||||||
try:
|
try:
|
||||||
if f.stat().st_mtime < cutoff:
|
if f.stat().st_mtime < cutoff:
|
||||||
f.unlink()
|
f.unlink()
|
||||||
except Exception:
|
except Exception as e:
|
||||||
pass
|
logger.debug("Failed to clean old screenshot %s: %s", f, e)
|
||||||
except Exception:
|
except Exception as e:
|
||||||
pass # Non-critical — don't fail the screenshot operation
|
logger.debug("Screenshot cleanup error (non-critical): %s", e)
|
||||||
|
|
||||||
|
|
||||||
def _cleanup_old_recordings(max_age_hours=72):
|
def _cleanup_old_recordings(max_age_hours=72):
|
||||||
@@ -1632,12 +1632,12 @@ def _cleanup_old_recordings(max_age_hours=72):
|
|||||||
cutoff = time.time() - (max_age_hours * 3600)
|
cutoff = time.time() - (max_age_hours * 3600)
|
||||||
for f in recordings_dir.glob("session_*.webm"):
|
for f in recordings_dir.glob("session_*.webm"):
|
||||||
try:
|
try:
|
||||||
if f.stat().st_mtime < cutoff:
|
if f.stat().st_mtime < cutoff:
|
||||||
f.unlink()
|
f.unlink()
|
||||||
except Exception:
|
except Exception as e:
|
||||||
pass
|
logger.debug("Failed to clean old recording %s: %s", f, e)
|
||||||
except Exception:
|
except Exception as e:
|
||||||
pass
|
logger.debug("Recording cleanup error (non-critical): %s", e)
|
||||||
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
@@ -1749,7 +1749,7 @@ def cleanup_browser(task_id: Optional[str] = None) -> None:
|
|||||||
os.kill(daemon_pid, signal.SIGTERM)
|
os.kill(daemon_pid, signal.SIGTERM)
|
||||||
logger.debug("Killed daemon pid %s for %s", daemon_pid, session_name)
|
logger.debug("Killed daemon pid %s for %s", daemon_pid, session_name)
|
||||||
except (ProcessLookupError, ValueError, PermissionError, OSError):
|
except (ProcessLookupError, ValueError, PermissionError, OSError):
|
||||||
pass
|
logger.debug("Could not kill daemon pid for %s (already dead or inaccessible)", session_name)
|
||||||
shutil.rmtree(socket_dir, ignore_errors=True)
|
shutil.rmtree(socket_dir, ignore_errors=True)
|
||||||
|
|
||||||
logger.debug("Removed task %s from active sessions", task_id)
|
logger.debug("Removed task %s from active sessions", task_id)
|
||||||
|
|||||||
@@ -332,10 +332,10 @@ def _rpc_server_loop(
|
|||||||
pass
|
pass
|
||||||
finally:
|
finally:
|
||||||
if conn:
|
if conn:
|
||||||
try:
|
try:
|
||||||
conn.close()
|
conn.close()
|
||||||
except OSError:
|
except OSError as e:
|
||||||
pass
|
logger.debug("RPC conn close error: %s", e)
|
||||||
|
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
@@ -547,10 +547,10 @@ def execute_code(
|
|||||||
|
|
||||||
finally:
|
finally:
|
||||||
# Cleanup temp dir and socket
|
# Cleanup temp dir and socket
|
||||||
try:
|
try:
|
||||||
server_sock.close()
|
server_sock.close()
|
||||||
except Exception:
|
except Exception as e:
|
||||||
pass
|
logger.debug("Server socket close error: %s", e)
|
||||||
try:
|
try:
|
||||||
import shutil
|
import shutil
|
||||||
shutil.rmtree(tmpdir, ignore_errors=True)
|
shutil.rmtree(tmpdir, ignore_errors=True)
|
||||||
|
|||||||
@@ -82,10 +82,10 @@ def _check_disk_usage_warning():
|
|||||||
for path in glob.glob(str(scratch_dir / "hermes-*")):
|
for path in glob.glob(str(scratch_dir / "hermes-*")):
|
||||||
for f in Path(path).rglob('*'):
|
for f in Path(path).rglob('*'):
|
||||||
if f.is_file():
|
if f.is_file():
|
||||||
try:
|
try:
|
||||||
total_bytes += f.stat().st_size
|
total_bytes += f.stat().st_size
|
||||||
except OSError:
|
except OSError as e:
|
||||||
pass
|
logger.debug("Could not stat file %s: %s", f, e)
|
||||||
|
|
||||||
total_gb = total_bytes / (1024 ** 3)
|
total_gb = total_bytes / (1024 ** 3)
|
||||||
|
|
||||||
@@ -228,16 +228,16 @@ def _prompt_for_sudo_password(timeout_seconds: int = 45) -> str:
|
|||||||
result["password"] = ""
|
result["password"] = ""
|
||||||
finally:
|
finally:
|
||||||
if tty_fd is not None and old_attrs is not None:
|
if tty_fd is not None and old_attrs is not None:
|
||||||
try:
|
try:
|
||||||
import termios as _termios
|
import termios as _termios
|
||||||
_termios.tcsetattr(tty_fd, _termios.TCSAFLUSH, old_attrs)
|
_termios.tcsetattr(tty_fd, _termios.TCSAFLUSH, old_attrs)
|
||||||
except Exception:
|
except Exception as e:
|
||||||
pass
|
logger.debug("Failed to restore terminal attributes: %s", e)
|
||||||
if tty_fd is not None:
|
if tty_fd is not None:
|
||||||
try:
|
try:
|
||||||
os.close(tty_fd)
|
os.close(tty_fd)
|
||||||
except Exception:
|
except Exception as e:
|
||||||
pass
|
logger.debug("Failed to close tty fd: %s", e)
|
||||||
result["done"] = True
|
result["done"] = True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@@ -669,10 +669,10 @@ def get_active_environments_info() -> Dict[str, Any]:
|
|||||||
import glob
|
import glob
|
||||||
for path in glob.glob(str(scratch_dir / pattern)):
|
for path in glob.glob(str(scratch_dir / pattern)):
|
||||||
try:
|
try:
|
||||||
size = sum(f.stat().st_size for f in Path(path).rglob('*') if f.is_file())
|
size = sum(f.stat().st_size for f in Path(path).rglob('*') if f.is_file())
|
||||||
total_size += size
|
total_size += size
|
||||||
except OSError:
|
except OSError as e:
|
||||||
pass
|
logger.debug("Could not stat path %s: %s", path, e)
|
||||||
|
|
||||||
info["total_disk_usage_mb"] = round(total_size / (1024 * 1024), 2)
|
info["total_disk_usage_mb"] = round(total_size / (1024 * 1024), 2)
|
||||||
return info
|
return info
|
||||||
@@ -697,10 +697,10 @@ def cleanup_all_environments():
|
|||||||
import glob
|
import glob
|
||||||
for path in glob.glob(str(scratch_dir / "hermes-*")):
|
for path in glob.glob(str(scratch_dir / "hermes-*")):
|
||||||
try:
|
try:
|
||||||
shutil.rmtree(path, ignore_errors=True)
|
shutil.rmtree(path, ignore_errors=True)
|
||||||
logger.info("Removed orphaned: %s", path)
|
logger.info("Removed orphaned: %s", path)
|
||||||
except OSError:
|
except OSError as e:
|
||||||
pass
|
logger.debug("Failed to remove orphaned path %s: %s", path, e)
|
||||||
|
|
||||||
if cleaned > 0:
|
if cleaned > 0:
|
||||||
logger.info("Cleaned %d environments", cleaned)
|
logger.info("Cleaned %d environments", cleaned)
|
||||||
|
|||||||
Reference in New Issue
Block a user