diff --git a/hermes_cli/callbacks.py b/hermes_cli/callbacks.py index fa51ee15..87f86b84 100644 --- a/hermes_cli/callbacks.py +++ b/hermes_cli/callbacks.py @@ -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: diff --git a/hermes_cli/config.py b/hermes_cli/config.py index 4cdbb0af..69530761 100644 --- a/hermes_cli/config.py +++ b/hermes_cli/config.py @@ -407,6 +407,7 @@ DEFAULT_CONFIG = { # off — skip all approval prompts (equivalent to --yolo) "approvals": { "mode": "manual", + "timeout": 60, }, # Permanently allowed dangerous command patterns (added via "always" approval) diff --git a/tools/approval.py b/tools/approval.py index e7313b00..95011173 100644 --- a/tools/approval.py +++ b/tools/approval.py @@ -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). @@ -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, @@ -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: