{"repo":"NousResearch/hermes-agent","public_id":"3765","display_label":"Approval timeout hardcoded 60s, make configurable","summary":"Multiple bug reports and fixes addressing that ACP and CLI approval callbacks ignore the `approvals.timeout` config and use a hardcoded 60-second timeout. Related work adds the ability to configure the dangerous-command approval timeout.","subsystem":"acp","counts":{"members":12,"open_issues":2,"open_issues_distinct":2,"open_prs":4,"closed_issues":1,"merged_prs":1,"attempt_prs":8},"landed_fix":{"status":"merged_to_main","basis":"structural","pr":3886},"resolution":{"state":"partially_resolved","by":"pr-3886"},"freshness":{"state":"stale","last_verified_at":"2026-03-30T07:02:02+00:00","is_stale":true},"grouping":{"backbone":"STRUCTURAL_PARTIAL","link_density":0.15272727272727274},"fix_edges":[{"issue_number":3765,"pr_number":3886,"tier":"verified","basis":"closing_ref","confidence":0.95,"recoverable_from_timeline":true}],"members":[{"kind":"issue","number":3765,"title":"feat: make dangerous command approval timeout configurable","url":null,"state":"closed"},{"kind":"issue","number":63302,"title":"[Bug]: ACP edit approval ignores `approvals.timeout` — hardcoded 60s, times out as a silent denial","url":null,"state":"open"},{"kind":"issue","number":63538,"title":"[Bug]: ACP dangerous-command approval callback ignores `approvals.timeout` — hardcoded 60s (sibling of #63302)","url":null,"state":"open"},{"kind":"pr","number":3766,"title":"✨(approvals): make dangerous command approval timeout configurable","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#3886","number":3886}],"category":"merged_via","implication":"superseded"},"merged":false},{"kind":"pr","number":3886,"title":"feat(approvals): make dangerous command approval timeout configurable","url":null,"state":"merged","close_reason":{"category":"merged","implication":"superseded"},"merged":true,"is_fix_pr":true,"fix_tier":"verified","files_changed":["hermes_cli/callbacks.py","hermes_cli/config.py","tools/approval.py"],"fix_hunks":[{"file":"hermes_cli/callbacks.py","hunks":["@@ -241,7 +241,8 @@ def approval_callback(cli, command: str, description: str) -> str:\n         lock = cli._approval_lock\n \n     with lock:\n-        timeout = 60\n+        from cli import CLI_CONFIG\n+        timeout = CLI_CONFIG.get(\"approvals\", {}).get(\"timeout\", 60)\n         response_queue = queue.Queue()\n         choices = [\"once\", \"session\", \"always\", \"deny\"]\n         if len(command) > 70:"]},{"file":"hermes_cli/config.py","hunks":["@@ -407,6 +407,7 @@ def ensure_hermes_home():\n     #   off    — skip all approval prompts (equivalent to --yolo)\n     \"approvals\": {\n         \"mode\": \"manual\",\n+        \"timeout\": 60,\n     },\n \n     # Permanently allowed dangerous command patterns (added via \"always\" approval)"]},{"file":"tools/approval.py","hunks":["@@ -241,7 +241,7 @@ def save_permanent_allowlist(patterns: set):\n # =========================================================================\n \n def prompt_dangerous_approval(command: str, description: str,\n-                              timeout_seconds: int = 60,\n+                              timeout_seconds: int | None = None,\n                               allow_permanent: bool = True,\n                               approval_callback=None) -> str:\n     \"\"\"Prompt the user to approve a dangerous command (CLI only).","@@ -256,6 +256,9 @@ def prompt_dangerous_approval(command: str, description: str,\n \n     Returns: 'once', 'session', 'always', or 'deny'\n     \"\"\"\n+    if timeout_seconds is None:\n+        timeout_seconds = _get_approval_timeout()\n+\n     if approval_callback is not None:\n         try:\n             return approval_callback(command, description,","@@ -336,15 +339,28 @@ def _normalize_approval_mode(mode) -> str:\n     return \"manual\"\n \n \n-def _get_approval_mode() -> str:\n-    \"\"\"Read the approval mode from config. Returns 'manual', 'smart', or 'off'.\"\"\"\n+def _get_approval_config() -> dict:\n+    \"\"\"Read the approvals config block. Returns a dict with 'mode', 'timeout', etc.\"\"\"\n     try:\n         from hermes_cli.config import load_config\n         config = load_config()\n-        mode = config.get(\"approvals\", {}).get(\"mode\", \"manual\")\n-        return _normalize_approval_mode(mode)\n+        return config.get(\"approvals\", {}) or {}\n     except Exception:\n-        return \"manual\"\n+        return {}\n+\n+\n+def _get_approval_mode() -> str:\n+    \"\"\"Read the approval mode from config. Returns 'manual', 'smart', or 'off'.\"\"\"\n+    mode = _get_approval_config().get(\"mode\", \"manual\")\n+    return _normalize_approval_mode(mode)\n+\n+\n+def _get_approval_timeout() -> int:\n+    \"\"\"Read the approval timeout from config. Defaults to 60 seconds.\"\"\"\n+    try:\n+        return int(_get_approval_config().get(\"timeout\", 60))\n+    except (ValueError, TypeError):\n+        return 60\n \n \n def _smart_approve(command: str, description: str) -> str:\n"]}]},{"kind":"pr","number":13088,"title":"fix: honor approvals.timeout across CLI and ACP","url":null,"state":"open","close_reason":{"refs":[{"kind":"commit","value":"8ae65d5c8","number":0}],"category":"implemented_elsewhere","implication":"superseded"},"merged":false},{"kind":"pr","number":63308,"title":"fix(acp): honor edit approval timeout","url":null,"state":"open","close_reason":{"refs":[{"kind":"commit","value":"b0bad3e259c9","number":0},{"kind":"pr","value":"#63308","number":63308},{"kind":"pr","value":"#63484","number":63484},{"kind":"pr","value":"#63689","number":63689},{"kind":"pr","value":"#64030","number":64030},{"kind":"pr","value":"#64336","number":64336},{"kind":"pr","value":"#63302","number":63302},{"kind":"pr","value":"#63575","number":63575},{"kind":"pr","value":"#63538","number":63538},{"kind":"pr","value":"#63308","number":63308},{"kind":"pr","value":"#63575","number":63575},{"kind":"pr","value":"#63308","number":63308},{"kind":"pr","value":"#63302","number":63302},{"kind":"pr","value":"#63484","number":63484},{"kind":"pr","value":"#63575","number":63575},{"kind":"pr","value":"#63538","number":63538},{"kind":"pr","value":"#63689","number":63689},{"kind":"pr","value":"#64030","number":64030},{"kind":"pr","value":"#63308","number":63308},{"kind":"pr","value":"#64336","number":64336},{"kind":"pr","value":"#63308","number":63308},{"kind":"pr","value":"#63484","number":63484},{"kind":"pr","value":"#64030","number":64030},{"kind":"pr","value":"#63308","number":63308},{"kind":"pr","value":"#63689","number":63689},{"kind":"pr","value":"#64336","number":64336},{"kind":"pr","value":"#63575","number":63575},{"kind":"pr","value":"#63538","number":63538},{"kind":"pr","value":"#63308","number":63308},{"kind":"pr","value":"#63575","number":63575},{"kind":"pr","value":"#63484","number":63484},{"kind":"pr","value":"#63689","number":63689},{"kind":"pr","value":"#64030","number":64030},{"kind":"pr","value":"#64336","number":64336},{"kind":"pr","value":"#63308","number":63308},{"kind":"pr","value":"#374151","number":374151},{"kind":"pr","value":"#63302","number":63302},{"kind":"pr","value":"#63308","number":63308},{"kind":"pr","value":"#63689","number":63689},{"kind":"pr","value":"#64336","number":64336}],"category":"partial_fix","implication":"needs_llm"},"merged":false},{"kind":"pr","number":63484,"title":"fix(acp): honor approvals.timeout config in ACP edit approval timeout","url":null,"state":"closed","close_reason":{"category":"superseded_by_pr","implication":"superseded"},"merged":false,"is_fix_pr":true,"fix_tier":"suggested","fix_detail_withheld":true},{"kind":"pr","number":63575,"title":"fix(acp): use approvals.timeout for both command and edit approval callbacks","url":null,"state":"open","close_reason":{"category":"uncategorized","implication":"needs_llm"},"merged":false},{"kind":"pr","number":63689,"title":"fix(acp): honor approvals.timeout config for edit approval timeout","url":null,"state":"closed","close_reason":{"category":"superseded_by_pr","implication":"superseded"},"merged":false},{"kind":"pr","number":64030,"title":"fix(acp): honor approvals.timeout config in ACP edit approval prompt (#63302)","url":null,"state":"closed","close_reason":{"refs":[{"kind":"pr","value":"#63308","number":63308},{"kind":"pr","value":"#63308","number":63308},{"kind":"pr","value":"#63302","number":63302},{"kind":"pr","value":"#64030","number":64030},{"kind":"pr","value":"#63308","number":63308}],"category":"duplicate_of","implication":"duplicate"},"merged":false},{"kind":"pr","number":64336,"title":"fix: honor approvals.timeout for ACP edit approval prompts (#63302)","url":null,"state":"open","close_reason":{"refs":[{"kind":"commit","value":"b1beab4e03ed","number":0},{"kind":"pr","value":"#63308","number":63308},{"kind":"pr","value":"#63484","number":63484},{"kind":"pr","value":"#63689","number":63689},{"kind":"pr","value":"#64030","number":64030},{"kind":"pr","value":"#64336","number":64336},{"kind":"pr","value":"#63302","number":63302},{"kind":"pr","value":"#63575","number":63575},{"kind":"pr","value":"#63538","number":63538},{"kind":"pr","value":"#63308","number":63308},{"kind":"pr","value":"#63302","number":63302},{"kind":"pr","value":"#63484","number":63484},{"kind":"pr","value":"#63575","number":63575},{"kind":"pr","value":"#63538","number":63538},{"kind":"pr","value":"#63689","number":63689},{"kind":"pr","value":"#64030","number":64030},{"kind":"pr","value":"#63308","number":63308},{"kind":"pr","value":"#64336","number":64336},{"kind":"pr","value":"#63308","number":63308},{"kind":"pr","value":"#63484","number":63484},{"kind":"pr","value":"#64030","number":64030},{"kind":"pr","value":"#63308","number":63308},{"kind":"pr","value":"#63689","number":63689},{"kind":"pr","value":"#64336","number":64336},{"kind":"pr","value":"#63575","number":63575},{"kind":"pr","value":"#63538","number":63538},{"kind":"pr","value":"#63308","number":63308},{"kind":"pr","value":"#63575","number":63575},{"kind":"pr","value":"#63484","number":63484},{"kind":"pr","value":"#63689","number":63689},{"kind":"pr","value":"#64030","number":64030},{"kind":"pr","value":"#64336","number":64336},{"kind":"pr","value":"#63308","number":63308},{"kind":"pr","value":"#374151","number":374151},{"kind":"pr","value":"#63302","number":63302},{"kind":"pr","value":"#63308","number":63308},{"kind":"pr","value":"#63689","number":63689},{"kind":"pr","value":"#64336","number":64336}],"category":"partial_fix","implication":"needs_llm"},"merged":false}],"trust":{"default_tier":"verified","includes_suggested":false}}