# NousResearch/hermes-agent · Approval timeout hardcoded 60s, make configurable

## Open now

- 2 issues, 4 PRs open on GitHub.
- #63302: \[Bug\]: ACP edit approval ignores \`approvals.timeout\` — hardcoded 60s, times out as a silent denial
- #63538: \[Bug\]: ACP dangerous-command approval callback ignores \`approvals.timeout\` — hardcoded 60s (sibling of #63302)
- PR #13088: fix: honor approvals.timeout across CLI and ACP
- PR #63308: fix(acp): honor edit approval timeout
- PR #63575: fix(acp): use approvals.timeout for both command and edit approval callbacks
- PR #64336: fix: honor approvals.timeout for ACP edit approval prompts (#63302)

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.

## TL;DR

- 12 member(s): 2 open issue(s), 1 closed issue(s), 4 open PR(s), 1 merged PR(s), 4 PR(s) closed without merging.
- A fix was merged to main in PR #3886. Verify it addresses your case.
- **Recommended:** read PR #3886 (and any competing PRs below) before filing. Confirm the merged change covers your symptom.

## Maintainer resolution verdict

- Internal-tracker verdict over this cluster's issue members: `partially_resolved` · deciding PR(s): `pr-3886`. This is distinct from the fix status above. Weigh it yourself.

## How this cluster was grouped

- Link evidence (`STRUCTURAL_PARTIAL`): some members are connected by structural evidence (shared changed files, or a closing/superseded reference); the rest are joined by an automated relevance judgement over their text.
- Members outside that structural core may belong to a different underlying problem.
- Pairwise link density `0.15`: the share of member pairs that carry a link, counting a structural link in full and a relevance-only link at 0.36. `1.00` means every pair is linked.

## Open related issues

- #63302: \[Bug\]: ACP edit approval ignores \`approvals.timeout\` — hardcoded 60s, times out as a silent denial
- #63538: \[Bug\]: ACP dangerous-command approval callback ignores \`approvals.timeout\` — hardcoded 60s (sibling of #63302)

## Closed / duplicate issues

- #3765: feat: make dangerous command approval timeout configurable

## Prior & competing PRs

- **#3886: feat(approvals): make dangerous command approval timeout configurable**. merged to main · fix tier `verified`
  - maintainer direction: category `merged`; implication `superseded`
  - files: `hermes_cli/callbacks.py`, `hermes_cli/config.py`, `tools/approval.py`
  - fix hunks:
  - `hermes_cli/callbacks.py`

    ```diff
    @@ -241,7 +241,8 @@ def approval_callback(cli, command: str, description: str) -> str:
             lock = cli._approval_lock
     
         with lock:
    -        timeout = 60
    +        from cli import CLI_CONFIG
    +        timeout = CLI_CONFIG.get("approvals", {}).get("timeout", 60)
             response_queue = queue.Queue()
             choices = ["once", "session", "always", "deny"]
             if len(command) > 70:
    ```
  - `hermes_cli/config.py`

    ```diff
    @@ -407,6 +407,7 @@ def ensure_hermes_home():
         #   off    — skip all approval prompts (equivalent to --yolo)
         "approvals": {
             "mode": "manual",
    +        "timeout": 60,
         },
     
         # Permanently allowed dangerous command patterns (added via "always" approval)
    ```
  - `tools/approval.py`

    ```diff
    @@ -241,7 +241,7 @@ def save_permanent_allowlist(patterns: set):
     # =========================================================================
     
     def prompt_dangerous_approval(command: str, description: str,
    -                              timeout_seconds: int = 60,
    +                              timeout_seconds: int | None = None,
                                   allow_permanent: bool = True,
                                   approval_callback=None) -> str:
         """Prompt the user to approve a dangerous command (CLI only).
    ```

    ```diff
    @@ -256,6 +256,9 @@ def prompt_dangerous_approval(command: str, description: str,
     
         Returns: 'once', 'session', 'always', or 'deny'
         """
    +    if timeout_seconds is None:
    +        timeout_seconds = _get_approval_timeout()
    +
         if approval_callback is not None:
             try:
                 return approval_callback(command, description,
    ```

    ```diff
    @@ -336,15 +339,28 @@ def _normalize_approval_mode(mode) -> str:
         return "manual"
     
     
    -def _get_approval_mode() -> str:
    -    """Read the approval mode from config. Returns 'manual', 'smart', or 'off'."""
    +def _get_approval_config() -> dict:
    +    """Read the approvals config block. Returns a dict with 'mode', 'timeout', etc."""
         try:
             from hermes_cli.config import load_config
             config = load_config()
    -        mode = config.get("approvals", {}).get("mode", "manual")
    -        return _normalize_approval_mode(mode)
    +        return config.get("approvals", {}) or {}
         except Exception:
    -        return "manual"
    +        return {}
    +
    +
    +def _get_approval_mode() -> str:
    +    """Read the approval mode from config. Returns 'manual', 'smart', or 'off'."""
    +    mode = _get_approval_config().get("mode", "manual")
    +    return _normalize_approval_mode(mode)
    +
    +
    +def _get_approval_timeout() -> int:
    +    """Read the approval timeout from config. Defaults to 60 seconds."""
    +    try:
    +        return int(_get_approval_config().get("timeout", 60))
    +    except (ValueError, TypeError):
    +        return 60
     
     
     def _smart_approve(command: str, description: str) -> str:
    ```
