文件最后提交记录最后更新时间
docs(plugins): rename disk-guardian to disk-cleanup + bundled-plugins docs The original name was cute but non-obvious; disk-cleanup says what it does. Plugin directory, script, state path, log lines, slash command, and test module all renamed. No user-visible state exists yet, so no migration path is needed. New website page "Built-in Plugins" documents the <repo>/plugins/<name>/ source, how discovery interacts with user/project plugins, the HERMES_DISABLE_BUNDLED_PLUGINS escape hatch, disk-cleanup's hook behaviour and deletion rules, and guidance on when a plugin belongs bundled vs. user-installable. Added to the Features → Core sidebar next to the main Plugins page, with a cross-reference from plugins.md. 1 个月前
chore: ruff auto-fix PLR6201 resweep — tuple → set in membership tests (#27355) Six days after #23937 (608 fixes) the codebase had accumulated 241 new PLR6201 violations. Same mechanical x in (...)x in {...} fix, same zero-risk profile: set lookup is O(1) vs O(n) for tuple and the two are semantically equivalent for hashable scalar membership tests. All 241 instances fixed via `ruff check --select PLR6201 --fix --unsafe-fixes`, zero remaining. Every changed value is a hashable scalar (str/int/None/enum/signal); no risk of unhashable runtime errors. No behavior change. Test plan: - 119 files changed, +244/-244 (net zero) — exactly one-line edits - ruff check clean afterward - Compile checks pass on the largest touched files (cli.py, run_agent.py, gateway/run.py, gateway/platforms/discord.py, model_tools.py) - Subset broad test run on tests/gateway/ tests/hermes_cli/ tests/agent/ tests/tools/: 18187 passed, 59 pre-existing failures (verified against origin/main with the same shape — identical failure count, identical category — all xdist test-order flakes unrelated to this change) Follows the same template as PR #23937 ([tracker: #23972](https://github.com/NousResearch/hermes-agent/issues/23972)).19 天前
feat(cross-platform): psutil for PID/process management + Windows footgun checker ## Why Hermes supports Linux, macOS, and native Windows, but the codebase grew up POSIX-first and has accumulated patterns that silently break (or worse, silently kill!) on Windows: - os.kill(pid, 0) as a liveness probe — on Windows this maps to CTRL_C_EVENT and broadcasts Ctrl+C to the target's entire console process group (bpo-14484, open since 2012). - os.killpg — doesn't exist on Windows at all (AttributeError). - os.setsid / os.getuid / os.geteuid — same. - signal.SIGKILL / signal.SIGHUP / signal.SIGUSR1 — module-attr errors at runtime on Windows. - open(path) / open(path, "r") without explicit encoding= — inherits the platform default, which is cp1252/mbcs on Windows (UTF-8 on POSIX), causing mojibake round-tripping between hosts. - wmic — removed from Windows 10 21H1+. This commit does three things: 1. Makes psutil a core dependency and migrates critical callsites to it. 2. Adds a grep-based CI gate (scripts/check-windows-footguns.py) that blocks new instances of any of the above patterns. 3. Fixes every existing instance in the codebase so the baseline is clean. ## What changed ### 1. psutil as a core dependency (pyproject.toml) Added psutil>=5.9.0,<8 to core deps. psutil is the canonical cross-platform answer for "is this PID alive" and "kill this process tree" — its pid_exists() uses OpenProcess + GetExitCodeProcess on Windows (NOT a signal call), and its Process.children(recursive=True) + .kill() combo replaces os.killpg() portably. ### 2. gateway/status.py::_pid_exists Rewrote to call psutil.pid_exists() first, falling back to the hand-rolled ctypes OpenProcess + WaitForSingleObject dance on Windows (and os.kill(pid, 0) on POSIX) only if psutil is somehow missing — e.g. during the scaffold phase of a fresh install before pip finishes. ### 3. os.killpg migration to psutil (7 callsites, 5 files) - tools/code_execution_tool.py - tools/process_registry.py - tools/tts_tool.py - tools/environments/local.py (3 sites kept as-is, suppressed with # windows-footgun: ok — the pgid semantics psutil can't replicate, and the calls are already Windows-guarded at the outer branch) - gateway/platforms/whatsapp.py ### 4. scripts/check-windows-footguns.py (NEW, 500 lines) Grep-based checker with 11 rules covering every Windows cross-platform footgun we've hit so far: 1. os.kill(pid, 0) — the silent killer 2. os.setsid without guard 3. os.killpg (recommends psutil) 4. os.getuid / os.geteuid / os.getgid 5. os.fork 6. signal.SIGKILL 7. signal.SIGHUP/SIGUSR1/SIGUSR2/SIGALRM/SIGCHLD/SIGPIPE/SIGQUIT 8. subprocess shebang script invocation 9. wmic without shutil.which guard 10. Hardcoded ~/Desktop (OneDrive trap) 11. asyncio.add_signal_handler without try/except 12. open() without encoding= on text mode Features: - Triple-quoted-docstring aware (won't flag prose inside docstrings) - Trailing-comment aware (won't flag mentions in # os.kill(pid, 0) comments) - Guard-hint aware (skips lines with hasattr(os, ...), shutil.which(...), if platform.system() != 'Windows', etc.) - Inline suppression with # windows-footgun: ok — <reason> - --list to print all rules with fixes - --all / --diff <ref> / staged-files (default) modes - Scans 380 files in under 2 seconds ### 5. CI integration A GitHub Actions workflow that runs the checker on every PR and push is staged at /tmp/hermes-stash/windows-footguns.yml — not included in this commit because the GH token on the push machine lacks workflow scope. A maintainer with workflow permissions should add it as .github/workflows/windows-footguns.yml in a follow-up. Content: ```yaml name: Windows footgun check on: push: branches: [main] pull_request: branches: [main] jobs: check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: {python-version: "3.11"} - run: python scripts/check-windows-footguns.py --all ``` ### 6. CONTRIBUTING.md — "Cross-Platform Compatibility" expansion Expanded from 5 to 16 rules, each with message, example, and fix. Recommends psutil as the preferred API for PID / process-tree operations. ### 7. Baseline cleanup (91 → 0 findings) - 14 open() sites → added encoding='utf-8' (internal logs/caches) or encoding='utf-8-sig' (user-editable files that Notepad may BOM) - 23 POSIX-only callsites in systemd helpers, pty_bridge, and plugin tool subprocess management → annotated with # windows-footgun: ok — <reason> - 7 os.killpg sites → migrated to psutil (see §3 above) ## Verification ``` $ python scripts/check-windows-footguns.py --all ✓ No Windows footguns found (380 file(s) scanned). $ python -c "from gateway.status import _pid_exists; import os > print('self:', _pid_exists(os.getpid())); print('bogus:', _pid_exists(999999))" self: True bogus: False ``` Proof-of-repro that os.kill(pid, 0) was actually killing processes before this fix — see commit 1cbe39914 and bpo-14484. This commit removes the last hand-rolled ctypes path from the hot liveness-check path and defers to the best-maintained cross-platform answer. 27 天前
docs(plugins): rename disk-guardian to disk-cleanup + bundled-plugins docs The original name was cute but non-obvious; disk-cleanup says what it does. Plugin directory, script, state path, log lines, slash command, and test module all renamed. No user-visible state exists yet, so no migration path is needed. New website page "Built-in Plugins" documents the <repo>/plugins/<name>/ source, how discovery interacts with user/project plugins, the HERMES_DISABLE_BUNDLED_PLUGINS escape hatch, disk-cleanup's hook behaviour and deletion rules, and guidance on when a plugin belongs bundled vs. user-installable. Added to the Features → Core sidebar next to the main Plugins page, with a cross-reference from plugins.md. 1 个月前
README.md

disk-cleanup

Auto-tracks and cleans up ephemeral files created during Hermes Agent sessions — test scripts, temp outputs, cron logs, stale chrome profiles. Scoped strictly to $HERMES_HOME and /tmp/hermes-*.

Originally contributed by @LVT382009 as a skill in PR #12212. Ported to the plugin system so the behaviour runs automatically via post_tool_call and on_session_end hooks — the agent never needs to remember to call a tool.

How it works

Hook Behaviour
post_tool_call When write_file / terminal / patch creates a file matching test_*, tmp_*, or *.test.* inside HERMES_HOME, track it silently as test / temp / cron-output.
on_session_end If any test files were auto-tracked during this turn, run quick cleanup (no prompts).

Deletion rules (same as the original PR):

Category Threshold Confirmation
test every session end Never
temp >7 days since tracked Never
cron-output >14 days since tracked Never
empty dirs under HERMES_HOME always Never
research >30 days, beyond 10 newest Always (deep only)
chrome-profile >14 days since tracked Always (deep only)
files >500 MB never auto Always (deep only)

Slash command

/disk-cleanup status                     # breakdown + top-10 largest
/disk-cleanup dry-run                    # preview without deleting
/disk-cleanup quick                      # run safe cleanup now
/disk-cleanup deep                       # quick + list items needing prompt
/disk-cleanup track <path> <category>    # manual tracking
/disk-cleanup forget <path>              # stop tracking

Safety

  • is_safe_path() rejects anything outside HERMES_HOME or /tmp/hermes-*
  • Windows mounts (/mnt/c etc.) are rejected
  • The state directory $HERMES_HOME/disk-cleanup/ is itself excluded
  • $HERMES_HOME/logs/, memories/, sessions/, skills/, plugins/, and config files are never tracked
  • Backup/restore is scoped to tracked.json — the plugin never touches agent logs
  • Atomic writes: .tmp → backup → rename