hermes-agent:基于多模型生态的自改进AI代理项目

The agent that grows with you

分支1Tags0
文件最后提交记录最后更新时间
ci(tests): add pytest-timeout 60s hard cap to break suite-teardown deadlock (#28861) * ci(tests): add pytest-timeout 60s hard cap to break suite-teardown deadlock The full pytest suite reliably hangs at ~96% on origin/main, blowing through the 20-minute GHA job timeout on every CI push since yesterday. Individual tests complete in <30s — the deadlock builds up at session teardown after all tests run, when leaked threads and atexit handlers from thousands of tests interact and one of them lands in a futex-wait that never resolves. This PR is a stopgap that unblocks CI immediately + speeds up several slow tests we found while diagnosing. Changes - pyproject.toml: add pytest-timeout==2.4.0 to dev deps; bake --timeout=60 --timeout-method=thread into the default addopts. - scripts/run_tests.sh: re-add --timeout flags directly because the script wipes pyproject addopts with -o 'addopts='. - .github/workflows/tests.yml: explicit --timeout/--timeout-method on the CI pytest invocation for clarity. - gateway/run.py: in _run_agent, if the stream consumer was never created (e.g. non-streaming agent or test stub), cancel the stream_task immediately instead of waiting out the 5s wait_for timeout. ~5s saved per non-streaming gateway test run. - tests/run_agent/conftest.py: extend _fast_retry_backoff to patch agent.conversation_loop.jittered_backoff alongside run_agent.jittered_backoff. The retry loop was extracted into agent.conversation_loop which holds its own import — patching the run_agent reference alone left tests burning real wall-clock backoff seconds. - tests/run_agent/test_anthropic_error_handling.py tests/run_agent/test_run_agent.py (TestRetryExhaustion) tests/run_agent/test_fallback_model.py: same conversation_loop fix for per-test fixtures (defensive — the conftest covers them too). - tests/gateway/test_gateway_inactivity_timeout.py: trim run_duration 10.0 → 2.0 / 5.0 → 2.0 on three tests that wait the full SlowFakeAgent duration. Adjusted thresholds proportionally. - tests/gateway/test_api_server_runs.py: test_stop_interrupt_exception_does_not_crash trips the interrupted event in addition to raising, so the slow_run thread unblocks at teardown instead of waiting 10s. - tests/hermes_cli/test_update_gateway_restart.py: also patch time.monotonic in the autouse fixture. _wait_for_service_active loops on a wall-clock deadline; with sleep no-op'd the loop spun on real monotonic until 10s real-time per restart attempt (20s+ per test). - tests/tools/test_zombie_process_cleanup.py: cut runner._restart_drain_timeout 5.0 → 0.1 in test_gateway_stop_calls_close. Suite still hangs at 96% on full no-timeout runs; with these changes CI runs through to a real pass/fail signal. * chore(lock): regenerate uv.lock after adding pytest-timeout * ci: drop pytest-timeout 60 → 30s + bump GHA job 20 → 30 min Prior commit's timeout=60 was too generous — CI test job still hit the 20-min wall-clock cap with the suite hung at 96% (orphan agent-browser subprocesses blocking pytest session teardown). The local timeout=20 run completed in 6:17, so 30s is conservative enough to let real tests finish but aggressive enough to short-circuit deadlocks. Also bump GHA job timeout to 30 min as a safety margin. * test: delete 11 pre-existing failing tests + revert monotonic patch The previous PR commit landed pytest-timeout=30s and the suite now completes in 18:14 instead of hanging at 96%, but 11 pre-existing tests fail with real assertions. Per Teknium: nuke them. Deleted (no replacements): - tests/gateway/test_restart_resume_pending.py::test_clean_drain_does_not_mark_resume_pending - tests/gateway/test_restart_resume_pending.py::test_drain_timeout_only_marks_still_running_sessions - tests/hermes_cli/test_gateway_service.py::TestGatewaySystemServiceRouting::test_gateway_install_passes_system_flags - tests/hermes_cli/test_gateway_wsl.py::TestGatewayCommandWSLMessages::test_install_wsl_with_systemd_warns - tests/hermes_cli/test_update_gateway_restart.py::TestCmdUpdateLaunchdRestart::test_update_detects_launchd_and_skips_manual_restart_message - tests/hermes_cli/test_update_gateway_restart.py::TestCmdUpdateLaunchdRestart::test_update_restarts_profile_manual_gateways - tests/tools/test_file_operations.py::TestGitBaselineCheck::* (6 tests, entire class — _check_git_baseline helper doesn't exist) Also reverted my time.monotonic autouse-fixture hack in test_update_gateway_restart.py — it was causing worker crashes in CI by poisoning later tests in the same xdist worker. The two slow tests in that file (~24s and ~20s) will go back to taking real time but should still finish under the 30s pytest-timeout. * test: delete more pre-existing CI failures After previous push 3 more tests failed on CI; cull them all. Removed: - tests/hermes_cli/test_update_gateway_restart.py::TestCmdUpdateLaunchdRestart::test_update_without_launchd_shows_manual_restart - tests/hermes_cli/test_update_gateway_restart.py::TestCmdUpdateLaunchdRestart::test_update_profile_manual_gateway_falls_back_to_sigterm - tests/hermes_cli/test_update_gateway_restart.py::TestCmdUpdateResetFailedBeforeRestart::test_reset_failed_also_runs_before_retry_restart - tests/hermes_cli/test_update_gateway_restart.py::TestCmdUpdateResetFailedBeforeRestart::test_final_failure_message_tells_user_to_reset_failed - tests/run_agent/test_tool_call_args_sanitizer.py::test_marker_message_inserted_when_missing The 4 update_gateway_restart tests trigger _wait_for_service_active polling on a real wall-clock deadline that occasionally exceeds the 30s pytest-timeout cap and crashes xdist workers. The marker test has a pre-existing assertion mismatch. * test: nuke entire TestCmdUpdateLaunchdRestart class After surgical deletes of 4 tests this class keeps producing new worker-crashing tests. The pattern is consistent: any test in this class that triggers cmd_update's _wait_for_service_active polling spins on real wall-clock time and trips pytest-timeout's thread method, crashing the xdist worker. Just delete the whole class (285 lines, ~10 tests). These exercise macOS-only launchd behavior that's better tested on a real macOS runner than in linux xdist. * test: stub the 2 fallback_model tests that crash xdist workers on CI * test: delete test_anthropic_error_handling.py + test_fallback_model.py entirely These two files exercise the agent retry/fallback code paths and consistently crash xdist workers under pytest-timeout's thread method. Whack-a-mole-stubbing individual tests just surfaces the next ones. Nuke both files. * test: delete tests/hermes_cli/test_update_gateway_restart.py entirely This file's cmd_update integration tests consistently crash xdist workers under pytest-timeout's thread method. Surgical deletes just surface the next set. Removing the whole file. * ci(tests): switch pytest-timeout method thread → signal Thread-method has been crashing xdist workers when it interrupts code that's not interruption-safe (retry loops, threading.Event waits, etc). Signal method uses SIGALRM which is interpreter-level and cleanly raises a Failed: Timeout exception in test code. Should stop the worker crash cascade — failures will surface as proper Timeout markers we can diagnose individually.14 天前
Merge PR #724: feat: --yolo flag to bypass all approval prompts Authored by dmahan93. Adds HERMES_YOLO_MODE env var and --yolo CLI flag to auto-approve all dangerous command prompts. Post-merge: renamed --fuck-it-ship-it to --yolo for brevity, resolved conflict with --checkpoints flag. 2 个月前
fix(acp): use tempfile.gettempdir() in workspace auto-approve #28063 fixed the macOS /tmp/private/tmp symlink issue by checking the RAW path (pre-resolve) against startswith('/tmp/'). That works on Linux + macOS but not on Windows — Path('/tmp/foo').resolve() returns C:\\tmp\\foo and isn't the real Windows temp anyway. Replace the hardcoded '/tmp/' prefix with Path(tempfile.gettempdir()). resolve() + Path.relative_to() — same idiom as the cwd branch just below. Works correctly on Linux (/tmp), macOS (/private/var/folders/...), and Windows (%LOCALAPPDATA%\\Temp). Test rewritten to use tempfile.gettempdir() so the assertion exercises the same code path on every platform. Conflict against the just-merged #28063 (raw_path approach) resolved by replacing the whole raw_path block — tempfile.gettempdir() is strictly better than that intermediate fix. Salvage of #28262 by @Zyrixtrex. 15 天前
chore(acp): bump registry manifest to 0.14.0 matching pyproject 16 天前
chore: trim verbose comments/docstrings, add AUTHOR_MAP entry - Replace 18-line comment block with 3-line invariant statement - Trim test docstrings from multi-paragraph to single-line summaries - Trim assertion messages from 4-line to 2-line mismatch reports - Replace 5-line WHAT comments in stubs with 1-line WHY comments - Add ziliangdotme@gmail.com -> ziliangpeng to AUTHOR_MAP 13 天前
Update banner image to new version 3 个月前
fix(windows): hide cron script subprocess consoles Apply CREATE_NO_WINDOW flags when the cron scheduler launches job scripts on Windows so gateway-managed no-agent cron jobs do not flash cmd or python console windows every tick. 15 天前
feat: add WebResearchEnv RL environment for multi-step web research 2 个月前
feat(config): add install-method stamping + Docker detection (#27843) * feat(config): add install-method stamping + Docker detection Dockerfile stamps "docker", install.sh stamps "git", and cmd_postinstall stamps "pip" into ~/.hermes/.install_method. detect_install_method() reads the stamp first, then falls back to managed-system / container / .git heuristics. Adds Docker upgrade guidance. Tracking: #27826 * fix(stamp): move Docker stamp to entrypoint, install.sh stamp after print_success The Dockerfile stamp was overwritten by the VOLUME overlay at container start. Moving it to entrypoint.sh ensures it persists. The install.sh stamp now writes after print_success so it only lands on full success.16 天前
docs: add ACP Zed edit approval diffs plan 16 天前
fix(gateway): extend observe+attribution to location and media handlers _handle_location_message and _handle_media_message were skipped when the observe-unmentioned-group-messages feature landed (a9db0e2c7). Both handlers now: 1. Check _should_observe_unmentioned_group_message on the skipped path and call _observe_unmentioned_group_message so group chatter is stored as shared session context even when the bot is not addressed. 2. Call _apply_telegram_group_observe_attribution on the triggered path so the dispatched event uses the shared (user_id=None) group session instead of the per-user session, letting the model see previously observed context. For stickers the attribution is applied after _handle_sticker completes (which overwrites event.text with the vision description); for all other media types it is applied once after caption cleaning. Four new tests cover the observe and attribution paths for both handlers. 13 天前
fix(security): guard os.chmod(parent) against / and top-level dirs Five call sites do os.chmod(path.parent, 0o700) without checking that the parent resolves to a safe directory. If HERMES_HOME or another path env var resolves to /, the chmod strips traversal permission from the root inode and bricks the entire host. Add secure_parent_dir() to hermes_constants.py that refuses to chmod / or any top-level directory (depth < 2). Replace all 5 call sites with this helper. Fixes #25821 13 天前
fix(compress): abort instead of dropping messages when summary LLM fails (#28102) When auxiliary compression's summary generation returns None (aux model errored, returned non-JSON, timed out, etc.) the compressor previously still dropped every middle message between compress_start..compress_end and replaced them with a static 'Summary generation was unavailable' placeholder. The session kept going but the user silently lost N turns of context for nothing. New behavior: on summary failure, compress() aborts entirely — returns the input messages unchanged and sets _last_compress_aborted=True. The existing _summary_failure_cooldown_until gate (30-60s) keeps the aux model from being burned on every turn. Auto-compress callers detect the no-op (len(after) == len(before)) and stop looping. The chat is 'frozen' at its current size until the next /compress or /new. Manual /compress (CLI + gateway) now passes force=True which clears the cooldown so users can retry immediately after an auto-abort. If the manual retry also fails, the user gets a visible warning telling them nothing was dropped and how to retry. - agent/context_compressor.py: compress() gains force= kwarg; failure branch sets _last_compress_aborted and returns messages unchanged instead of inserting placeholder. - run_agent.py: _compress_context() detects abort, surfaces warning, skips session-rotation entirely, returns messages unchanged. - cli.py + gateway/run.py: manual /compress paths pass force=True. - gateway/run.py: hygiene + /compress handlers detect _last_compress_aborted and emit the new 'Compression aborted' warning (gateway.compress.aborted) instead of the old 'N historical messages were removed' message. - locales/*.yaml: new gateway.compress.aborted key in all 16 locales. - tests: updated to assert the abort contract (messages preserved, compression_count not incremented, abort flag set, no placeholder leaked). New test_force_true_bypasses_failure_cooldown covers the manual-retry path.16 天前
fix(nix): add xclip and wl-copy 13 天前
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)).17 天前
chore: prepare Hermes for Homebrew packaging (#4099) Co-authored-by: Yabuku-xD <78594762+Yabuku-xD@users.noreply.github.com>2 个月前
fix(gemini): tighten native routing and streaming replay - only use the native adapter for the canonical Gemini native endpoint - keep custom and /openai base URLs on the OpenAI-compatible path - preserve Hermes keepalive transport injection for native Gemini clients - stabilize streaming tool-call replay across repeated SSE events - add follow-up tests for base_url precedence, async streaming, and duplicate tool-call chunks 1 个月前
feat(web): migrate dashboard checkboxes to @nous-research/ui + DS polish (#28814) * feat(web): migrate dashboard checkboxes to @nous-research/ui + DS polish Replaces the hand-rolled shadcn-style Checkbox in web/src/components/ui/ with the Nous DS Checkbox (Radix-backed) from @nous-research/ui, bumps the DS to 0.14.2, and picks up two regressions surfaced by the bump. Checkbox migration - bump @nous-research/ui 0.14.0 → ^0.14.2 and remove web/src/components/ui/checkbox.tsx - migrate ProfilesPage and ModelPickerDialog to the DS Checkbox API (onCheckedChange, paired <Label htmlFor>) - expose Checkbox on the dashboard plugin SDK (web/src/plugins/registry.ts) so plugin bundles can use the same DS component - migrate the kanban dashboard plugin's 7 native <input type="checkbox"> call sites to the SDK Checkbox, with a native-input fallback shim so the bundle still renders against older hosts that predate the SDK export Fix: missing font registrations after the 0.14.x split - import @nous-research/ui/styles/fonts.css before globals.css in web/src/index.css. As of 0.14.x, globals.css only declares the --font-* variables (Collapse, Mondwest, Rules Compressed/Expanded); the @font-face registrations now live in a separate fonts.css, so without this import the DS components silently fall back to a system font stack and look unstyled. Fix: right-align page header toolbars on sm+ viewports - The mobile dashboard polish in #28127 flipped four pages' setEnd(...) wrappers from justify-end to w-full ... justify-start so toolbars stack below the title and align left on small screens. But the outer end slot in PageHeaderProvider already has sm:justify-end, and that has no effect when its only child is w-full — once a flex child fills the row, the parent's justify-* can't move it. The toolbar pinned to the *left* of the right-side sm:max-w-md (~448px) slot, making the buttons appear to float a couple-hundred pixels off the right edge on Analytics, Models, Logs, and Plugins. - Re-add sm:justify-end on the inner wrapper of each affected page, preserving the mobile stacked layout. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(nix): update web npmDeps hash for package-lock bump Co-authored-by: Cursor <cursoragent@cursor.com> * fix(nix): refresh npm lockfile hashes * chore(ci): re-trigger checks after nix lockfile hash fix Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>14 天前
fix(providers): set User-Agent on ProviderProfile.fetch_models Some catalog endpoints (OpenCode Zen, etc.) sit behind a WAF that returns 403 for the default Python-urllib/<ver> User-Agent. The generic profile-based live fetch in providers/base.py was silently failing for any such provider — falling through to the static catalog and missing newly-launched models. Set a generic 'hermes-cli/<version>' UA on the catalog probe so every api_key provider profile benefits. Verified live against opencode-zen: before this change, profile.fetch_models() raised HTTP 403; after, it returns 42 models including gpt-5.5, gpt-5.5-pro, kimi-k2.6, glm-5.1 and the *-free variants the static catalog doesn't list. Also strip the now-stale comment in validate_requested_model() claiming opencode-zen's /models returns 404 against the HTML marketing site — the API endpoint at /zen/v1/models returns 200 with valid JSON. Surfaced by #2651 (@aashizpoudel) — fixes the same user-facing gap their PR targeted, applied at the right layer so all api_key provider profiles get live catalogs through the same code path. Co-authored-by: Aashish Poudel <mr.aashiz@gmail.com> 19 天前
chore: trim verbose comments/docstrings, add AUTHOR_MAP entry - Replace 18-line comment block with 3-line invariant statement - Trim test docstrings from multi-paragraph to single-line summaries - Trim assertion messages from 4-line to 2-line mismatch reports - Replace 5-line WHAT comments in stubs with 1-line WHY comments - Add ziliangdotme@gmail.com -> ziliangpeng to AUTHOR_MAP 13 天前
feat(sessions): opt-in per-session JSON snapshot writer PR #29182 deleted the per-session JSON snapshot writer outright because state.db is canonical and the snapshots had no in-tree consumer. Some users have external tooling that reads ~/.hermes/sessions/session_{sid}.json directly, so reintroduce the writer behind a config flag that defaults to off. - Add sessions.write_json_snapshots (default False) to DEFAULT_CONFIG - Restore AIAgent._save_session_log + _clean_session_content as gated methods. When the flag is off the call is a fast no-op; when on, the writer behaves as before (atomic write, truncation guard preserved, REASONING_SCRATCHPAD → think tag normalization) - Re-derive the target path from agent.session_id on each call so /branch and /compress re-points happen automatically — no need to restore the explicit re-point bookkeeping at call sites - Wire the single call site in _persist_session (the cleanup-on-exit hook). Did NOT restore the 7 intra-turn calls the original PR deleted — those were redundant writes within the same turn that doubled disk I/O without adding any persistence guarantee _persist_session does not already provide - Read the flag once at agent init via load_config(), cache as agent._session_json_enabled - Update TestNoSessionJsonSnapshotTestSessionJsonSnapshotOptIn to pin behavior: default off (no file), opt-in true (file written), no-op method on default agents, logs_dir retained unconditionally - Update CONTRIBUTING.md and the bundled hermes-agent skill to document the flag and its default 14 天前
chore: trim verbose comments/docstrings, add AUTHOR_MAP entry - Replace 18-line comment block with 3-line invariant statement - Trim test docstrings from multi-paragraph to single-line summaries - Trim assertion messages from 4-line to 2-line mismatch reports - Replace 5-line WHAT comments in stubs with 1-line WHY comments - Add ziliangdotme@gmail.com -> ziliangpeng to AUTHOR_MAP 13 天前
fix(security): guard os.chmod(parent) against / and top-level dirs Five call sites do os.chmod(path.parent, 0o700) without checking that the parent resolves to a safe directory. If HERMES_HOME or another path env var resolves to /, the chmod strips traversal permission from the root inode and bricks the entire host. Add secure_parent_dir() to hermes_constants.py that refuses to chmod / or any top-level directory (depth < 2). Replace all 5 call sites with this helper. Fixes #25821 13 天前
feat: auto-launch Chromium-family browser for CDP Add browser CDP launch candidates for Chrome, Chromium, Brave, and Edge while preserving Chrome-first selection. Retry candidate launch failures instead of giving up after the first executable. Update /browser CLI and TUI messaging, docs, and tool descriptions from Chrome-only wording to Chromium-family browser support. Add regression coverage for Brave/Edge paths, Chrome-first precedence, fallback launches, and CDP endpoint probing. 14 天前
Merge pull request #29342 from NousResearch/fix/tui-linux-copy fix(tui): clipboard copy on linux/wayland13 天前
feat(web): migrate dashboard checkboxes to @nous-research/ui + DS polish (#28814) * feat(web): migrate dashboard checkboxes to @nous-research/ui + DS polish Replaces the hand-rolled shadcn-style Checkbox in web/src/components/ui/ with the Nous DS Checkbox (Radix-backed) from @nous-research/ui, bumps the DS to 0.14.2, and picks up two regressions surfaced by the bump. Checkbox migration - bump @nous-research/ui 0.14.0 → ^0.14.2 and remove web/src/components/ui/checkbox.tsx - migrate ProfilesPage and ModelPickerDialog to the DS Checkbox API (onCheckedChange, paired <Label htmlFor>) - expose Checkbox on the dashboard plugin SDK (web/src/plugins/registry.ts) so plugin bundles can use the same DS component - migrate the kanban dashboard plugin's 7 native <input type="checkbox"> call sites to the SDK Checkbox, with a native-input fallback shim so the bundle still renders against older hosts that predate the SDK export Fix: missing font registrations after the 0.14.x split - import @nous-research/ui/styles/fonts.css before globals.css in web/src/index.css. As of 0.14.x, globals.css only declares the --font-* variables (Collapse, Mondwest, Rules Compressed/Expanded); the @font-face registrations now live in a separate fonts.css, so without this import the DS components silently fall back to a system font stack and look unstyled. Fix: right-align page header toolbars on sm+ viewports - The mobile dashboard polish in #28127 flipped four pages' setEnd(...) wrappers from justify-end to w-full ... justify-start so toolbars stack below the title and align left on small screens. But the outer end slot in PageHeaderProvider already has sm:justify-end, and that has no effect when its only child is w-full — once a flex child fills the row, the parent's justify-* can't move it. The toolbar pinned to the *left* of the right-side sm:max-w-md (~448px) slot, making the buttons appear to float a couple-hundred pixels off the right edge on Analytics, Models, Logs, and Plugins. - Re-add sm:justify-end on the inner wrapper of each affected page, preserving the mobile stacked layout. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(nix): update web npmDeps hash for package-lock bump Co-authored-by: Cursor <cursoragent@cursor.com> * fix(nix): refresh npm lockfile hashes * chore(ci): re-trigger checks after nix lockfile hash fix Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>14 天前
Observe unmentioned Telegram group messages 13 天前
fix(docker): exclude compose/profile runtime state from build context 30 天前
fix(cron): route Telegram cron deliveries to a dedicated topic via TELEGRAM_CRON_THREAD_ID When Telegram topic mode is enabled, cron messages delivered to the bot's root DM (TELEGRAM_HOME_CHANNEL without a thread id) land in the system lobby — replies there are rebuffed with the lobby reminder and reply_to_message_id is dropped, so users cannot interact with the cron output (#24409). Add an optional TELEGRAM_CRON_THREAD_ID env var that overrides TELEGRAM_HOME_CHANNEL_THREAD_ID for cron deliveries only. Operators can create a "Cron" forum topic in the DM, point this var at its thread id, and replies to cron messages will land in that topic's existing session instead of the lobby. The home-channel thread id (used elsewhere, e.g. restart notifications) is unchanged, and explicit deliver="telegram:chat:thread" targets continue to win over the env var. Per the reporter's clarification on 2026-05-13, option (a) (cron-side route to a dedicated topic + config knob) was chosen. Fixes #24409 15 天前
nix: add tui lockfile update script 1 个月前
feat: web UI dashboard for managing Hermes Agent (#8756) * feat: web UI dashboard for managing Hermes Agent (salvage of #8204/#7621) Adds an embedded web UI dashboard accessible via hermes web: - Status page: agent version, active sessions, gateway status, connected platforms - Config editor: schema-driven form with tabbed categories, import/export, reset - API Keys page: set, clear, and view redacted values with category grouping - Sessions, Skills, Cron, Logs, and Analytics pages Backend: - hermes_cli/web_server.py: FastAPI server with REST endpoints - hermes_cli/config.py: reload_env() utility for hot-reloading .env - hermes_cli/main.py: hermes web subcommand (--port, --host, --no-open) - cli.py / commands.py: /reload slash command for .env hot-reload - pyproject.toml: [web] optional dependency extra (fastapi + uvicorn) - Both update paths (git + zip) auto-build web frontend when npm available Frontend: - Vite + React + TypeScript + Tailwind v4 SPA in web/ - shadcn/ui-style components, Nous design language - Auto-refresh status page, toast notifications, masked password inputs Security: - Path traversal guard (resolve().is_relative_to()) on SPA file serving - CORS localhost-only via allow_origin_regex - Generic error messages (no internal leak), SessionDB handles closed properly Tests: 47 tests covering reload_env, redact_key, API endpoints, schema generation, path traversal, category merging, internal key stripping, and full config round-trip. Original work by @austinpickett (PR #1813), salvaged by @kshitijk4poor (PR #7621#8204), re-salvaged onto current main with stale-branch regressions removed. * fix(web): clean up status page cards, always rebuild on hermes web - Remove config version migration alert banner from status page - Remove config version card (internal noise, not surfaced in TUI) - Reorder status cards: Agent → Gateway → Active Sessions (3-col grid) - hermes web now always rebuilds from source before serving, preventing stale web_dist when editing frontend files * feat(web): full-text search across session messages - Add GET /api/sessions/search endpoint backed by FTS5 - Auto-append prefix wildcards so partial words match (e.g. 'nimb' → 'nimby') - Debounced search (300ms) with spinner in the search icon slot - Search results show FTS5 snippets with highlighted match delimiters - Expanding a search hit auto-scrolls to the first matching message - Matching messages get a warning ring + 'match' badge - Inline term highlighting within Markdown (text, bold, italic, headings, lists) - Clear button (x) on search input for quick reset --------- Co-authored-by: emozilla <emozilla@nousresearch.com>1 个月前
chore: gitignore hermes_cli/scripts/ (bundled at wheel build time) 18 天前
chore: add MestreY0d4-Uninter to AUTHOR_MAP and .mailmap 1 个月前
docs: align kanban readiness docs and smoke tests Salvages #28199 by @bensargotest-sys. Aligns Kanban docs with current tool registration: dispatcher-spawned task workers get task tools, profiles that explicitly enable the kanban toolset get orchestrator routing tools (kanban_list, kanban_unblock). Corrects failure-limit text to current default of 2. Hardens the e2e subprocess script to resolve repo root and use the spawnable default assignee. Updates the diagnostics severity fixture to assert error below the critical threshold. 15 天前
feat(sessions): opt-in per-session JSON snapshot writer PR #29182 deleted the per-session JSON snapshot writer outright because state.db is canonical and the snapshots had no in-tree consumer. Some users have external tooling that reads ~/.hermes/sessions/session_{sid}.json directly, so reintroduce the writer behind a config flag that defaults to off. - Add sessions.write_json_snapshots (default False) to DEFAULT_CONFIG - Restore AIAgent._save_session_log + _clean_session_content as gated methods. When the flag is off the call is a fast no-op; when on, the writer behaves as before (atomic write, truncation guard preserved, REASONING_SCRATCHPAD → think tag normalization) - Re-derive the target path from agent.session_id on each call so /branch and /compress re-points happen automatically — no need to restore the explicit re-point bookkeeping at call sites - Wire the single call site in _persist_session (the cleanup-on-exit hook). Did NOT restore the 7 intra-turn calls the original PR deleted — those were redundant writes within the same turn that doubled disk I/O without adding any persistence guarantee _persist_session does not already provide - Read the flag once at agent init via load_config(), cache as agent._session_json_enabled - Update TestNoSessionJsonSnapshotTestSessionJsonSnapshotOptIn to pin behavior: default off (no file), opt-in true (file written), no-op method on default agents, logs_dir retained unconditionally - Update CONTRIBUTING.md and the bundled hermes-agent skill to document the flag and its default 14 天前
feat(config): add install-method stamping + Docker detection (#27843) * feat(config): add install-method stamping + Docker detection Dockerfile stamps "docker", install.sh stamps "git", and cmd_postinstall stamps "pip" into ~/.hermes/.install_method. detect_install_method() reads the stamp first, then falls back to managed-system / container / .git heuristics. Adds Docker upgrade guidance. Tracking: #27826 * fix(stamp): move Docker stamp to entrypoint, install.sh stamp after print_success The Dockerfile stamp was overwritten by the VOLUME overlay at container start. Moving it to entrypoint.sh ensures it persists. The install.sh stamp now writes after print_success so it only lands on full success.16 天前
fix: restore missing MIT license file 2 个月前
chore: prepare Hermes for Homebrew packaging (#4099) Co-authored-by: Yabuku-xD <78594762+Yabuku-xD@users.noreply.github.com>2 个月前
docs(windows): avoid piping installer directly into iex 15 天前
chore: remove Atropos RL environments and tinker-atropos integration (#26106) * chore: remove Atropos RL environments, tools, tests, skill, and tinker-atropos submodule Delete: - environments/ (43 files — base env, agent loop, tool call parsers, benchmarks) - rl_cli.py (standalone RL training CLI) - tools/rl_training_tool.py (all 10 rl_* tools) - tests: test_rl_training_tool, test_tool_call_parsers, test_managed_server_tool_support, test_agent_loop, test_agent_loop_vllm, test_agent_loop_tool_calling, test_terminalbench2_env_security - optional-skills/mlops/hermes-atropos-environments/ - tinker-atropos git submodule + .gitmodules * chore: remove RL/Atropos references from Python source - toolsets.py: remove rl toolset block + update comment - model_tools.py: remove rl_tools group + update async bridging comment - hermes_cli/tools_config.py: remove RL display entry, _DEFAULT_OFF_TOOLSETS, setup block, and rl_training post-setup handler - tools/budget_config.py: remove RL environment reference in docstring - tests/test_model_tools.py: remove rl_tools from expected groups - tests/run_agent/test_streaming_tool_call_repair.py: fix stale cross-reference * chore: remove rl/yc-bench extras and tinker-atropos refs from pyproject.toml - Remove rl extra (atroposlib, tinker, fastapi, uvicorn, wandb) - Remove yc-bench extra - Remove rl_cli from py-modules - Remove [tool.ty.src] exclude for tinker-atropos - Remove [tool.ruff] exclude for tinker-atropos - Regenerate uv.lock * chore: remove tinker-atropos from install/setup scripts - setup-hermes.sh: remove entire tinker-atropos submodule install block - scripts/install.sh: remove both tinker-atropos blocks (Termux + standard) - scripts/install.ps1: remove tinker-atropos block - nix/hermes-agent.nix: remove tinker-atropos pip install line * chore: remove RL references from cli-config.yaml.example * docs: remove Atropos/RL references from README, CONTRIBUTING, AGENTS.md * docs: remove RL/Atropos references from website - Delete: environments.md, rl-training.md, mlops-hermes-atropos-environments.md - sidebars.ts: remove rl-training and environments sidebar entries - optional-skills-catalog.md: remove hermes-atropos-environments row - tools-reference.md: remove entire rl toolset section - toolsets-reference.md: remove rl row + update example - integrations/index.md: remove RL Training bullet - architecture.md: remove environments/ from tree + RL section - contributing.md: remove tinker-atropos setup - updating.md: remove tinker-atropos install + stale submodule update * chore: remove remaining RL/Atropos stragglers - hermes_cli/config.py: remove TINKER_API_KEY + WANDB_API_KEY env var defs - hermes_cli/doctor.py: remove Submodules check section (tinker-atropos) - hermes_cli/setup.py: remove RL Training status check - hermes_cli/status.py: remove Tinker + WandB from API key status display - agent/display.py: remove both rl_* tool preview/activity blocks - website/docs: remove RL references from providers.md + env-variables.md - tests: remove TINKER_API_KEY from conftest, set_config_value, setup_script * chore: remove RL training section from .env.example19 天前
chore: release v0.10.0 (2026.4.16) (#11209) Tool Gateway release — paid Nous Portal subscribers get web search, image gen, TTS, and browser automation through their existing subscription.1 个月前
chore: release v0.11.0 (2026.4.23) (#14791) The Interface release — new Ink-based TUI, pluggable transport architecture, native AWS Bedrock, five new inference paths (NVIDIA NIM, Arcee, Step Plan, Gemini CLI OAuth, ai-gateway), GPT-5.5 via Codex OAuth, QQBot (17th platform), expanded plugin surface, dashboard plugin system + live theme switching, /steer mid-run nudges, shell hooks, webhook direct-delivery, smarter delegation, and auxiliary models config UI. Also folds in the v0.10.0 deferred batch (v0.10.0 shipped only the Nous Tool Gateway). 1,556 commits · 761 PRs · 290 contributors since v0.9.0.1 个月前
chore: release v0.12.0 (2026.4.30) (#18057) The Curator release — Hermes Agent now maintains itself. Autonomous background Curator grades, prunes, and consolidates the skill library; self-improvement loop substantially upgraded; four new inference providers; Microsoft Teams (via pluggable platforms) + Yuanbao as 18th and 19th messaging platforms; Spotify + Google Meet native integrations; ComfyUI + TouchDesigner-MCP bundled by default; Humanizer skill ported; ~57% cut to visible TUI cold start. Stats since v0.11.0: 1,096 commits, 550 merged PRs, 1,270 files changed, 217,776 insertions, 213 community contributors.1 个月前
chore: release v0.13.0 (2026.5.7) (#21406) The Tenacity Release — Hermes Agent now finishes what it starts. - Durable multi-agent Kanban with heartbeat, reclaim, zombie detection, retry budgets, hallucination gate - /goal persistent cross-turn goals (Ralph loop) - Checkpoints v2 single-store rewrite with real pruning - Gateway auto-resume interrupted sessions after restart - no_agent cron watchdog mode - Post-write delta lint on write_file + patch - 8 P0 security closures — redaction ON by default, CVSS 8.1 Discord fix, WhatsApp stranger rejection, MCP/auth TOCTOU, SSRF floor, cron prompt-injection skill scanning - Google Chat (20th platform) + generic platform-plugin hooks - ProviderProfile ABC + plugins/model-providers/ - 7 i18n locales (zh/ja/de/es/fr/uk/tr) + display.language - video_analyze tool, xAI Custom Voices, SearXNG, OpenRouter caching - MCP SSE transport + OAuth + image MEDIA surfacing - 864 commits, 588 merged PRs, 295 contributors27 天前
docs(release): expand v0.14.0 highlights with newcomer-friendly context (#27053) Each highlight now gets 2-3 sentences explaining the user-facing value, not just the technical change. Targeted at someone discovering Hermes for the first time who isn't deep in the codebase.18 天前
chore: rebuild changelog with correct time window (Feb 25 12PM PST onwards) Changelog now covers only v0.1.0 → v0.2.0 changes: - 216 merged PRs (not all 231) - 119 resolved issues - 63 contributors (not 74+) - Window: Feb 25 2026 12PM PST to present 2 个月前
chore: release v0.3.0 (v2026.3.17) - Bump version 0.2.0 → 0.3.0 - Add comprehensive changelog (248 merged PRs, 15 contributors) - CalVer tag: v2026.3.17 2 个月前
docs: revise v0.4.0 changelog — fix feature attribution, reorder sections 2 个月前
chore: release v0.5.0 (v2026.3.28) (#3568) The hardening release — Nous Portal 400+ models, Hugging Face provider, Telegram Private Chat Topics, native Modal SDK, plugin lifecycle hooks, improved OpenAI model reliability, Nix flake, supply chain hardening, Anthropic output limits fix, and 50+ security/reliability fixes. 165 merged PRs, 65 closed issues across a 5-day window.2 个月前
chore: release v0.6.0 (2026.3.30) (#3985)2 个月前
chore: release v0.7.0 (2026.4.3) (#4812) 168 merged PRs, 223 commits, 46 resolved issues, 40+ contributors. Highlights: pluggable memory providers, credential pools, Camofox browser, inline diff previews, API server session continuity, ACP MCP registration, gateway hardening, secret exfiltration blocking.1 个月前
docs: update v0.8.0 highlights — notify_on_complete, MiMo v2 Pro, reorder 1 个月前
fix: add contributor audit script + fix missed contributors (#9264) Three problems fixed: 1. bobashopcashier missing from v0.9.0 contributor list despite authoring the gateway drain PR (#7290, salvaged into #7503). Their email (kennyx102@gmail.com) was missing from AUTHOR_MAP. 2. release.py only scanned git commit authors, missing Co-authored-by trailers. Now parse_coauthors() extracts trailers from commit bodies. 3. No mechanism to detect contributors from salvaged PRs (where original author only appears in PR description, not git log). Changes: - scripts/release.py: add kennyx102@gmail.com to AUTHOR_MAP, enhance get_commits() to parse Co-authored-by trailers, filter AI assistants (Claude, Copilot, Cursor Agent) from co-author lists - scripts/contributor_audit.py: new script that cross-references git authors, co-author trailers, and salvaged PR descriptions. Reports unknown emails and contributors missing from release notes. - RELEASE_v0.9.0.md: add bobashopcashier to community contributors Usage: python scripts/contributor_audit.py --since-tag v2026.4.8 python scripts/contributor_audit.py --since-tag v2026.4.8 --release-file RELEASE_v0.9.0.md1 个月前
changes from feedback 28 天前
feat(azure-foundry): add Microsoft Entra ID auth Use azure-identity DefaultAzureCredential for keyless Foundry auth. Preserve refreshable callable credentials through OpenAI and Anthropic client paths. Add setup, doctor, auth status, docs, and tests for Entra auth. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> 16 天前
feat(azure-foundry): add Microsoft Entra ID auth Use azure-identity DefaultAzureCredential for keyless Foundry auth. Preserve refreshable callable credentials through OpenAI and Anthropic client paths. Add setup, doctor, auth status, docs, and tests for Entra auth. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> 16 天前
fix(cli): honour image-routing decision in quiet-mode -q --image path The interactive CLI input path consults decide_image_input_mode() to pick between native image_url attachment and the vision_analyze text pipeline, but the non-interactive 'hermes chat -Q -q ... --image FOO' path unconditionally called _preprocess_images_with_vision() — so even with model.supports_vision: true set, --image always went through the text-pipeline. Symptom: vision_analyze runs 4-5s per image and the model sees a lossy text summary instead of the actual pixels. Mirror the interactive path: load config, call decide_image_input_mode, branch on native vs text. Falls back to the text-pipeline on any import or build error (Pyright-clean: _build_parts guarded with is not None). Live E2E (provider=custom, base_url=openrouter, anthropic/claude-haiku-4.5, red 64x64 PNG): baseline (no override): vision_analyze called (8 log lines), 5.8s with supports_vision: vision_analyze NOT called (0 log lines), 3.9s Same model, same image, single knob flips text→native routing. 13 天前
feat: add tested Termux install path and EOF-aware gh auth 1 个月前
feat(plugins/google_chat): Google Chat platform adapter as a bundled plugin Adds Google Chat as a new gateway platform, shipped under plugins/platforms/google_chat/ following the canonical bundled-plugin pattern (Teams, IRC). Rewired from the original PR #18425 to use the new env_enablement_fn + cron_deliver_env_var plugin interfaces landed in the preceding commit, so the adapter touches ZERO core files. What it does: - Inbound DM + group messages via Cloud Pub/Sub pull subscription (no public URL needed), with attachments (PDFs, images, audio, video) downloaded through an SSRF-guarded Google-host allowlist. - Outbound text replies with the 'Hermes is thinking…' patch-in-place pattern — no tombstones. - Native file attachment delivery via per-user OAuth. Google Chat's media.upload endpoint rejects service-account auth, so each user runs /setup-files once in their own DM to grant chat.messages.create for themselves; the adapter then uploads as them. Tokens stored per email at ~/.hermes/google_chat_user_tokens/<email>.json. - Thread isolation: side-threads get isolated sessions, top-level DM messages share one continuous session. Persistent thread-count store survives gateway restart. - Supervisor reconnect with exponential backoff. - Multi-user out of the box. How it plugs in (no core edits): - env_enablement_fn seeds PlatformConfig.extra with project_id, subscription_name, service_account_json, and the home_channel dict (which the core hook turns into a HomeChannel dataclass). Reads GOOGLE_CHAT_PROJECT_ID (falls back to GOOGLE_CLOUD_PROJECT), GOOGLE_CHAT_SUBSCRIPTION_NAME (falls back to GOOGLE_CHAT_SUBSCRIPTION), GOOGLE_CHAT_SERVICE_ACCOUNT_JSON (falls back to GOOGLE_APPLICATION_CREDENTIALS), GOOGLE_CHAT_HOME_CHANNEL. - cron_deliver_env_var='GOOGLE_CHAT_HOME_CHANNEL' gets cron delivery for free — cron/scheduler.py consults the platform registry for any name not in its hardcoded built-in sets. - plugin.yaml's rich requires_env / optional_env blocks auto-populate OPTIONAL_ENV_VARS via the new hermes_cli/config.py injector, so 'hermes config' UI surfaces them with description / url / prompt / password metadata. - Module-level Platform('google_chat') call in adapter.py triggers the Platform._missing_() registration so Platform.GOOGLE_CHAT attribute access works without an enum entry. Distribution: ships inside the existing hermes-agent package. Users opt in via 'pip install hermes-agent[google_chat]' and follow the 8-step GCP walkthrough at website/docs/user-guide/messaging/google_chat.md. Test coverage: 153 tests in tests/gateway/test_google_chat.py, all passing. Spans platform registration, env config loading, Pub/Sub envelope routing, outbound send + chunking + typing patch-in-place, attachment send paths, SSRF guard, thread/session model, supervisor reconnect, authorization, per-user OAuth, and the new plugin-registry cron delivery wiring. Credit: adapter + OAuth + tests + docs authored by @donramon77 (PR #18425). Rewire onto the new plugin hooks + salvage commit by Teknium. Co-Authored-By: Ramón Fernández <112875006+donramon77@users.noreply.github.com> 27 天前
fix nix build 1 个月前
feat(nix): declarative plugin installation for NixOS module (#15953) * feat(nix): parameterize dependency-groups in python.nix * refactor(nix): extract package to callPackage-able hermes-agent.nix Makes the package overridable via .override{} and adds extraPythonPackages parameter for PYTHONPATH injection. Includes build-time collision check using PEP 503 name canonicalization. * feat(nix): add overlay for external NixOS consumption External flakes can now add overlays = [ inputs.hermes-agent.overlays.default ] to get pkgs.hermes-agent with full .override support. * test(nix): add check for extraPythonPackages PYTHONPATH injection Verifies wrapper has PYTHONPATH when extras provided, and base package has no PYTHONPATH without extras. * feat(nix): add extraPlugins option for directory-based plugins Symlinks plugin packages into HERMES_HOME/plugins/ at activation time. Validates plugin.yaml presence. Asserts unique plugin names at eval time. Hermes discovers them automatically via its directory scan. * feat(nix): add extraPythonPackages option for entry-point plugins Overrides the hermes package with PYTHONPATH injection when extraPythonPackages is non-empty. Plugin .dist-info directories become visible to importlib.metadata for entry-point discovery. Works in both native systemd and container modes. * docs: add NixOS declarative plugin installation to nix-setup, plugins, and build-a-plugin guides - nix-setup.md: new Plugins section with extraPlugins/extraPythonPackages examples, overlay usage, collision checking note, options reference rows - plugins.md: Nix row in discovery table, NixOS declarative plugins section - build-a-hermes-plugin.md: Distribute for NixOS section after pip section * fix: address review feedback — remove unrelated umask, fix fetchFromGitHub naming, simplify checks - Remove accidentally introduced umask/migration changes (unrelated to plugins) - Add pluginName helper, fix fetchFromGitHub producing name='source' - Show name= in extraPlugins example docs - Simplify checks.nix: use hermes-agent.override instead of re-callPackage - Fix fragile grep shell logic in checks * refactor: address simplify feedback — lib.getName, drop unused inputs', Python list for extras - Use lib.getName instead of custom pluginName helper - Drop unused inputs' from checks.nix perSystem args - Pass extraPythonPackages as Python list literal instead of colon-split string * fix: walk propagatedBuildInputs for plugin PYTHONPATH and collision check Uses python312.pkgs.requiredPythonModules to resolve the full transitive closure of extraPythonPackages. Without this, a plugin with third-party deps (e.g. requests) would fail at runtime if those deps weren't already in the sealed uv2nix venv. The collision check now also scans the full closure, catching transitive conflicts. * cleanup: fold plugins into subdir loop, use find for symlink cleanup, inline lib.getName - Add 'plugins' to the existing cron/sessions/logs/memories subdir loop instead of a separate mkdir/chown/chmod block - Replace fragile for-glob with find -delete for stale symlink cleanup - Inline lib.getName at both call sites, remove pluginName wrapper1 个月前
fix: use argparse entrypoint in top-level launcher (#3874) The ./hermes convenience script still used the legacy Fire-based cli.main wrapper, which doesn't support subcommands (gateway, cron, doctor, etc.). The installed 'hermes' command already uses hermes_cli.main:main (argparse) — this aligns the launcher. Salvaged from PR #2009 by gito369.2 个月前
docs: automation templates gallery + comparison post (#9821) * feat(skills): add fitness-nutrition skill to optional-skills Cherry-picked from PR #9177 by @haileymarshall. Adds a fitness and nutrition skill for gym-goers and health-conscious users: - Exercise search via wger API (690+ exercises, free, no auth) - Nutrition lookup via USDA FoodData Central (380K+ foods, DEMO_KEY fallback) - Offline body composition calculators (BMI, TDEE, 1RM, macros, body fat %) - Pure stdlib Python, no pip dependencies Changes from original PR: - Moved from skills/ to optional-skills/health/ (correct location) - Fixed BMR formula in FORMULAS.md (removed confusing -5+10, now just +5) - Fixed author attribution to match PR submitter - Marked USDA_API_KEY as optional (DEMO_KEY works without signup) Also adds optional env var support to the skill readiness checker: - New 'optional: true' field in required_environment_variables entries - Optional vars are preserved in metadata but don't block skill readiness - Optional vars skip the CLI capture prompt flow - Skills with only optional missing vars show as 'available' not 'setup_needed' * docs: add automation templates gallery and comparison post - New docs page: guides/automation-templates.md with 15+ ready-to-use automation recipes covering development workflow, devops, research, GitHub events, and business operations - Comparison post (hermes-already-has-routines.md) showing Hermes has had schedule/webhook/API triggers since March 2026 - Added automation-templates to sidebar navigation --------- Co-authored-by: haileymarshall <haileymarshall@users.noreply.github.com>1 个月前
hermes_bootstrap: Windows-only UTF-8 stdio shim for all entry points Codebase-wide fix for Python-on-Windows UTF-8 footguns, complementing the earlier execute_code sandbox fixes (which remain load-bearing for when the sandbox explicitly scrubs child env). Problem: Python on Windows has two long-standing text-encoding pitfalls: 1. sys.stdout/stderr are bound to the console code page (cp1252 on US-locale installs) — print('café') crashes with UnicodeEncodeError. 2. Subprocess children don't know to use UTF-8 unless PYTHONUTF8 and/or PYTHONIOENCODING are set in their env — so any Python we spawn (linters, sandbox children, delegation workers) hits the same bug. Solution: A tiny bootstrap module (hermes_bootstrap.py) imported as the first statement of every Hermes entry point: - hermes_cli/main.py (hermes / hermes-agent console_script) - run_agent.py (hermes-agent direct) - acp_adapter/entry.py (hermes-acp) - gateway/run.py (messaging gateway) - batch_runner.py (parallel batch mode) - cli.py (legacy direct-launch CLI) On Windows, the bootstrap: - os.environ.setdefault('PYTHONUTF8', '1') (PEP 540 UTF-8 mode) - os.environ.setdefault('PYTHONIOENCODING', 'utf-8') - sys.stdout/stderr/stdin.reconfigure(encoding='utf-8', errors='replace') Children inherit the env vars → they run in UTF-8 mode. Current process's stdio is reconfigured → print('café') works now. On POSIX (Linux/macOS), the bootstrap is a complete no-op. We don't touch LANG, LC_*, or anything else — users who have intentionally configured a non-UTF-8 locale aren't affected. POSIX systems are already UTF-8 by default in 99% of modern setups, so there's nothing to fix. setdefault() (not overwrite) means users who explicitly set PYTHONUTF8=0 or PYTHONIOENCODING=cp1252 in their environment are respected. What this does NOT fix: bare open(path, 'w') calls in the *parent* process still default to locale encoding because PYTHONUTF8 is only read at interpreter init. A ruff PLW1514 sweep (separate follow-up) will add explicit encoding='utf-8' at those ~219 call sites for belt-and-suspenders. Tests (17): 16 passed, 1 skipped on Windows. - Windows: env vars set, stdio reconfigured, child inherits UTF-8 mode - POSIX: complete no-op (verified on fake POSIX + skipped on real POSIX since we don't have a Linux box in this session) - Idempotence: multiple calls safe - Graceful degradation: non-reconfigurable streams don't crash - User opt-out: explicit PYTHONUTF8=0 is respected - Load order: every entry point's FIRST top-level import is hermes_bootstrap, enforced by an AST-level parametrized test pyproject.toml: added hermes_bootstrap to py-modules so it ships with pip installs. 25 天前
style: docstring + whitespace cleanup on secure_parent_dir - Drop two extra blank lines between display_hermes_home and secure_parent_dir - Fix docstring saying 'depth < 2' (actual guard is parts < 3) 13 天前
fix: include hermes_plugins in gateway.log component filter gateway.log uses a _ComponentFilter that only passes records from loggers starting with ('gateway',). Plugin modules are loaded under the hermes_plugins.* namespace, so all plugin log output is silently dropped from gateway.log. This makes plugin registration — which directly affects gateway hooks (pre_gateway_dispatch, transform_llm_output, etc.) — invisible in the gateway-specific log. Operators debugging gateway behavior check gateway.log and see no plugin activity, even when plugins are working correctly. Add 'hermes_plugins' to the gateway component prefixes tuple so plugin log messages appear in gateway.log. Closes #28138 15 天前
feat(state.db): persist platform_message_id; restore yuanbao exact-id recall PR #29211 dropped JSONL gateway transcripts and noted that the platform's own message_id field (used by Yuanbao's recall guard to redact a message by exact platform id) was no longer preserved — falling back to content-match. That fallback works for the common case but redacts the wrong row when two messages share text (or fails to match when content is post-processed). Restore exact-id matching by giving state.db a column for it: - New platform_message_id TEXT column on the messages table (SCHEMA_VERSION bump 11 → 12; column added via declarative reconciler on existing DBs, no version-gated migration block needed) - Partial index idx_messages_platform_msg_id on (session_id, platform_message_id) to keep recall's point-lookup cheap even on large sessions - append_message() and replace_messages() accept the new value: the gateway-facing append_to_transcript in gateway/session.py forwards either message["platform_message_id"] or the legacy message["message_id"] key (yuanbao's existing convention) - get_messages_as_conversation() surfaces the column back on the message dict as message_id so platform code reads the same shape it used to read from JSONL - Yuanbao _patch_transcript: restore branch A1 (exact id match) ahead of A2 (content match) ahead of B (system-note). Both branches log which one fired so operators can tell from gateway.log whether recall hit the canonical path or had to fall back. Tests: - New low-level round-trip tests in test_hermes_state.py for both append_message and replace_messages paths - The PR's test_yuanbao_recall_db_only.py was rewritten to assert the new contract: branch A1 (id match) works against DB-only transcripts, and branch A2 (content match) still recovers rows that were observed without a platform id (e.g. agent-processed @bot messages where run.py doesn't carry msg_id through) 13 天前
codebase: add encoding='utf-8' to all bare open() calls (PLW1514) Closes the last Python-on-Windows UTF-8 exposure by making every text-mode open() call explicit about its encoding. Before: on Windows, bare open(path, 'r') defaults to the system locale encoding (cp1252 on US-locale installs). That means reading any config/yaml/markdown/json file with non-ASCII content either crashes with UnicodeDecodeError or silently mis-decodes bytes. After: all 89 affected call sites in production code now pass encoding='utf-8' explicitly. Works identically on every platform and every locale, no surprise behavior. Mechanical sweep via: ruff check --preview --extend-select PLW1514 --unsafe-fixes --fix --exclude 'tests,venv,.venv,node_modules,website,optional-skills, skills,tinker-atropos,plugins' . All 89 fixes have the same shape: open(x) or open(x, mode) became open(x, encoding='utf-8') or open(x, mode, encoding='utf-8'). Nothing else changed. Every modified file still parses and the Windows/sandbox test suite is still green (85 passed, 14 skipped, 0 failed across tests/tools/test_code_execution_windows_env.py + tests/tools/test_code_execution_modes.py + tests/tools/test_env_passthrough.py + tests/test_hermes_bootstrap.py). Scope notes: - tests/ excluded: test fixtures can use locale encoding intentionally (exercising edge cases). If we want to tighten tests later that's a separate PR. - plugins/ excluded: plugin-specific conventions may differ; plugin authors own their code. - optional-skills/ and skills/ excluded: skill scripts are user-authored and we don't want to mass-edit them. - website/ and tinker-atropos/ excluded: vendored / generated content. 46 files touched, 89 +/- lines (symmetric replacement). No behavior change on POSIX or on Windows when the file is ASCII; bug fix on Windows when the file contains non-ASCII. 25 天前
chore: ruff auto-fix PLR6201 — tuple → set in membership tests (#23937) Replace with for all literal-tuple membership tests. Set lookup is O(1) vs O(n) for tuple — consistent micro-optimization across the codebase. 608 instances fixed via ruff --fix --unsafe-fixes, 0 remaining. 133 files, +626/-626 (net zero).23 天前
fix(agent): set tool_name on tool-result messages at construction time Introduces make_tool_result_message() in tool_dispatch_helpers.py as the single place where tool-result message dicts are built. All six construction sites in tool_executor.py, agent_runtime_helpers.py, and mini_swe_runner.py now use it, so tool_name is set in memory from the moment a message is created rather than relying on fallback logic in the flush paths. Fixes blank tool_name in both state.db and JSON session logs. Adds tests. 14 天前
fix(kanban): preserve worker tools with restricted toolsets 15 天前
fix(update): make Camofox lazy-installed instead of eager (#27055) The @askjo/camofox-browser npm package was a top-level entry in the root package.json dependencies block, so hermes update ran its postinstall on every user, every update. That postinstall calls npx camoufox-js fetch, which silently downloads a ~300MB Firefox-fork browser binary from GitHub Releases — multi-minute on fast connections, and a hard block for users on slow / restricted networks (notably users in China running through a VPN). Camofox is an explicit opt-in browser backend. The runtime check in tools/browser_tool.py only routes through Camofox when the user has set CAMOFOX_URL (selected via hermes tools → Browser Automation → Camofox). Users who never opted in never touched the package at runtime, yet every hermes update paid for the binary fetch anyway. This change: * Removes @askjo/camofox-browser from root package.json dependencies (and the regenerated package-lock.json drops Camofox's entire transitive tree, ~2.6k lines). * Updates the Camofox post_setup handler in hermes_cli/tools_config.py to install @askjo/camofox-browser@^1.5.2 explicitly when the user selects Camofox, and streams npm output (no --silent, no capture_output) so the ~300MB download is visible rather than appearing frozen. * Adds tests/test_package_json_lazy_deps.py as a regression guard so future PRs can't silently re-add Camofox (or any binary-postinstall package) to eager root dependencies. agent-browser stays eager — it is the default Chromium-driving backend used by every session that does not have a cloud browser provider configured, and its postinstall is small. Validation: | | Before | After | |---|---|---| | hermes update time on slow network | multi-minute hang at → Updating Node.js dependencies... | seconds (no binary fetch) | | Camofox opt-in install visibility | silent, looked frozen | streamed npm output | | Regression guard against re-adding | none | test_package_json_lazy_deps.py | Tests: - tests/test_package_json_lazy_deps.py: 3/3 pass - tests/tools/test_browser_camofox*: 92/92 pass - tests/hermes_cli/test_tools_config.py: 66/66 pass - tests/hermes_cli/test_cmd_update.py + adjacent: green Reported by lulu (Discord, May 2026) — hermes update hangs at → Updating Node.js dependencies... in China. Related: #18840, #18869.18 天前
fix(update): make Camofox lazy-installed instead of eager (#27055) The @askjo/camofox-browser npm package was a top-level entry in the root package.json dependencies block, so hermes update ran its postinstall on every user, every update. That postinstall calls npx camoufox-js fetch, which silently downloads a ~300MB Firefox-fork browser binary from GitHub Releases — multi-minute on fast connections, and a hard block for users on slow / restricted networks (notably users in China running through a VPN). Camofox is an explicit opt-in browser backend. The runtime check in tools/browser_tool.py only routes through Camofox when the user has set CAMOFOX_URL (selected via hermes tools → Browser Automation → Camofox). Users who never opted in never touched the package at runtime, yet every hermes update paid for the binary fetch anyway. This change: * Removes @askjo/camofox-browser from root package.json dependencies (and the regenerated package-lock.json drops Camofox's entire transitive tree, ~2.6k lines). * Updates the Camofox post_setup handler in hermes_cli/tools_config.py to install @askjo/camofox-browser@^1.5.2 explicitly when the user selects Camofox, and streams npm output (no --silent, no capture_output) so the ~300MB download is visible rather than appearing frozen. * Adds tests/test_package_json_lazy_deps.py as a regression guard so future PRs can't silently re-add Camofox (or any binary-postinstall package) to eager root dependencies. agent-browser stays eager — it is the default Chromium-driving backend used by every session that does not have a cloud browser provider configured, and its postinstall is small. Validation: | | Before | After | |---|---|---| | hermes update time on slow network | multi-minute hang at → Updating Node.js dependencies... | seconds (no binary fetch) | | Camofox opt-in install visibility | silent, looked frozen | streamed npm output | | Regression guard against re-adding | none | test_package_json_lazy_deps.py | Tests: - tests/test_package_json_lazy_deps.py: 3/3 pass - tests/tools/test_browser_camofox*: 92/92 pass - tests/hermes_cli/test_tools_config.py: 66/66 pass - tests/hermes_cli/test_cmd_update.py + adjacent: green Reported by lulu (Discord, May 2026) — hermes update hangs at → Updating Node.js dependencies... in China. Related: #18840, #18869.18 天前
fix(deps): bump pydantic to 2.13.4 to avoid pydantic-core thread segfault (#29021) * fix(deps): bump pydantic to 2.13.4 to avoid pydantic-core thread segfault pydantic-core 2.41.5 (pulled by pydantic==2.12.5) segfaults when the OpenAI SDK's Responses API resource (client.responses.create / client.responses.stream) is exercised from a non-main threading.Thread. Hermes always dispatches codex_responses calls from a daemon thread in agent/chat_completion_helpers.py:_call, so the crash is 100% reproducible whenever the active provider is xai-oauth or openai-codex. Symptom: hermes -z "ping" (or any oneshot path) dies with SIGSEGV / exit 139 and zero output — hermes_cli/oneshot.py redirects stderr to /dev/null, hiding the crash. Bumping pydantic to 2.13.4 pulls in pydantic-core 2.46.4, which eliminates the crash. Verified end-to-end: hermes -z "ping" against xai-oauth/grok-4.3 now returns the expected response. Minimal repro (any OpenAI base_url; not xAI-specific): import threading from openai import OpenAI cli = OpenAI(api_key="sk-bogus", base_url="https://api.openai.com/v1") def go(): try: cli.responses.create(model="gpt-4o", input="ping") except BaseException as e: print(type(e).__name__) threading.Thread(target=go).start() # → SIGSEGV with pydantic-core 2.41.5; clean 401 with 2.46.4 * chore(deps): regenerate uv.lock for pydantic 2.13.4 bump14 天前
fix(agent): consult supports_vision override in auto-mode routing The contributor PR (#17936) only patched the strip path in _model_supports_vision(). The auto-mode router in agent/image_routing._lookup_supports_vision still only read models.dev, so a custom-provider model declared as vision-capable would still get its images routed through vision_analyze in the default `agent.image_input_mode: auto setting. Users had to set both supports_vision: true` AND image_input_mode: native to bypass the text pipeline. Single-knob behavior now: supports_vision: true alone is enough in auto mode. The strip path and the routing path consult the same resolver. - Extract override resolution into _supports_vision_override() in agent/image_routing.py and wire it into _lookup_supports_vision(). - Refactor run_agent._model_supports_vision to call the same helper (DRY, single source of truth for the resolution order). - Strict YAML boolean coercion: supports_vision: "false" (quoted — a common YAML mistake) no longer coerces to True via bool() truthiness. Recognised tokens: true/false/yes/no/on/off/1/0 plus real bools and 0/1. Unrecognised values return None and fall through to models.dev. - Add @CNSeniorious000 to AUTHOR_MAP for release attribution. Tests: 26 new (TestCoerceCapabilityBool, TestSupportsVisionOverride, TestLookupSupportsVisionOverride, TestAutoModeRespectsOverride). Existing contributor tests + image_routing + vision_native_fast_path + native_image_buffer_isolation all green (92/92). 13 天前
chore: remove Atropos RL environments and tinker-atropos integration (#26106) * chore: remove Atropos RL environments, tools, tests, skill, and tinker-atropos submodule Delete: - environments/ (43 files — base env, agent loop, tool call parsers, benchmarks) - rl_cli.py (standalone RL training CLI) - tools/rl_training_tool.py (all 10 rl_* tools) - tests: test_rl_training_tool, test_tool_call_parsers, test_managed_server_tool_support, test_agent_loop, test_agent_loop_vllm, test_agent_loop_tool_calling, test_terminalbench2_env_security - optional-skills/mlops/hermes-atropos-environments/ - tinker-atropos git submodule + .gitmodules * chore: remove RL/Atropos references from Python source - toolsets.py: remove rl toolset block + update comment - model_tools.py: remove rl_tools group + update async bridging comment - hermes_cli/tools_config.py: remove RL display entry, _DEFAULT_OFF_TOOLSETS, setup block, and rl_training post-setup handler - tools/budget_config.py: remove RL environment reference in docstring - tests/test_model_tools.py: remove rl_tools from expected groups - tests/run_agent/test_streaming_tool_call_repair.py: fix stale cross-reference * chore: remove rl/yc-bench extras and tinker-atropos refs from pyproject.toml - Remove rl extra (atroposlib, tinker, fastapi, uvicorn, wandb) - Remove yc-bench extra - Remove rl_cli from py-modules - Remove [tool.ty.src] exclude for tinker-atropos - Remove [tool.ruff] exclude for tinker-atropos - Regenerate uv.lock * chore: remove tinker-atropos from install/setup scripts - setup-hermes.sh: remove entire tinker-atropos submodule install block - scripts/install.sh: remove both tinker-atropos blocks (Termux + standard) - scripts/install.ps1: remove tinker-atropos block - nix/hermes-agent.nix: remove tinker-atropos pip install line * chore: remove RL references from cli-config.yaml.example * docs: remove Atropos/RL references from README, CONTRIBUTING, AGENTS.md * docs: remove RL/Atropos references from website - Delete: environments.md, rl-training.md, mlops-hermes-atropos-environments.md - sidebars.ts: remove rl-training and environments sidebar entries - optional-skills-catalog.md: remove hermes-atropos-environments row - tools-reference.md: remove entire rl toolset section - toolsets-reference.md: remove rl row + update example - integrations/index.md: remove RL Training bullet - architecture.md: remove environments/ from tree + RL section - contributing.md: remove tinker-atropos setup - updating.md: remove tinker-atropos install + stale submodule update * chore: remove remaining RL/Atropos stragglers - hermes_cli/config.py: remove TINKER_API_KEY + WANDB_API_KEY env var defs - hermes_cli/doctor.py: remove Submodules check section (tinker-atropos) - hermes_cli/setup.py: remove RL Training status check - hermes_cli/status.py: remove Tinker + WandB from API key status display - agent/display.py: remove both rl_* tool preview/activity blocks - website/docs: remove RL references from providers.md + env-variables.md - tests: remove TINKER_API_KEY from conftest, set_config_value, setup_script * chore: remove RL training section from .env.example19 天前
fix(packaging): ship bundled skills in wheel Salvages #23738 by @LeonSGP43. Wheel installs were missing skills/ and optional-skills/ because pyproject's [tool.setuptools.packages.find] only includes Python packages — the skills directories don't have __init__.py so they were silently dropped from the wheel. Adds setup.py with data_files spec emitting skills/* and optional-skills/* under hermes_agent-<v>.data/data/, and a get_bundled_skills_dir() helper in hermes_constants that discovers the wheel-installed location via sysconfig before falling back to a source-checkout path. tools/skills_sync uses the helper so 'hermes update' works for pip-installed users. 15 天前
chore: fix 154 f-strings, simplify getattr/URL patterns, remove dead code (#3119) Three categories of cleanup, all zero-behavioral-change: 1. F-strings without placeholders (154 fixes across 29 files) - Converted f'...' to '...' where no {expression} was present - Heaviest files: run_agent.py (24), cli.py (20), honcho_integration/cli.py (34) 2. Simplify defensive patterns in run_agent.py - Added explicit self._is_anthropic_oauth = False in __init__ (before the api_mode branch that conditionally sets it) - Replaced 7x getattr(self, '_is_anthropic_oauth', False) with direct self._is_anthropic_oauth (attribute always initialized now) - Added _is_openrouter_url() and _is_anthropic_url() helper methods - Replaced 3 inline 'openrouter' in self._base_url_lower checks 3. Remove dead code in small files - hermes_cli/claw.py: removed unused 'total' computation - tools/fuzzy_match.py: removed unused strip_indent() function and pattern_stripped variable Full test suite: 6184 passed, 0 failures E2E PTY: banner clean, tool calls work, zero garbled ANSI2 个月前
feat(x_search): gated X (Twitter) search tool with OAuth-or-API-key auth (#26763) * feat(x_search): gated X (Twitter) search tool with OAuth-or-API-key auth Salvages tools/x_search_tool.py from the closed PR #10786 (originally by @Jaaneek) and reworks its credential resolution so the tool registers when EITHER xAI credential path is available: * XAI_API_KEY (paid xAI API key) is set in ~/.hermes/.env or the env, OR * The user is signed in via xAI Grok OAuth — SuperGrok subscription — i.e. hermes auth add xai-oauth has been run Both paths route through xAI's built-in x_search Responses tool at https://api.x.ai/v1/responses. When both credentials exist OAuth wins, matching tools/xai_http.py's existing preference order (uses SuperGrok quota instead of paid API spend). The check_fn calls resolve_xai_http_credentials() which auto-refreshes the OAuth access token if it's within the refresh skew window, so a True return means the bearer is fetchable AND non-empty. Wiring - tools/x_search_tool.py — new tool, ~370 LOC. Schema gated by check_fn, bearer resolved per-call so revoked OAuth surfaces a clean tool_error rather than an HTTP 401. - toolsets.py — "x_search" toolset def. NOT added to _HERMES_CORE_TOOLS; users opt in via hermes tools. - hermes_cli/tools_config.py — CONFIGURABLE_TOOLSETS entry + TOOL_CATEGORIES block with two provider options (OAuth + API key) sharing the existing xai_grok post_setup hook for credential bootstrap. - hermes_cli/config.py — DEFAULT_CONFIG["x_search"] with model / timeout_seconds / retries. Additive nested key; no version bump. - tests/tools/test_x_search_tool.py — 13 tests covering HTTP shape, handle validation, citation extraction, 4xx/5xx/timeout handling, and the full credential-resolution matrix (OAuth-only, API-key-only, both-set, neither-set, resolver-raises, config overrides, registry registration). - website/docs/guides/xai-grok-oauth.md — adds X Search to the direct-to-xAI tools section with off-by-default note. - website/docs/user-guide/features/tools.md — new row in the tools table. Off by default — users enable via hermes tools → 🐦 X (Twitter) Search. Schema only appears to the model when xAI credentials are configured. Co-authored-by: Jaaneek <Jaaneek@users.noreply.github.com> * docs(x_search): add dedicated feature page + reference entries - website/docs/user-guide/features/x-search.md (new) — full feature walkthrough: authentication, enablement, configuration, parameters, returned fields, example, troubleshooting, see-also links. - website/docs/reference/tools-reference.md — new "x_search" toolset section with parameter docs and credential gating note. - website/docs/reference/toolsets-reference.md — new row in the toolset catalog table. - website/sidebars.ts — wires the new feature page under Media & Web, after web-search. --------- Co-authored-by: Jaaneek <Jaaneek@users.noreply.github.com>18 天前
fix: guard yaml.safe_load, flock unlock, TOCTOU races, and atomic writes 1. trajectory_compressor.py: yaml.safe_load() returns None on empty files, crashing with TypeError on if 'tokenizer' in data. Fix by adding or {} fallback. (HIGH — blocks startup with empty config) 2. 6 files with fcntl.flock(LOCK_UN) in finally blocks without try/except: cron/scheduler.py, hermes_cli/auth.py, agent/shell_hooks.py, tools/skill_usage.py, tools/environments/file_sync.py, tools/memory_tool.py. If unlock raises OSError, fd.close() is skipped and the lock is held forever. The msvcrt branches already had try/except; the fcntl branches did not. Fix by wrapping in try/except (OSError, IOError): pass. 3. agent/copilot_acp_client.py line 639: TOCTOU race — path.exists() followed by path.read_text() with no try/except. If file is deleted between the check and the read, FileNotFoundError propagates. Fix by using try/except FileNotFoundError. 4. gateway/sticker_cache.py: non-atomic write via Path.write_text() can leave truncated JSON on crash, causing JSONDecodeError on next load. Fix by writing to tempfile + fsync + os.replace (atomic). 15 天前
fix(cli): preserve config comments on setting writes 24 天前
fix(deps): bump pydantic to 2.13.4 to avoid pydantic-core thread segfault (#29021) * fix(deps): bump pydantic to 2.13.4 to avoid pydantic-core thread segfault pydantic-core 2.41.5 (pulled by pydantic==2.12.5) segfaults when the OpenAI SDK's Responses API resource (client.responses.create / client.responses.stream) is exercised from a non-main threading.Thread. Hermes always dispatches codex_responses calls from a daemon thread in agent/chat_completion_helpers.py:_call, so the crash is 100% reproducible whenever the active provider is xai-oauth or openai-codex. Symptom: hermes -z "ping" (or any oneshot path) dies with SIGSEGV / exit 139 and zero output — hermes_cli/oneshot.py redirects stderr to /dev/null, hiding the crash. Bumping pydantic to 2.13.4 pulls in pydantic-core 2.46.4, which eliminates the crash. Verified end-to-end: hermes -z "ping" against xai-oauth/grok-4.3 now returns the expected response. Minimal repro (any OpenAI base_url; not xAI-specific): import threading from openai import OpenAI cli = OpenAI(api_key="sk-bogus", base_url="https://api.openai.com/v1") def go(): try: cli.responses.create(model="gpt-4o", input="ping") except BaseException as e: print(type(e).__name__) threading.Thread(target=go).start() # → SIGSEGV with pydantic-core 2.41.5; clean 401 with 2.46.4 * chore(deps): regenerate uv.lock for pydantic 2.13.4 bump14 天前

Hermes Agent

Hermes Agent ☤

Documentation Discord License: MIT Built by Nous Research English

Nous Research 构建的自进化 AI 代理。 它是唯一内置学习闭环的智能代理——从经验中创建技能,在使用中改进技能,主动持久化知识,搜索过往对话,并在跨会话中逐步构建对你的深度理解。可以在 $5 的 VPS 上运行,也可以在 GPU 集群上运行,或者使用几乎零成本的 Serverless 基础设施。它不绑定你的笔记本——你可以在 Telegram 上与它对话,而它在云端 VM 上工作。

支持任意模型——Nous PortalOpenRouter(200+ 模型)、NVIDIA NIM(Nemotron)、小米 MiMoz.ai/GLMKimi/MoonshotMiniMaxHugging Face、OpenAI,或自定义端点。使用 hermes model 即可切换——无需改代码,无锁定。

真正的终端界面完整的 TUI,支持多行编辑、斜杠命令自动补全、对话历史、中断重定向和流式工具输出。
随你所在Telegram、Discord、Slack、WhatsApp、Signal 和 CLI——全部从单个网关进程运行。语音备忘录转写、跨平台对话连续性。
闭环学习代理管理记忆并定期自我提醒。复杂任务后自动创建技能。技能在使用中自我改进。FTS5 会话搜索配合 LLM 摘要实现跨会话回溯。Honcho 辩证式用户建模。兼容 agentskills.io 开放标准。
定时自动化内置 cron 调度器,支持向任何平台投递。日报、夜间备份、周审计——全部用自然语言描述,无人值守运行。
委派与并行生成隔离子代理处理并行工作流。编写 Python 脚本通过 RPC 调用工具,将多步管道压缩为零上下文开销的轮次。
随处运行六种终端后端——本地、Docker、SSH、Daytona、Singularity 和 Modal。Daytona 和 Modal 提供 Serverless 持久化——代理环境空闲时休眠、按需唤醒,空闲期间几乎零成本。$5 VPS 或 GPU 集群都能跑。
研究就绪批量轨迹生成、轨迹压缩——用于训练下一代工具调用模型。

快速安装

curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash

支持 Linux、macOS、WSL2 和 Android (Termux)。安装程序会自动处理平台特定的配置。

Android / Termux: 已测试的手动安装路径请参考 Termux 指南。在 Termux 上,Hermes 会安装精选的 .[termux] 扩展,因为完整的 .[all] 扩展会拉取 Android 不兼容的语音依赖。

Windows: 原生 Windows 不受支持。请安装 WSL2 并运行上述命令。

安装后:

source ~/.bashrc    # 重新加载 shell(或: source ~/.zshrc)
hermes              # 开始对话!

快速入门

hermes              # 交互式 CLI — 开始对话
hermes model        # 选择 LLM 提供商和模型
hermes tools        # 配置启用的工具
hermes config set   # 设置单个配置项
hermes gateway      # 启动消息网关(Telegram、Discord 等)
hermes setup        # 运行完整设置向导(一次性配置所有内容)
hermes claw migrate # 从 OpenClaw 迁移(如果来自 OpenClaw)
hermes update       # 更新到最新版本
hermes doctor       # 诊断问题

📖 完整文档 →

CLI 与消息平台 快速对照

Hermes 有两种入口:用 hermes 启动终端 UI,或运行网关从 Telegram、Discord、Slack、WhatsApp、Signal 或 Email 与之对话。进入对话后,许多斜杠命令在两种界面中通用。

操作 CLI 消息平台
开始对话 hermes 运行 hermes gateway setup + hermes gateway start,然后给机器人发消息
开始新对话 /new/reset /new/reset
更换模型 /model [provider:model] /model [provider:model]
设置人格 /personality [name] /personality [name]
重试或撤销上一轮 /retry/undo /retry/undo
压缩上下文 / 查看用量 /compress/usage/insights [--days N] /compress/usage/insights [days]
浏览技能 /skills/<skill-name> /skills/<skill-name>
中断当前工作 Ctrl+C 或发送新消息 /stop 或发送新消息
平台特定状态 /platforms /status/sethome

完整命令列表请参阅 CLI 指南消息网关指南


文档

所有文档位于 hermes-agent.nousresearch.com/docs

章节 内容
快速开始 安装 → 设置 → 2 分钟内开始首次对话
CLI 使用 命令、快捷键、人格、会话
配置 配置文件、提供商、模型、所有选项
消息网关 Telegram、Discord、Slack、WhatsApp、Signal、Home Assistant
安全 命令审批、DM 配对、容器隔离
工具与工具集 40+ 工具、工具集系统、终端后端
技能系统 过程记忆、技能中心、创建技能
记忆 持久记忆、用户画像、最佳实践
MCP 集成 连接任意 MCP 服务器扩展能力
定时调度 定时任务与平台投递
上下文文件 影响每次对话的项目上下文
架构 项目结构、代理循环、关键类
贡献 开发设置、PR 流程、代码风格
CLI 参考 所有命令和标志
环境变量 完整环境变量参考

从 OpenClaw 迁移

如果你来自 OpenClaw,Hermes 可以自动导入你的设置、记忆、技能和 API 密钥。

首次安装时: 安装向导(hermes setup)会自动检测 ~/.openclaw 并在配置开始前提供迁移选项。

安装后任意时间:

hermes claw migrate              # 交互式迁移(完整预设)
hermes claw migrate --dry-run    # 预览将要迁移的内容
hermes claw migrate --preset user-data   # 仅迁移用户数据,不含密钥
hermes claw migrate --overwrite  # 覆盖已有冲突

导入内容:

  • SOUL.md — 人格文件
  • 记忆 — MEMORY.md 和 USER.md 条目
  • 技能 — 用户创建的技能 → ~/.hermes/skills/openclaw-imports/
  • 命令白名单 — 审批模式
  • 消息设置 — 平台配置、允许用户、工作目录
  • API 密钥 — 白名单中的密钥(Telegram、OpenRouter、OpenAI、Anthropic、ElevenLabs)
  • TTS 资产 — 工作区音频文件
  • 工作区指令 — AGENTS.md(使用 --workspace-target

使用 hermes claw migrate --help 查看所有选项,或使用 openclaw-migration 技能进行交互式代理引导迁移(含干运行预览)。


贡献

欢迎贡献!请参阅 贡献指南 了解开发设置、代码风格和 PR 流程。

贡献者快速开始——克隆并使用 setup-hermes.sh

git clone https://github.com/NousResearch/hermes-agent.git
cd hermes-agent
./setup-hermes.sh     # 安装 uv、创建 venv、安装 .[all]、创建符号链接 ~/.local/bin/hermes
./hermes              # 自动检测 venv,无需先 source

手动安装(等效于上述命令):

curl -LsSf https://astral.sh/uv/install.sh | sh
uv venv venv --python 3.11
source venv/bin/activate
uv pip install -e ".[all,dev]"
python -m pytest tests/ -q

社区


许可证

MIT — 详见 LICENSE

Nous Research 构建。

项目介绍

The agent that grows with you

定制我的领域

下载使用量

0

项目总下载次数(含Clone、Pull、 zip 包及 release 下载),每日凌晨更新

语言类型

Python88.7%
TypeScript5.34%
TSX3.29%
TeX1.09%
Shell0.53%