- **#63484: fix(acp): honor approvals.timeout config in ACP edit approval timeout**. closed without merge · fix tier `suggested`
  - maintainer direction: category `superseded_by_pr`; implication `superseded`
  - _suggested-tier fix detail withheld; re-request with `?tier=all` to include it._
- **#3766: ✨(approvals): make dangerous command approval timeout configurable**. closed without merge
  - maintainer direction: category `merged_via`; implication `superseded`; refs `#3886`
- **#13088: fix: honor approvals.timeout across CLI and ACP**. open
  - maintainer direction: category `implemented_elsewhere`; implication `superseded`; refs `8ae65d5c8`
  - files: `acp_adapter/permissions.py`, `cli.py`, `tests/acp/test_permissions.py`, `tests/cli/test_cli_approval_ui.py`
- **#63308: fix(acp): honor edit approval timeout**. open
  - maintainer direction: category `partial_fix`; implication `needs_llm`; refs `b0bad3e259c9`, `#63308`, `#63484`, `#63689`, `#64030`, `#64336`, `#63302`, `#63575`, `#63538`, `#63308`, `#63575`, `#63308`, `#63302`, `#63484`, `#63575`, `#63538`, `#63689`, `#64030`, `#63308`, `#64336`, `#63308`, `#63484`, `#64030`, `#63308`, `#63689`, `#64336`, `#63575`, `#63538`, `#63308`, `#63575`, `#63484`, `#63689`, `#64030`, `#64336`, `#63308`, `#374151`, `#63302`, `#63308`, `#63689`, `#64336`
  - files: `acp_adapter/edit_approval.py`, `acp_adapter/server.py`, `tests/acp/test_edit_approval.py`, `tests/acp/test_server.py`
- **#63575: fix(acp): use approvals.timeout for both command and edit approval callbacks**. open
  - maintainer direction: category `uncategorized`; implication `needs_llm`
  - files: `acp_adapter/server.py`, `tests/acp/test_approval_timeout_config.py`
- **#63689: fix(acp): honor approvals.timeout config for edit approval timeout**. closed without merge
  - maintainer direction: category `superseded_by_pr`; implication `superseded`
- **#64030: fix(acp): honor approvals.timeout config in ACP edit approval prompt (#63302)**. closed without merge
  - maintainer direction: category `duplicate_of`; implication `duplicate`; refs `#63308`, `#63308`, `#63302`, `#64030`, `#63308`
- **#64336: fix: honor approvals.timeout for ACP edit approval prompts (#63302)**. open
  - maintainer direction: category `partial_fix`; implication `needs_llm`; refs `b1beab4e03ed`, `#63308`, `#63484`, `#63689`, `#64030`, `#64336`, `#63302`, `#63575`, `#63538`, `#63308`, `#63302`, `#63484`, `#63575`, `#63538`, `#63689`, `#64030`, `#63308`, `#64336`, `#63308`, `#63484`, `#64030`, `#63308`, `#63689`, `#64336`, `#63575`, `#63538`, `#63308`, `#63575`, `#63484`, `#63689`, `#64030`, `#64336`, `#63308`, `#374151`, `#63302`, `#63308`, `#63689`, `#64336`
  - files: `acp_adapter/edit_approval.py`, `acp_adapter/server.py`

## Fix status

- Merged to main in PR #3886 (cited above). No release data is tracked. Verify downstream.
- Fix last verified (merged) `2026-03-30`. This confirmation is old and may be out of date (a revert/reopen would not have invalidated it here); re-verify.
- Subsystem: `acp`
