← back
artifacts/transcript.md
HERMES_AGENT_TIMEOUT功能说明
workspace
content
# Transcript · HERMES_AGENT_TIMEOUT功能说明 ## user · 2026-07-03 05:49:01 提示:HERMES_AGENT_TIMEOUT=0 disables the gateway inactivity kill for a running agent — use for long research run 这是啥功能,要启用吗?去社区了解一下 ## assistant · 2026-07-03 05:51:21 ### tool calls - `skill_view` `{"name":"hermes-agent"}` - `terminal` `{"command":"python3 - <<'PY'\nimport urllib.request\nurls=[\n 'https://hermes-agent.nousresearch.com/docs',\n 'https://api.github.com/search/issues?q=%22HERMES_AGENT_TIMEOUT%22+OR+%22Tool+Slimmer%22+Hermes+Agent',\n 'https://api.github.com/search/code?q=%22HERMES_AGENT_TIMEOUT%22',\n]\nfor u in urls:\n print('\\nURL',u)\n try:\n req=urllib.request.Request(u,headers={'User-Agent':'Hermes-Agent-Check'})\n with urllib.request.urlopen(req,timeout=20) as r:\n data=r.read(4000)\n print(r.status, r.headers.get('content-type'))\n print(data[:1000].decode('utf-8','replace'))\n except Exception as e:\n print('ERR',type(e).__name__,e)\nPY", "timeout":60}` ## tool · 2026-07-03 05:51:21 {"success": true, "name": "hermes-agent", "description": "Configure, extend, or contribute to Hermes Agent.", "tags": ["hermes", "setup", "configuration", "multi-agent", "spawning", "cli", "gateway", "development"], "related_skills": ["claude-code", "codex", "opencode"], "content": "---\nname: hermes-agent\ndescription: \"Configure, extend, or contribute to Hermes Agent.\"\nversion: 2.1.0\nauthor: Hermes Agent + Teknium\nlicense: MIT\nmetadata:\n hermes:\n tags: [hermes, setup, configuration, multi-agent, spawning, cli, gateway, development]\n homepage: https://github.com/NousResearch/hermes-agent\n related_skills: [claude-code, codex, opencode]\n---\n\n# Hermes Agent\n\nHermes Agent is an open-source AI agent framework by Nous Research that runs in your terminal, messaging platforms, and IDEs. It belongs to the same category as Claude Code (Anthropic), Codex (OpenAI), and OpenClaw — autonomous coding and task-execution agents that use tool calling to interact with your system. Hermes works with any LLM provider (OpenRouter, Anthropic, OpenAI, DeepSeek, local models, and 15+ others) and runs on Linux, macOS, and WSL.\n\nWhat makes Hermes different:\n\n- **Self-improving through skills** — Hermes learns from experience by saving reusable procedures as skills. When it solves a complex problem, discovers a workflow, or gets corrected, it can persist that knowledge as a skill document that loads into future sessions. Skills accumulate over time, making the agent better at your specific tasks and environment.\n- **Persistent memory across sessions** — remembers who you are, your preferences, environment details, and lessons learned. Pluggable memory backends (built-in, Honcho, Mem0, and more) let you choose how memory works.\n- **Multi-platform gateway** — the same agent runs on Telegram, Discord, Slack, WhatsApp, Signal, Matrix, Email, and 10+ other platforms with full tool access, not just chat.\n- **Provider-agnostic** — swap models and providers mid-workflow without changing anything else. Credential pools rotate across multiple API keys automatically.\n- **Profiles** — run multiple independent Hermes instances with isolated configs, sessions, skills, and memory.\n- **Extensible** — plugins, MCP servers, custom tools, webhook triggers, cron scheduling, and the full Python ecosystem.\n\nPeople use Hermes for software development, research, system administration, data analysis, content creation, home automation, and anything else that benefits from an AI agent with persistent context and full system access.\n\n**This skill helps you work with Hermes Agent effectively** — setting it up, configuring features, spawning additional agent instances, troubleshooting issues, finding the right commands and settings, and understanding how the system works when you need to extend or contribute to it.\n\n**Docs:** https://hermes-agent.nousresearch.com/docs/\n\n## Quick Start\n\n```bash\n# Install\ncurl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash\n\n# Interactive chat (default)\nhermes\n\n# Single query\nhermes chat -q \"What is the capital of France?\"\n\n# Setup wizard\nhermes setup\n\n# Change model/provider\nhermes model\n\n# Check health\nhermes doctor\n```\n\n---\n\n## CLI Reference\n\n### Global Flags\n\n```\nhermes [flags] [command]\n\n --version, -V Show version\n --resume, -r SESSION Resume session by ID or title\n --continue, -c [NAME] Resume by name, or most recent session\n --worktree, -w Isolated git worktree mode (parallel agents)\n --skills, -s SKILL Preload skills (comma-separate or repeat)\n --profile, -p NAME Use a named profile\n --yolo Skip dangerous command approval\n --pass-session-id Include session ID in system prompt\n```\n\nNo subcommand defaults to `chat`.\n\n### Chat\n\n```\nhermes chat [flags]\n -q, --query TEXT Single query, non-interactive\n -m, --model MODEL Model (e.g. anthropic/claude-sonnet-4)\n -t, --toolsets LIST Comma-separated toolsets\n --provider PROVIDER Force provider (openrouter, anthropic, nous, etc.)\n -v, --verbose Verbose output\n -Q, --quiet Suppress banner, spinner, tool previews\n --checkpoints Enable filesystem checkpoints (/rollback)\n --source TAG Session source tag (default: cli)\n```\n\n### Configuration\n\n```\nhermes setup [section] Interactive wizard (model|terminal|gateway|tools|agent)\nhermes model Interactive model/provider picker\nhermes config View current config\nhermes config edit Open config.yaml in $EDITOR\nhermes config set KEY VAL Set a config value\nhermes config path Print config.yaml path\nhermes config env-path Print .env path\nhermes config check Check for missing/outdated config\nhermes config migrate Update config with new options\nhermes login [--provider P] OAuth login (nous, openai-codex)\nhermes logout Clear stored auth\nhermes doctor [--fix] Check dependencies and config\nhermes status [--all] Show component status\n```\n\n### Tools & Skills\n\n```\nhermes tools Interactive tool enable/disable (curses UI)\nhermes tools list Show all tools and status\nhermes tools enable NAME Enable a toolset\nhermes tools disable NAME Disable a toolset\n\nhermes skills list List installed skills\nhermes skills search QUERY Search the skills hub\nhermes skills install ID Install a skill (ID can be a hub identifier OR a direct https://…/SKILL.md URL; pass --name to override when frontmatter has no name)\nhermes skills inspect ID Preview without installing\nhermes skills config Enable/disable skills per platform\nhermes skills check Check for updates\nhermes skills update Update outdated skills\nhermes skills uninstall N Remove a hub skill\nhermes skills publish PATH Publish to registry\nhermes skills browse Browse all available skills\nhermes skills tap add REPO Add a GitHub repo as skill source\n```\n\n### MCP Servers\n\n```\nhermes mcp serve Run Hermes as an MCP server\nhermes mcp add NAME Add an MCP server (--url or --command)\nhermes mcp remove NAME Remove an MCP server\nhermes mcp list List configured servers\nhermes mcp test NAME Test connection\nhermes mcp configure NAME Toggle tool selection\n```\n\n### Gateway (Messaging Platforms)\n\n```\nhermes gateway run Start gateway foreground\nhermes gateway install Install as background service\nhermes gateway start/stop Control the service\nhermes gateway restart Restart the service\nhermes gateway status Check status\nhermes gateway setup Configure platforms\n```\n\nSupported platforms: Telegram, Discord, Slack, WhatsApp, Signal, Email, SMS, Matrix, Mattermost, Home Assistant, DingTalk, Feishu, WeCom, BlueBubbles (iMessage), Weixin (WeChat), API Server, Webhooks. Open WebUI connects via the API Server adapter.\n\n### Platform Isolation for Multi-Instance Deployments\n\nWhen running two Hermes instances (e.g. VPS + WSL2), never configure both with the same platform credentials — a Telegram bot token or WeChat session can only be consumed by one gateway at a time.\n\nPreferred split:\n- VPS remains the sole gateway owner for WeChat and/or Telegram.\n- WSL2 skips gateway entirely; deliver results via HTTP/API back to the VPS instance, or use a separate platform (Feishu) on the WSL2 instance.\n\nThis avoids duplicate replies, race conditions on updates, and session desynchronization. Profiles do not isolate platform listeners — the gateway binding is per-process, not per-profile.\n\nPlatform docs: https://hermes-agent.nousresearch.com/docs/user-guide/messaging/\n\n### Sessions\n\n```\nhermes sessions list List recent sessions\nhermes sessions browse Interactive ...[truncated 47920 chars] ## tool · 2026-07-03 05:51:21 {"output": "URL https://hermes-agent.nousresearch.com/docs\n200 text/html; charset=utf-8\n<!doctype html>\n<html lang=\"en\" dir=\"ltr\" class=\"docs-wrapper plugin-docs plugin-id-default docs-version-current docs-doc-page docs-doc-id-index\" data-has-hydrated=\"false\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"generator\" content=\"Docusaurus v3.9.2\">\n<title data-rh=\"true\">Hermes Agent Documentation | Hermes Agent</title><meta data-rh=\"true\" name=\"viewport\" content=\"width=device-width,initial-scale=1\"><meta data-rh=\"true\" name=\"twitter:card\" content=\"summary_large_image\"><meta data-rh=\"true\" property=\"og:image\" content=\"https://hermes-agent.nousresearch.com/docs/img/hermes-agent-banner.png\"><meta data-rh=\"true\" name=\"twitter:image\" content=\"https://hermes-agent.nousresearch.com/docs/img/hermes-agent-banner.png\"><meta data-rh=\"true\" property=\"og:url\" content=\"https://hermes-agent.nousresearch.com/docs/\"><meta data-rh=\"true\" property=\"og:locale\" content=\"en\"><meta data-rh=\"true\" property=\"og:locale:alternate\" content=\"zh_Hans\"><meta data-rh=\"true\" name=\"docusaurus_locale\" content=\"en\"\n\nURL https://api.github.com/search/issues?q=%22HERMES_AGENT_TIMEOUT%22+OR+%22Tool+Slimmer%22+Hermes+Agent\n200 application/json; charset=utf-8\n{\"total_count\":121848,\"incomplete_results\":false,\"items\":[{\"url\":\"https://api.github.com/repos/NousResearch/hermes-agent/issues/50120\",\"repository_url\":\"https://api.github.com/repos/NousResearch/hermes-agent\",\"labels_url\":\"https://api.github.com/repos/NousResearch/hermes-agent/issues/50120/labels{/name}\",\"comments_url\":\"https://api.github.com/repos/NousResearch/hermes-agent/issues/50120/comments\",\"events_url\":\"https://api.github.com/repos/NousResearch/hermes-agent/issues/50120/events\",\"html_url\":\"https://github.com/NousResearch/hermes-agent/issues/50120\",\"id\":4710291432,\"node_id\":\"I_kwDOPRF1G88AAAABGMFX6A\",\"number\":50120,\"title\":\"bug(gateway): malformed HERMES_TELEGRAM_FOLLOWUP_GRACE_SECONDS crashes message handler (bare float, missed by _float_env extraction)\",\"user\":{\"login\":\"Bartok9\",\"id\":259807879,\"node_id\":\"U_kgDOD3xahw\",\"avatar_url\":\"https://avatars.githubusercontent.com/u/259807879?v=4\",\"gravatar_id\":\"\",\"url\":\"https://api.github.com/users/Bartok9\",\"html_url\":\"https://github.com/\n\nURL https://api.github.com/search/code?q=%22HERMES_AGENT_TIMEOUT%22\nERR HTTPError HTTP Error 401: Unauthorized", "exit_code": 0, "error": null} ## assistant · 2026-07-03 05:51:22 ### tool calls - `search_files` `{"path":"/usr/local/lib/hermes-agent","pattern":"HERMES_AGENT_TIMEOUT|Tool Slimmer|tool_slimmer|inactivity","target":"content","output_mode":"content","context":2,"limit":50}` - `terminal` `{"command":"python3 - <<'PY'\nimport urllib.parse, urllib.request, json\nqueries=[\n '\"HERMES_AGENT_TIMEOUT\" repo:NousResearch/hermes-agent',\n '\"Tool Slimmer\" repo:NousResearch/hermes-agent',\n '\"tool_slimmer\" repo:NousResearch/hermes-agent',\n '\"inactivity kill\" \"hermes-agent\"',\n '\"HERMES_AGENT_TIMEOUT\" \"hermes-agent\"',\n]\nfor q in queries:\n url='https://api.github.com/search/issues?per_page=5&q='+urllib.parse.quote(q)\n print('\\nQUERY',q)\n try:\n req=urllib.request.Request(url,headers={'User-Agent':'Hermes-Agent-Check','Accept':'application/vnd.github+json'})\n with urllib.request.urlopen(req,timeout=20) as r:\n data=json.load(r)\n print('total',data.get('total_count'))\n for it in data.get('items',[])[:5]:\n print('-',it.get('html_url'), '|', it.get('title'))\n except Exception as e:\n print('ERR',type(e).__name__,e)\nPY","timeout":60}` - `terminal` `{"command":"set -e\nprintf 'service env refs:\\n'\nsystemctl cat hermes-gateway 2>/dev/null | grep -E 'HERMES_AGENT_TIMEOUT|Environment|ExecStart' || true\nprintf '\\nprocess env refs:\\n'\nPID=$(pgrep -f 'hermes.*gateway|gateway.*run' | head -1 || true)\nif [ -n \"$PID\" ] && [ -r /proc/$PID/environ ]; then tr '\\0' '\\n' < /proc/$PID/environ | grep '^HERMES_AGENT_TIMEOUT=' || true; else echo 'no readable gateway process env'; fi\nprintf '\\nconfig/env refs:\\n'\ngrep -R \"HERMES_AGENT_TIMEOUT\" -n /root/.hermes /etc/systemd /root 2>/dev/null | head -20 || true","timeout":30}` ## tool · 2026-07-03 05:51:22 {"total_count": 222, "matches_format": "path-grouped: each file path on its own line, followed by indented '<line>: <content>' rows for matches in that file", "matches_text": "/usr/local/lib/hermes-agent/tests/cron/test_cron_inactivity_timeout.py\n 1: \"\"\"Tests for cron job inactivity-based timeout.\n 2: \n 3: Tests cover:\n 4: - Active agent runs indefinitely (no inactivity timeout)\n 5: - Idle agent triggers inactivity timeout with diagnostic info\n 6: - Unlimited timeout (HERMES_CRON_TIMEOUT=0)\n 7: - Backward compat: HERMES_CRON_TIMEOUT env var still works\n 81: \n 82: class TestInactivityTimeout:\n 83: \"\"\"Test the inactivity-based timeout polling loop in cron scheduler.\"\"\"\n 84: \n 85: def test_active_agent_completes_normally(self):\n 86: \"\"\"An agent that finishes quickly should return its result.\"\"\"\n 87: agent = FakeAgent(idle_seconds=0.0)\n 88: _cron_inactivity_limit = 10.0\n 89: _POLL_INTERVAL = 0.1\n 90: \n 91: pool = concurrent.futures.ThreadPoolExecutor(max_workers=1)\n 92: future = pool.submit(agent.run_conversation, \"test prompt\")\n 93: _inactivity_timeout = False\n 94: \n 95: result = None\n 103: _act = agent.get_activity_summary()\n 104: _idle_secs = _act.get(\"seconds_since_activity\", 0.0)\n 105: if _idle_secs >= _cron_inactivity_limit:\n 106: _inactivity_timeout = True\n 107: break\n 108: \n 110: assert result is not None\n 111: assert result[\"final_response\"] == \"Done\"\n 112: assert not _inactivity_timeout\n 113: assert not agent._interrupted\n 114: \n 125: )\n 126: \n 127: _cron_inactivity_limit = 0.5 # 0.5s inactivity triggers timeout\n 128: _POLL_INTERVAL = 0.1\n 129: \n 130: pool = concurrent.futures.ThreadPoolExecutor(max_workers=1)\n 131: future = pool.submit(agent.run_conversation, \"test prompt\")\n 132: _inactivity_timeout = False\n 133: \n 134: result = None\n 145: except Exception:\n 146: pass\n 147: if _idle_secs >= _cron_inactivity_limit:\n 148: _inactivity_timeout = True\n 149: break\n 150: \n 151: pool.shutdown(wait=False, cancel_futures=True)", "truncated": true} [Hint: Results truncated. Use offset=50 to see more, or narrow with a more specific pattern or file_glob.] ## tool · 2026-07-03 05:51:22 {"output": "QUERY \"HERMES_AGENT_TIMEOUT\" repo:NousResearch/hermes-agent\ntotal 24\n- https://github.com/NousResearch/hermes-agent/issues/50120 | bug(gateway): malformed HERMES_TELEGRAM_FOLLOWUP_GRACE_SECONDS crashes message handler (bare float, missed by _float_env extraction)\n- https://github.com/NousResearch/hermes-agent/pull/8298 | fix(gateway): add inactivity timeout for /background tasks\n- https://github.com/NousResearch/hermes-agent/pull/50227 | fix(gateway): harden HERMES_TELEGRAM_FOLLOWUP_GRACE_SECONDS parsing via _float_env (#50120)\n- https://github.com/NousResearch/hermes-agent/pull/13873 | fix(gateway): harden timeout env parsing to prevent message handler crashes\n- https://github.com/NousResearch/hermes-agent/pull/51324 | fix(gateway): harden HERMES_TELEGRAM_FOLLOWUP_GRACE_SECONDS env parsing (salvage of #13873 by @Junass1)\n\nQUERY \"Tool Slimmer\" repo:NousResearch/hermes-agent\ntotal 5\n- https://github.com/NousResearch/hermes-agent/issues/6839 | Feature: Lazy Tool Schema Loading — Two-Pass Tool Injection to Reduce Token Overhead\n- https://github.com/NousResearch/hermes-agent/issues/17649 | Feature: Semantic Skill Retrieval with SQLite FTS5 — Replace 4500-token broadcast with on-demand search\n- https://github.com/NousResearch/hermes-agent/issues/13332 | Feature: Hybrid Tool Pre-Selection (Semantic + Keyword) — RAG-style schema injection to reduce token overhead without extra LLM round trips\n- https://github.com/NousResearch/hermes-agent/pull/48622 | feat(tools): add stub_mode to reduce main-thread tool schema tokens\n- https://github.com/NousResearch/hermes-agent/issues/18074 | [Feature] Support Anthropic Tool Search for MCP tools\n\nQUERY \"tool_slimmer\" repo:NousResearch/hermes-agent\ntotal 0\n\nQUERY \"inactivity kill\" \"hermes-agent\"\ntotal 19\n- https://github.com/NousResearch/hermes-agent/pull/11772 | fix(agent): heartbeat during sparse streaming to avoid cron/gateway inactivity kills\n- https://github.com/NousResearch/hermes-agent/pull/8298 | fix(gateway): add inactivity timeout for /background tasks\n- https://github.com/NousResearch/hermes-agent/issues/55038 | [Docs]: bundled hermes-agent skill says cron has a \"3-minute hard interrupt per run\" — scheduler actually uses a 600s inactivity timeout\n- https://github.com/2lab-ai/soma-work/issues/442 | [Analysis] hermes-agent v0.4→v0.8 Delta: 신규 피쳐 및 아키텍처 변경점\n- https://github.com/NousResearch/hermes-agent/pull/6584 | fix: disable delegation toolset in cron to prevent inactivity timeout kill\n\nQUERY \"HERMES_AGENT_TIMEOUT\" \"hermes-agent\"\ntotal 36\n- https://github.com/NousResearch/hermes-agent/issues/50120 | bug(gateway): malformed HERMES_TELEGRAM_FOLLOWUP_GRACE_SECONDS crashes message handler (bare float, missed by _float_env extraction)\n- https://github.com/NousResearch/hermes-agent/pull/8298 | fix(gateway): add inactivity timeout for /background tasks\n- https://github.com/NousResearch/hermes-agent/pull/50227 | fix(gateway): harden HERMES_TELEGRAM_FOLLOWUP_GRACE_SECONDS parsing via _float_env (#50120)\n- https://github.com/NousResearch/hermes-agent/pull/51324 | fix(gateway): harden HERMES_TELEGRAM_FOLLOWUP_GRACE_SECONDS env parsing (salvage of #13873 by @Junass1)\n- https://github.com/NousResearch/hermes-agent/pull/13873 | fix(gateway): harden timeout env parsing to prevent message handler crashes", "exit_code": 0, "error": null} ## tool · 2026-07-03 05:51:22 {"output": "service env refs:\n\nprocess env refs:\nHERMES_AGENT_TIMEOUT=1800\n\nconfig/env refs:\n\n[Command timed out after 30s]", "exit_code": 124, "error": null} ## assistant · 2026-07-03 05:51:22 ### tool calls - `search_files` `{"context":4,"limit":80,"output_mode":"content","path":"/usr/local/lib/hermes-agent","pattern":"HERMES_AGENT_TIMEOUT","target":"content"}` - `terminal` `{"command":"python3 - <<'PY'\nimport urllib.request, json\nfor url in ['https://api.github.com/repos/NousResearch/hermes-agent/pulls/8298','https://api.github.com/repos/NousResearch/hermes-agent/issues/55038','https://api.github.com/repos/NousResearch/hermes-agent/pulls/11772','https://api.github.com/repos/NousResearch/hermes-agent/issues/6839']:\n print('\\nURL',url)\n try:\n req=urllib.request.Request(url,headers={'User-Agent':'Hermes-Agent-Check','Accept':'application/vnd.github+json'})\n with urllib.request.urlopen(req,timeout=20) as r: data=json.load(r)\n print('title:',data.get('title'))\n body=(data.get('body') or '')\n print(body[:2500])\n except Exception as e: print('ERR',type(e).__name__,e)\nPY","timeout":60}` ## tool · 2026-07-03 05:51:22 {"total_count": 252, "matches_format": "path-grouped: each file path on its own line, followed by indented '<line>: <content>' rows for matches in that file", "matches_text": "/usr/local/lib/hermes-agent/tests/gateway/test_gateway_inactivity_timeout.py\n 4: - Warning fires once when inactivity reaches gateway_timeout_warning threshold\n 5: - Warning does not fire when gateway_timeout is 0 (unlimited)\n 6: - Warning fires only once per run, not on every poll\n 7: - Full timeout still fires at gateway_timeout threshold\n 8: - Warning respects HERMES_AGENT_TIMEOUT_WARNING env var\n 9: - Warning disabled when gateway_timeout_warning is 0\n 10: \"\"\"\n 11: \n 12: import concurrent.futures\n 236: assert _warning_fired\n 237: assert _inactivity_timeout\n 238: \n 239: def test_warning_env_var_respected(self, monkeypatch):\n 240: \"\"\"HERMES_AGENT_TIMEOUT_WARNING env var is parsed correctly.\"\"\"\n 241: monkeypatch.setenv(\"HERMES_AGENT_TIMEOUT_WARNING\", \"600\")\n 242: _warning = float(os.getenv(\"HERMES_AGENT_TIMEOUT_WARNING\", 900))\n 243: assert _warning == 600.0\n 244: \n 245: def test_warning_zero_means_disabled(self, monkeypatch):\n 246: \"\"\"HERMES_AGENT_TIMEOUT_WARNING=0 disables the warning.\"\"\"\n 247: monkeypatch.setenv(\"HERMES_AGENT_TIMEOUT_WARNING\", \"0\")\n 248: _raw = float(os.getenv(\"HERMES_AGENT_TIMEOUT_WARNING\", 900))\n 249: _warning = _raw if _raw > 0 else None\n 250: assert _warning is None\n 251: \n 252: def test_unlimited_timeout_no_warning(self):\n/usr/local/lib/hermes-agent/tests/gateway/test_running_agent_session_toggles.py\n 91: runner._emit_gateway_run_progress = AsyncMock()\n 92: \n 93: # Simulate agent actively running for this session so the guard fires.\n 94: # Note: the stale-eviction branch calls agent.get_activity_summary() and\n 95: # compares seconds_since_activity against HERMES_AGENT_TIMEOUT. Return a\n 96: # dict with recent activity so the eviction path doesn't clear our\n 97: # fake running agent before the toggle guard runs.\n 98: import time\n 99: sk = build_session_key(_make_source())\n/usr/local/lib/hermes-agent/tests/gateway/test_compression_failure_session_sync.py\n 123: fake_run_agent = types.ModuleType(\"run_agent\")\n 124: fake_run_agent.AIAgent = _CompressionThenFailureAgent\n 125: monkeypatch.setitem(sys.modules, \"run_agent\", fake_run_agent)\n 126: monkeypatch.setenv(\"HERMES_TOOL_PROGRESS_MODE\", \"off\")\n 127: monkeypatch.setenv(\"HERMES_AGENT_TIMEOUT\", \"0\")\n 128: monkeypatch.setattr(gateway_run, \"_load_gateway_config\", lambda: {})\n 129: monkeypatch.setattr(\"gateway.stream_consumer.GatewayStreamConsumer\", _StreamConsumer)\n 130: \n 131: import hermes_cli.tools_config as tools_config\n/usr/local/lib/hermes-agent/tests/gateway/test_config_env_bridge_authority.py\n 41: sys.exit(2)\n 42: \n 43: for k in (\n 44: \"HERMES_MAX_ITERATIONS\",\n 45: \"HERMES_AGENT_TIMEOUT\",\n 46: \"HERMES_AGENT_TIMEOUT_WARNING\",\n 47: \"HERMES_GATEWAY_BUSY_INPUT_MODE\",\n 48: \"HERMES_GATEWAY_BUSY_TEXT_MODE\",\n 49: \"HERMES_TIMEZONE\",\n 50: ):\n 124: \"gateway_timeout\": 1800,\n 125: \"gateway_timeout_warning\": 900,\n 126: })\n 127: _write_env(hermes_home, {\n 128: \"HERMES_AGENT_TIMEOUT\": \"60\",\n 129: \"HERMES_AGENT_TIMEOUT_WARNING\": \"30\",\n 130: })\n 131: \n 132: env = _run_gateway_import(hermes_home, initial_env={})\n 133: \n 134: assert env.get(\"HERMES_AGENT_TIMEOUT\") == \"1800\"\n 135: assert env.get(\"HERMES_AGENT_TIMEOUT_WARNING\") == \"900\"\n 136: \n 137: \n 138: def test_config_display_busy_input_mode_wins_over_stale_env(hermes_home: Path) -> None:\n 139: _write_config(hermes_home, display_cfg={\"busy_input_mode\": \"interrupt\"})\n/usr/local/lib/hermes-agent/gateway/run.py.bak-20260623-174159\n 595: \"\"\"Return the configured auto-continue freshness window in seconds.\n 596: \n 597: Reads ``HERMES_AUTO_CONTINUE_FRESHNESS`` (bridged from\n 598: ``config.yaml`` ``agent.gateway_auto_continue_freshness`` at gateway\n 599: startup, same pattern as ``HERMES_AGENT_TIMEOUT``). Falls back to the\n 600: module default when unset or malformed. Non-positive values disable\n 601: the freshness gate (restores the pre-fix \"always fresh\" behaviour for\n 602: users who want to opt out).\n 603: \"\"\"\n 612: ", "truncated": true} [Hint: Results truncated. Use offset=80 to see more, or narrow with a more specific pattern or file_glob.] ## tool · 2026-07-03 05:51:22 {"output": "URL https://api.github.com/repos/NousResearch/hermes-agent/pulls/8298\ntitle: fix(gateway): add inactivity timeout for /background tasks\n## Summary\n\nBackground tasks spawned by `/background` had two ways to silently drop results:\n\n1. **No inactivity timeout**: Regular agent sessions have a configurable inactivity timeout (default 1800s) that kills stuck agents and notifies the user. Background tasks used a bare `await loop.run_in_executor(None, run_sync)` with no timeout — a hung API call or stuck tool would run forever with no user notification.\n\n2. **CancelledError not caught**: On gateway restart/shutdown, background asyncio tasks are cancelled. `asyncio.CancelledError` inherits from `BaseException` (not `Exception`), so the existing error handler never fired and the task disappeared silently.\n\n### Changes\n\n- Add the same inactivity polling loop used by regular sessions, checking `agent.get_activity_summary()` every 5s against `HERMES_AGENT_TIMEOUT` (default 1800s)\n- Fire a warning message at `HERMES_AGENT_TIMEOUT_WARNING` (default 900s)\n- On timeout, interrupt the agent and send diagnostic info to the user (timeout duration, last active tool)\n- Catch `asyncio.CancelledError` separately to send a best-effort notification on gateway shutdown\n\nNo behavioral change when background tasks complete normally.\n\n## Test plan\n\n- [ ] Run `/background` with a prompt that completes normally — verify result is still delivered\n- [ ] Run `/background` with a task likely to hang — verify warning at 15 min and timeout at 30 min\n- [ ] Restart the gateway while a `/background` task is running — verify cancellation message is sent\n- [ ] Set `HERMES_AGENT_TIMEOUT=0` — verify background tasks run without timeout (unlimited mode)\n\n\nURL https://api.github.com/repos/NousResearch/hermes-agent/issues/55038\ntitle: [Docs]: bundled hermes-agent skill says cron has a \"3-minute hard interrupt per run\" — scheduler actually uses a 600s inactivity timeout\n### Summary\n\nThe bundled `hermes-agent` skill states a cron **\"Invariant\"** that contradicts the actual scheduler code. It says each cron run has a **3-minute hard interrupt**, but the scheduler enforces an **inactivity-based** timeout with a **600s (10-minute)** default that does *not* cap a job that keeps doing work. The mismatch is misleading in opposite directions: it understates the allowed runtime (3 min vs 10 min) **and** misrepresents the mechanism (hard wall-clock kill vs inactivity watchdog).\n\n### Where\n\n`skills/autonomous-ai-agents/hermes-agent/SKILL.md` (the bundled skill), in the **Cron → Invariants** bullet:\n\n> - **Invariants:** 3-minute hard interrupt per run, `.tick.lock` file prevents duplicate ticks across processes, ...\n\n### What the code actually does\n\n`cron/scheduler.py` (the run loop) runs each job under an **inactivity** timeout, not a wall-clock cap. From the in-code comment and logic:\n\n```python\n# Run the agent with an *inactivity*-based timeout: the job can run\n# for hours if it's actively calling tools / receiving stream tokens,\n# but a hung API call or stuck tool with no activity for the configured\n# duration is caught and killed. Default 600s (10 min inactivity);\n# override via HERMES_CRON_TIMEOUT env var. 0 = unlimited.\n...\n_cron_timeout = 600.0 # default when env unset\n_cron_inactivity_limit = _cron_timeout if _cron_timeout > 0 else None\n_POLL_INTERVAL = 5.0 # polls activity every 5s\n```\n\nThe loop polls the agent's activity tracker every 5s and only interrupts when there has been no activity (no tool call, API call, or stream delta) for the configured limit. So:\n\n- The default is **600s of inactivity**, not a 3-minute wall-clock cap.\n- An actively-working job (tool calls / streaming) can run **well beyond 3 minutes** — \"hours,\" per the comment.\n- It is overridable via `HERMES_CRON_TIMEOUT` (and `0` = unlimited) — which the skill text doesn't mention at all.\n\n### Why it matters\n\nThe \"3-minute hard interrupt\" wording leads a reader to architect around a cap that doesn't exist — e.g. splitting a legitimately long, active job (a council turn plus parallel inspectors) into a needless multi-tick design to \"beat the 180s kill,\" when a single tick is actually fine. It also hides the real, useful knob (`HERMES_CRON_TIMEOUT`).\n\n### Suggested fix\n\nReplace the invariant line with one that matches the code, e.g.:\n\n> - **Timeout:** **inactivity**-based, not a wall-clock cap — a job may run as lo\n\nURL https://api.github.com/repos/NousResearch/hermes-agent/pulls/11772\ntitle: fix(agent): heartbeat during sparse streaming to avoid cron/gateway inactivity kills\n## Summary\r\n\r\nLong-running provider streams can go many seconds without delivering a chunk (server-side buffering, slow prefill, sparse SSE). Cron (`HERMES_CRON_TIMEOUT`) and the gateway (`HERMES_AGENT_TIMEOUT`) poll `AIAgent.get_activity_summary()` and may treat the absence of chunk-driven activity as idle, interrupting runs that are still legitimately blocked on the stream iterator.\r\n\r\nThis PR adds a **background stream heartbeat**: while a streaming iterator is active, a daemon thread periodically calls `_touch_activity()` so inactivity monitors observe continued progress even when no chunks arrive.\r\n\r\n## Changes\r\n\r\n- **`AIAgent`**: Optional `stream_heartbeat_interval` (seconds). If omitted, reads `HERMES_STREAM_HEARTBEAT_INTERVAL` (default **30**); **`0` disables** the heartbeat.\r\n- **`_stream_heartbeat_scope()`**: Context manager wrapping blocking stream consumption.\r\n- **Wrapped paths**: OpenAI chat completions streaming, Anthropic `messages.stream`, Codex Responses streaming (including the `responses.create(stream=True)` fallback), and the Bedrock `converse_stream` worker wait loop.\r\n- **`cron/scheduler.py`**: When loading `config.yaml`, if the corresponding env vars are not already set, bridge `cron.idle_timeout` → `HERMES_CRON_TIMEOUT` and `cron.heartbeat_interval` → `HERMES_STREAM_HEARTBEAT_INTERVAL`.\r\n- **`gateway/run.py`**: On startup, bridge `agent.stream_heartbeat_interval` and top-level `cron.*` to those env vars when not already set.\r\n- **`cli-config.yaml.example`**: Document optional keys.\r\n\r\n## Tests\r\n\r\n- **Added**: `tests/run_agent/test_stream_heartbeat.py` (disabled interval + activity refresh under simulated blocking).\r\n- **Regression**: `tests/cron/test_cron_inactivity_timeout.py` (unchanged by this PR; run to ensure cron inactivity polling still behaves).\r\n\r\n## How to verify\r\n\r\n```bash\r\npytest tests/run_agent/test_stream_heartbeat.py tests/cron/test_cron_inactivity_timeout.py -q -o addopts=\n\nURL https://api.github.com/repos/NousResearch/hermes-agent/issues/6839\ntitle: Feature: Lazy Tool Schema Loading — Two-Pass Tool Injection to Reduce Token Overhead\n## Problem\n\nEvery API call injects full tool schemas for ALL enabled toolsets. With 50+ tools across terminal, file, web, browser, delegate, vision, memory, and more, this consumes ~3,500-5,000 tokens per call — regardless of whether the conversation needs those tools.\n\nOn local models, tool-formatted prompts are 10x slower to process than plain text (benchmarked: 1,230 tok/s vs 134 tok/s with 8 tools — see #5544). Even on cloud providers, this is wasted tokens at scale.\n\nFor simple conversational turns (\"hi\", \"what model are you using?\"), the model doesn't need to know about browser_click or web_crawl or delegate_task. But it gets all of them anyway.\n\n## Proposed Solution\n\nTwo-pass lazy tool loading:\n\n**Pass 1 (every call):** Send tool names + one-line descriptions only (~300-500 tokens vs ~4,000)\n**Pass 2 (on demand):** When the model picks a tool, send the full schema in a follow-up call\n\n### Flow:\n1. User sends message\n2. Hermes sends system prompt + conversation history + ABBREVIATED tool list (name + 1-line description)\n3. Model either:\n a. Responds normally (no tools needed) ...[truncated 1472 chars] ## assistant · 2026-07-03 05:51:22 ### tool calls - `search_files` `{"context":6,"limit":20,"output_mode":"content","path":"/usr/local/lib/hermes-agent/gateway/run.py","pattern":"HERMES_AGENT_TIMEOUT|gateway_timeout|_float_env|inactivity","target":"content"}` - `search_files` `{"context":6,"limit":30,"output_mode":"content","path":"/usr/local/lib/hermes-agent/hermes_cli","pattern":"gateway_timeout|HERMES_AGENT_TIMEOUT|timeout_warning","target":"content"}` - `search_files` `{"context":5,"limit":30,"output_mode":"content","path":"/usr/local/lib/hermes-agent","pattern":"tool_slimmer|ToolSlimmer|slimmer|hydrate_tools","target":"content"}` ## tool · 2026-07-03 05:51:22 {"total_count": 207, "matches_format": "path-grouped: each file path on its own line, followed by indented '<line>: <content>' rows for matches in that file", "matches_text": "/usr/local/lib/hermes-agent/gateway/run.py\n 550: # * resume_pending (gateway restart/shutdown watchdog marked the session)\n 551: # * tool-tail (last persisted message is a tool result the agent\n 552: # never got to reply to)\n 553: # In both cases \"when did we last do anything on this transcript\" is the\n 554: # correct freshness question, so one signal replaces two divergent ones.\n 555: #\n 556: # Default window: 1 hour. This comfortably covers ``agent.gateway_timeout``\n 557: # (30 min default) plus runtime slack — a legitimate long-running turn that\n 558: # gets interrupted near its timeout boundary and is resumed shortly after\n 559: # is still classified fresh. Override via\n 560: # ``config.yaml`` ``agent.gateway_auto_continue_freshness``.\n 561: _AUTO_CONTINUE_FRESHNESS_SECS_DEFAULT = 60 * 60\n 562: \n 597: \n 598: def _auto_continue_freshness_window() -> float:\n 599: \"\"\"Return the configured auto-continue freshness window in seconds.\n 600: \n 601: Reads ``HERMES_AUTO_CONTINUE_FRESHNESS`` (bridged from\n 602: ``config.yaml`` ``agent.gateway_auto_continue_freshness`` at gateway\n 603: startup, same pattern as ``HERMES_AGENT_TIMEOUT``). Falls back to the", "truncated": true} [Hint: Results truncated. Use offset=20 to see more, or narrow with a more specific pattern or file_glob.] ## tool · 2026-07-03 05:51:22 {"total_count": 65, "matches_format": "path-grouped: each file path on its own line, followed by indented '<line>: <content>' rows for matches in that file", "matches_text": "/usr/local/lib/hermes-agent/hermes_cli/dump.py\n 198: \n 199: overrides = {}\n 200: \n 201: # Sections with interesting user-facing overrides\n 202: interesting_paths = [\n 203: (\"agent\", \"max_turns\"),\n 204: (\"agent\", \"gateway_timeout\"),\n 205: (\"agent\", \"tool_use_enforcement\"),\n 206: (\"terminal\", \"backend\"),\n 207: (\"terminal\", \"docker_image\"),\n 208: (\"terminal\", \"persistent_shell\"),\n 209: (\"browser\", \"allow_private_urls\"),\n 210: (\"compression\", \"enabled\"),\n/usr/local/lib/hermes-agent/hermes_cli/tips.py\n 437: 'hermes fallback manages the fallback_model chain interactively — no hand-editing config.yaml.',\n 438: 'hermes pairing rotates the DM pairing token — the first messager after rotation claims access to the bot.',\n 439: 'hermes setup walks first-time users through provider, keys, and platform wiring in one interactive flow.',\n 440: 'hermes status --deep runs the full health sweep across every component; plain hermes status is the quick view.',\n 441: \n 442: # --- Agent Behavior Env Vars ---\n 443: 'HERMES_AGENT_TIMEOUT=0 disables the gateway inactivity kill for a running agent — use for long research runs.',\n 444: 'HERMES_ENABLE_PROJECT_PLUGINS=1 auto-loads repo-local plugins from ./.hermes/plugins/ — trust-gated by design.',\n 445: \"HERMES_DISABLE_FILE_STATE_GUARD=1 turns off the 'file changed since you read it' guard on patch and write_file.\",\n 446: 'HERMES_ALLOW_PRIVATE_URLS=true lets web tools hit localhost and private networks — off by default in gateway mode.',\n 447: 'HERMES_OPTIONAL_SKILLS=name1,name2 auto-installs extra optional-catalog skills on first run per profile.',\n 448: 'HERMES_BUNDLED_SKILLS points at a custom bundled-skill tree — used by Homebrew and Nix packaging.',\n 449: 'HERMES_DUMP_REQUEST_STDOUT=1 dumps every API request payload to stdout instead of log files.',\n/usr/local/lib/hermes-agent/hermes_cli/config.py\n 892: \"agent\": {\n 893: \"max_turns\": 90,\n 894: # Inactivity timeout for gateway agent execution (seconds).\n 895: # The agent can run indefinitely as long as it's actively calling", "truncated": true} [Hint: Results truncated. Use offset=30 to see more, or narrow with a more specific pattern or file_glob.] ## tool · 2026-07-03 05:51:22 {"total_count": 11, "matches_format": "path-grouped: each file path on its own line, followed by indented '<line>: <content>' rows for matches in that file", "matches_text": "/usr/local/lib/hermes-agent/ui-tui/src/__tests__/statusRule.test.ts\n 104: })\n 105: })\n 106: \n 107: describe('busyIndicatorWidth', () => {\n 108: it('reserves a bare spinner for the verb-less unicode style', () => {\n 109: // unicode is a 1-col braille spinner with no verb; far slimmer than the\n 110: // kaomoji face which carries a wide glyph + rotating verb.\n 111: expect(busyIndicatorWidth('unicode', false)).toBeLessThan(busyIndicatorWidth('kaomoji', false))\n 112: expect(busyIndicatorWidth('unicode', false)).toBe(1)\n 113: })\n 114: "} ## assistant · 2026-07-03 05:51:22 ### tool calls - `read_file` `{"path":"/usr/local/lib/hermes-agent/hermes_cli/config.py","offset":888,"limit":30}` - `search_files` `{"context":4,"limit":40,"output_mode":"content","path":"/usr/local/lib/hermes-agent/gateway/run.py","pattern":"gateway_timeout_warning|gateway_timeout|HERMES_AGENT_TIMEOUT_WARNING|_float_env\\(\"HERMES_AGENT_TIMEOUT","target":"content"}` - `terminal` `{"command":"python3 - <<'PY'\nimport yaml, pathlib, json\np=pathlib.Path('/root/.hermes/config.yaml')\nprint('config_exists',p.exists())\nif p.exists():\n data=yaml.safe_load(p.read_text()) or {}\n print(json.dumps({'agent':data.get('agent'), 'prompt_caching':data.get('prompt_caching'), 'disabled_toolsets':data.get('disabled_toolsets')}, ensure_ascii=False, indent=2))\nPY","timeout":20}` ## tool · 2026-07-03 05:51:22 {"content": "888| \"toolsets\": [\"hermes-cli\"],\n889| # Global active chat session cap across CLI, TUI/dashboard, and messaging.\n890| # None/0 = unbounded.\n891| \"max_concurrent_sessions\": None,\n892| \"agent\": {\n893| \"max_turns\": 90,\n894| # Inactivity timeout for gateway agent execution (seconds).\n895| # The agent can run indefinitely as long as it's actively calling\n896| # tools or receiving API responses. Only fires when the agent has\n897| # been completely idle for this duration. 0 = unlimited.\n898| \"gateway_timeout\": 1800,\n899| # Graceful drain timeout for gateway stop/restart (seconds).\n900| # The gateway stops accepting new work, waits for running agents\n901| # to finish, then interrupts any remaining runs after the timeout.\n902| # 0 = no drain, interrupt immediately.\n903| #\n904| # 180s is calibrated for realistic in-flight agent turns: a typical\n905| # coding conversation mid-reasoning runs 60–150s per call, so a 60s\n906| # budget routinely interrupted legitimate work on /restart. Raise\n907| # further in config.yaml if you run very-long-reasoning models.\n908| \"restart_drain_timeout\": 180,\n909| # Max app-level retry attempts for API errors (connection drops,\n910| # provider timeouts, 5xx, etc.) before the agent surfaces the\n911| # failure. The OpenAI SDK already does its own low-level retries\n912| # (max_retries=2 default) for transient network errors; this is\n913| # the Hermes-level retry loop that wraps the whole call. Lower\n914| # this to 1 if you use fallback providers and want fast failover\n915| # on flaky primaries; raise it if you prefer to tolerate longer\n916| # provider hiccups on a single provider.\n917| \"api_max_retries\": 3,\n918|", "total_lines": 7136, "file_size": 322949, "truncated": true, "hint": "Use offset=918 to continue reading (showing 888-917 of 7136 lines)", "is_binary": false, "is_image": false} ## tool · 2026-07-03 05:51:22 {"total_count": 53, "matches_format": "path-grouped: each file path on its own line, followed by indented '<line>: <content>' rows for matches in that file", "matches_text": "/usr/local/lib/hermes-agent/gateway/run.py\n 552: # never got to reply to)\n 553: # In both cases \"when did we last do anything on this transcript\" is the\n 554: # correct freshness question, so one signal replaces two divergent ones.\n 555: #\n 556: # Default window: 1 hour. This comfortably covers ``agent.gateway_timeout``\n 557: # (30 min default) plus runtime slack — a legitimate long-running turn that\n 558: # gets interrupted near its timeout boundary and is resumed shortly after\n 559: # is still classified fresh. Override via\n 560: # ``config.yaml`` ``agent.gateway_auto_continue_freshness``.\n 1582: _agent_cfg = _cfg.get(\"agent\", {})\n 1583: if _agent_cfg and isinstance(_agent_cfg, dict):\n 1584: if \"max_turns\" in _agent_cfg:\n 1585: os.environ[\"HERMES_MAX_ITERATIONS\"] = str(_agent_cfg[\"max_turns\"])\n 1586: if \"gateway_timeout\" in _agent_cfg:\n 1587: os.environ[\"HERMES_AGENT_TIMEOUT\"] = str(_agent_cfg[\"gateway_timeout\"])\n 1588: if \"gateway_timeout_warning\" in _agent_cfg:\n 1589: os.environ[\"HERMES_AGENT_TIMEOUT_WARNING\"] = str(_agent_cfg[\"gateway_timeout_warning\"])\n 1590: if \"gateway_notify_interval\" in _agent_cfg:\n 1591: os.environ[\"HERMES_AGENT_NOTIFY_INTERVAL\"] = str(_agent_cfg[\"gateway_notify_interval\"])\n 1592: if \"restart_drain_timeout\" in _agent_cfg:\n 1593: os.environ[\"HERMES_RESTART_DRAIN_TIMEOUT\"] = str(_agent_cfg[\"restart_drain_timeout\"])\n 7573: # With inactivity-based timeout, active tasks can run for hours, so\n 7574: # wall-clock age alone isn't sufficient. Evict only when the agent\n 7575: # has been *idle* beyond the inactivity threshold (or when the agent\n 7576: # object has no activity tracker and wall-clock age is extreme).\n 7577: _raw_stale_timeout = _float_env(\"HERMES_AGENT_TIMEOUT\", 1800)\n 7578: _stale_ts = self._running_agents_ts.get(_quick_key, 0)\n 7579: if _quick_key in self._running_agents and _stale_ts:\n 7580: _stale_age = time.time() - _stale_ts\n 7581: _stale_agent = self._running_agents.get(_quick_key)\n 16542: # hours if it's actively calling tools / receiving stream tokens,\n 16543: # but a hung API call or stuck tool with no activity for the\n 16544: # configured duration is caught and killed. (#4815)\n 16545: #\n 16546: # Config: agent.gateway_timeout in config.yaml, or\n 16547: # HERMES_AGENT_TIMEOUT env var (env var takes precedence).\n 16548: # Default 1800s (30 min inactivity). 0 = unlimited.\n 16549: _agent_timeout_raw = _float_env(\"HERMES_AGENT_TIMEOUT\", 1800)\n 16550: _agent_timeout = _agent_timeout_raw if _agent_timeout_raw > 0 else None\n 16551: _agent_warning_raw = _float_env(\"HERMES_AGENT_TIMEOUT_WARNING\", 900)", "truncated": true} [Hint: Results truncated. Use offset=40 to see more, or narrow with a more specific pattern or file_glob.] ## tool · 2026-07-03 05:51:22 {"output": "config_exists True\n{\n \"agent\": {\n \"max_turns\": 90,\n \"gateway_timeout\": 1800,\n \"restart_drain_timeout\": 180,\n \"api_max_retries\": 0,\n \"service_tier\": \"\",\n \"tool_use_enforcement\": \"auto\",\n \"task_completion_guidance\": true,\n \"parallel_tool_call_guidance\": true,\n \"environment_probe\": true,\n \"environment_hint\": \"\",\n \"coding_context\": \"auto\",\n \"gateway_timeout_warning\": 900,\n \"clarify_timeout\": 600,\n \"gateway_notify_interval\": 180,\n \"gateway_auto_continue_freshness\": 3600,\n \"image_input_mode\": \"auto\",\n \"disabled_toolsets\": [\n \"browser\",\n \"computer_use\",\n \"kanban\",\n \"vision\"\n ],\n \"reasoning_effort\": \"medium\"\n },\n \"prompt_caching\": {\n \"cache_ttl\": \"1h\",\n \"enabled\": true\n },\n \"disabled_toolsets\": null\n}", "exit_code": 0, "error": null} ## assistant · 2026-07-03 05:51:22 Boss,查过了。这不是新功能,更像一个**网关防挂死保护的开关**。 结论:**我不建议现在全局启用 `HERMES_AGENT_TIMEOUT=0`。** 当前你的网关实际是: ```text HERMES_AGENT_TIMEOUT=1800 ``` 也就是 **30 分钟“无活动”才杀掉运行中的 agent**。注意它不是 30 分钟总时长限制:只要 agent 还在调用工具、收流式响应、推进任务,就可以跑很久;只有卡死、API 挂住、工具无输出时才会触发。 社区/代码里对应说明: - PR #8298:给 gateway `/background` 任务加 inactivity timeout,避免后台任务静默挂死。 - 默认 1800 秒,900 秒会先 warning。 - `HERMES_AGENT_TIMEOUT=0` 表示**无限等待**,适合特别长的研究任务,但风险是:如果模型/API/工具真的卡死,网关不会自动清理这个运行中的 agent。 - 另有 PR #11772 加了 stream heartbeat,避免长流式响应被误判为无活动。 我的建议: 1. **日常保持现在的 1800 秒。** 这是稳妥值。 2. 真要跑“长研究任务”,优先用: - `/background` - cron - `terminal(background=True, notify_on_complete=True)` 而不是把全局 gateway timeout 关掉。 3. 如果以后确实经常被误杀,可以把 `agent.gateway_timeout` 提到 3600 或 7200,而不是直接设 0。 Tool Slimmer 那句是当前环境提示:工具 schema 可能被瘦身隐藏,缺工具时要显式请求完整工具集。它是省 token 的机制,不是你需要手动启用的业务功能。 当前状态:**无需启用。保持现状即可,Boss。** ## session_meta · 2026-07-03 05:51:22 ## user · 2026-07-03 05:54:35 background目前是可以正常启动的对吗 ## assistant · 2026-07-03 05:55:10 ### tool calls - `terminal` `{"command":"set -e\nprintf 'gateway status:\\n'\nhermes gateway status 2>&1 || true\nprintf '\\nrunning processes:\\n'\npgrep -af 'hermes.*gateway|gateway.*run' || true\nprintf '\\nrecent background/gateway log refs:\\n'\nfor f in /root/.hermes/logs/gateway.log /root/.hermes/logs/agent.log; do\n [ -f \"$f\" ] || continue\n echo \"--- $f\"\n grep -iE '/background|background task|HERMES_AGENT_TIMEOUT|inactivity timeout|CancelledError|timeout' \"$f\" 2>/dev/null | tail -40 || true\ndone","timeout":60}` - `terminal` `{"command":"python3 - <<'PY'\nimport yaml, pathlib, os, json\np=pathlib.Path('/root/.hermes/config.yaml')\ndata=yaml.safe_load(p.read_text()) if p.exists() else {}\nprint(json.dumps({\n 'config_agent_gateway_timeout': (data or {}).get('agent',{}).get('gateway_timeout'),\n 'config_agent_gateway_timeout_warning': (data or {}).get('agent',{}).get('gateway_timeout_warning'),\n 'config_agent_max_turns': (data or {}).get('agent',{}).get('max_turns'),\n 'env_HERMES_AGENT_TIMEOUT_current_shell': os.environ.get('HERMES_AGENT_TIMEOUT'),\n}, ensure_ascii=False, indent=2))\nPY","timeout":20}` ## tool · 2026-07-03 05:55:10 {"output": "gateway status:\n● hermes-gateway.service - Hermes Agent Gateway - Messaging Platform Integration\n Loaded: loaded (/root/.config/systemd/user/hermes-gateway.service; enabled; preset: enabled)\n Active: active (running) since Mon 2026-06-29 12:21:10 CST; 3 days ago\n Main PID: 2425470 (hermes)\n Tasks: 66 (limit: 7047)\n Memory: 3.1G\n CPU: 1h 9min 42.348s\n CGroup: /user.slice/user-0.slice/user@0.service/app.slice/hermes-gateway.service\n ├─ 192012 /usr/bin/bash -c \"source /tmp/hermes-snap-66b0228d8388.sh >/dev/null 2>&1 || true\\nbuiltin cd -- /root || exit 126\\neval 'set -e\\nprintf '\\\\''gateway status:\\\\n'\\\\''\\nhermes gateway status 2>&1 || true\\nprintf '\\\\''\\\\nrunning processes:\\\\n'\\\\''\\npgrep -af '\\\\''hermes.*gateway|gateway.*run'\\\\'' || true\\nprintf '\\\\''\\\\nrecent background/gateway log refs:\\\\n'\\\\''\\nfor f in /root/.hermes/logs/gateway.log /root/.hermes/logs/agent.log; do\\n [ -f \\\"\\$f\\\" ] || continue\\n echo \\\"--- \\$f\\\"\\n grep -iE '\\\\''/background|background task|HERMES_AGENT_TIMEOUT|inactivity timeout|CancelledError|timeout'\\\\'' \\\"\\$f\\\" 2>/dev/null | tail -40 || true\\ndone'\\n__hermes_ec=\\$?\\nexport -p > /tmp/hermes-snap-66b0228d8388.sh 2>/dev/null || true\\npwd -P > /tmp/hermes-cwd-66b0228d8388.txt 2>/dev/null || true\\nprintf '\\\\n__HERMES_CWD_66b0228d8388__%s__HERMES_CWD_66b0228d8388__\\\\n' \\\"\\$(pwd -P)\\\"\\nexit \\$__hermes_ec\"\n ├─ 192014 /usr/local/lib/hermes-agent/venv/bin/python3 /root/.local/bin/hermes gateway status\n ├─ 192019 systemctl --user status hermes-gateway --no-pager\n ├─2425470 /usr/local/lib/hermes-agent/venv/bin/python -m hermes_cli.main gateway run\n ├─2425476 /root/.local/bin/uv tool uvx amap-mcp-server\n ├─2425478 /root/.local/bin/uv tool uvx --from jobson-xhs-mcp xhs-mcp\n ├─2425519 /root/.cache/uv/archive-v0/LPWA6EWrRIreH1NY/bin/python /root/.cache/uv/archive-v0/LPWA6EWrRIreH1NY/bin/amap-mcp-server\n ├─2425522 /root/.cache/uv/archive-v0/EatXorTLuHpK-0iT/bin/python /root/.cache/uv/archive-v0/EatXorTLuHpK-0iT/bin/xhs-mcp\n └─3435192 python3 /root/.hermes/temp-oauth-proxy/proxy.py\n\nJul 03 05:43:31 VM-0562c027-d4bb-4aa7-97ab-d6b12d3013d1 python[2425470]: client_session: <aiohttp.client.ClientSession object at 0x7f0c65770490>\nJul 03 05:43:31 VM-0562c027-d4bb-4aa7-97ab-d6b12d3013d1 python[2425470]: ERROR asyncio: Unclosed connector\nJul 03 05:43:31 VM-0562c027-d4bb-4aa7-97ab-d6b12d3013d1 python[2425470]: connections: ['deque([(<aiohttp.client_proto.ResponseHandler object at 0x7f0c0c7202f0>, 2308090.112860622)])']\nJul 03 05:43:31 VM-0562c027-d4bb-4aa7-97ab-d6b12d3013d1 python[2425470]: connector: <aiohttp.connector.TCPConnector object at 0x7f0c2eeae850>\nJul 03 05:50:25 VM-0562c027-d4bb-4aa7-97ab-d6b12d3013d1 python[2425470]: WARNING agent.tool_executor: Tool terminal returned error (30.11s): {\"output\": \"service env refs:\\n\\nprocess env refs:\\nHERMES_AGENT_TIMEOUT=1800\\n\\nconfig/env refs:\\n\\n[Command timed out after 30s]\", \"exit_code\": 124, \"error\": null}\nJul 03 05:51:22 VM-0562c027-d4bb-4aa7-97ab-d6b12d3013d1 python[2425470]: ERROR asyncio: Unclosed client session\nJul 03 05:51:22 VM-0562c027-d4bb-4aa7-97ab-d6b12d3013d1 python[2425470]: client_session: <aiohttp.client.ClientSession object at 0x7f0c441b9e50>\nJul 03 05:51:22 VM-0562c027-d4bb-4aa7-97ab-d6b12d3013d1 python[2425470]: ERROR asyncio: Unclosed connector\nJul 03 05:51:22 VM-0562c027-d4bb-4aa7-97ab-d6b12d3013d1 python[2425470]: connections: ['deque([(<aiohttp.client_proto.ResponseHandler object at 0x7f0bedc78bb0>, 2308561.350712005)])']\nJul 03 05:51:22 VM-0562c027-d4bb-4aa7-97ab-d6b12d3013d1 python[2425470]: connector: <aiohttp.connector.TCPConnector object at 0x7f0c0c34ce90>\n✓ User gateway service is running\n✓ Systemd linger is enabled (service survives logout)\n\nrunning processes:\n192012 /usr/bin/bash -c source /tmp/hermes-snap-66b0228d8388.sh >/dev/null 2>&1 || true builtin cd -- /root || exit 126 eval 'set -e printf '\\''gateway status:\\n'\\'' hermes gateway status 2>&1 || true printf '\\''\\nrunning processes:\\n'\\'' pgrep -af '\\''hermes.*gateway|gateway.*run'\\'' || true printf '\\''\\nrecent background/gateway log refs:\\n'\\'' for f in /root/.hermes/logs/gateway.log /root/.hermes/logs/agent.log; do [ -f \"$f\" ] || continue echo \"--- $f\" grep -iE '\\''/background|background task|HERMES_AGENT_TIMEOUT|inactivity timeout|CancelledError|timeout'\\'' \"$f\" 2>/dev/null | tail -40 || true done' __hermes_ec=$? export -p > /tmp/hermes-snap-66b0228d8388.sh 2>/dev/null || true pwd -P > /tmp/hermes-cwd-66b0228d8388.txt 2>/dev/null || true printf '\\n__HERMES_CWD_66b0228d8388__%s__HERMES_CWD_66b0228d8388__\\n' \"$(pwd -P)\" exit $__hermes_ec\n2425470 /usr/local/lib/hermes-agent/venv/bin/python -m hermes_cli.main gateway run\n\nrecent background/gateway log refs:\n--- /root/.hermes/logs/gateway.log\n2026-06-06 19:52:09,944 WARNING gateway.platforms.weixin: [Weixin] send chunk failed to=o9cq8022 attempt=2/5, retrying in 2.00s: iLink sendmessage error: ret=None errcode=-14 errmsg=session timeout\n2026-06-06 19:52:12,163 WARNING gateway.platforms.weixin: [Weixin] send chunk failed to=o9cq8022 attempt=3/5, retrying in 3.00s: iLink sendmessage error: ret=None errcode=-14 errmsg=session timeout\n2026-06-06 19:52:15,371 WARNING gateway.platforms.weixin: [Weixin] send chunk failed to=o9cq8022 attempt=4/5, retrying in 4.00s: iLink sendmessage error: ret=None errcode=-14 errmsg=session timeout\n2026-06-06 19:52:19,586 ERROR gateway.platforms.weixin: [Weixin] send failed to=o9cq8022: iLink sendmessage error: ret=None errcode=-14 errmsg=session timeout\n2026-06-09 19:42:00,418 INFO gateway.run: inbound message: platform=weixin user=o9cq80224vlqy98r_Ts2t7UZFQ1o@im.wechat chat=o9cq80224vlqy98r_Ts2t7UZFQ1o@im.wechat msg='CND那个是timeout的,你看看咋回事'\n2026-07-03 05:49:04,582 INFO gateway.run: inbound message: platform=weixin user=o9cq80224vlqy98r_Ts2t7UZFQ1o@im.wechat chat=o9cq80224vlqy98r_Ts2t7UZFQ1o@im.wechat msg=' 提示:HERMES_AGENT_TIMEOUT=0 disables the gateway inactivity kill for a running ag' reply_to_id=None reply_to_text=''\n--- /root/.hermes/logs/agent.log\n2026-07-03 05:49:04,582 INFO gateway.run: inbound message: platform=weixin user=o9cq80224vlqy98r_Ts2t7UZFQ1o@im.wechat chat=o9cq80224vlqy98r_Ts2t7UZFQ1o@im.wechat msg=' 提示:HERMES_AGENT_TIMEOUT=0 disables the gateway inactivity kill for a running ag' reply_to_id=None reply_to_text=''\n2026-07-03 05:49:04,975 INFO [20260703_054723_8a6e2f57] agent.turn_context: conversation turn: session=20260703_054723_8a6e2f57 model=gpt-5.5 provider=custom platform=weixin history=0 msg=' 提示:HERMES_AGENT_TIMEOUT=0 disables the gateway inactivity kill for a running ag...'\n2026-07-03 05:50:25,431 WARNING [20260703_054723_8a6e2f57] agent.tool_executor: Tool terminal returned error (30.11s): {\"output\": \"service env refs:\\n\\nprocess env refs:\\nHERMES_AGENT_TIMEOUT=1800\\n\\nconfig/env refs:\\n\\n[Command timed out after 30s]\", \"exit_code\": 124, \"error\": null}", "exit_code": 0, "error": null} ## tool · 2026-07-03 05:55:10 {"output": "{\n \"config_agent_gateway_timeout\": 1800,\n \"config_agent_gateway_timeout_warning\": 900,\n \"config_agent_max_turns\": 90,\n \"env_HERMES_AGENT_TIMEOUT_current_shell\": \"1800\"\n}", "exit_code": 0, "error": null} ## assistant · 2026-07-03 05:55:10 是的,Boss,**目前 background 相关能力可以正常启动**。 我刚核验了 live 状态: - `hermes-gateway.service`:**active running** - gateway 主进程:`python -m hermes_cli.main gateway run` - systemd linger:**enabled**,SSH 断开后服务不会随登录会话退出 - 当前 `HERMES_AGENT_TIMEOUT=1800` - config 中 `agent.gateway_timeout=1800`,warning 是 `900` 所以结论是: **/background 可以由 gateway 承载运行;当前不是 timeout=0,而是 30 分钟无活动保护。** 补充一句:这个 30 分钟是“无活动”超时,不是总运行时长。后台任务只要还在收模型流、跑工具或有 activity heartbeat,就不会因为跑得久被杀。真正卡死 30 分钟才会被清理。 Tool Slimmer 提示我收到了:如果后面任务缺工具,我会先请求完整工具集,不会编替代工具。
保存
raw