§
    ‰eîi®¹  ã                   óì  — U d Z ddlZddlZddlZddlZddlZddlZddlZddlZddl	m
Z
  ej        e¦  «        Z ej        dd¬¦  «        Zej        e         ed<   ded	ej        e         fd
„Zdej        e         d	dfd„Zdded	efd„ZdZdZdZdZde› de› d�Zde› de› d�ZdZdZdddddddd ed!z   d"fed#z   d$fed%z   d&fed'z   d(fgZd)ed	efd*„Z d+ed	e!fd,„Z"g d-‘d.‘d/‘d0‘d1‘d2‘d3‘d4‘d5‘d6‘d7‘d8‘d9‘d:‘d;‘d<‘d=‘d‘d>‘d?‘d@‘dA‘dBe› �dCf‘dDe› �dEf‘dBe› dFe› �dGf‘dDe› dFe› �dHf‘dI‘dJ‘dK‘dL‘dM‘dN‘dO‘dP‘dQ‘dR‘dS‘dTe› dFe› �dUf‘dV‘dW‘dX‘dY‘dZ‘d[‘d\‘d]‘d^‘Z#d_ed	efd`„Z$i Z%e!ee&e         f         eda<   e#D ]x\  Z'Z( e$e'¦  «        Z)e(Z*e% +                    e* e&¦   «         ¦  «         ,                    e*e)h¦  «         e% +                    e) e&¦   «         ¦  «         ,                    e)e*h¦  «         Œydbed	e&e         fdc„Z-d)ed	efdd„Z.d)ed	efde„Z/ ej0        ¦   «         Z1i Z2e!ee!f         edf<   i Z3e!ee&f         edg<    e&¦   «         Z4e&e         edh<    e&¦   «         Z5e&edi<    G dj„ dk¦  «        Z6i Z7e!ee8f         edl<   i Z9e!ee:f         edm<   ded	dfdn„Z;ded	dfdo„Z<	 d’dedqedre=d	e>fds„Z?ded	e=fdt„Z@dedue!fdv„ZAdedbefdw„ZBded	dfdx„ZCded	dfdy„ZDded	dfdz„ZEded	e=fd{„ZFd	e=fd|„ZGdedbed	e=fd}„ZHdbefd~„ZIde&fd€„ZJd	e&fd�„ZKde&fd‚„ZL	 	 	 d“d)ed+ed„e>dz  d…e=d	ef
d†„ZMd	efd‡„ZNd	e!fdˆ„ZOd	efd‰„ZPd	e>fdŠ„ZQd	efd‹„ZRd)ed+ed	efdŒ„ZS	 d”d)ed�ed	e!fdŽ„ZTd�e!d	efd�„ZU	 d”d)ed�ed	e!fd‘„ZV eK¦   «          dS )•aÇ  Dangerous command approval -- detection, prompting, and per-session state.

This module is the single source of truth for the dangerous command system:
- Pattern detection (DANGEROUS_PATTERNS, detect_dangerous_command)
- Per-session approval state (thread-safe, keyed by session_key)
- Approval prompting (CLI interactive + gateway async)
- Smart approval via auxiliary LLM (auto-approve low-risk commands)
- Permanent allowlist persistence (config.yaml)
é    N)ÚOptionalÚapproval_session_keyÚ ©ÚdefaultÚ_approval_session_keyÚsession_keyÚreturnc                 ó:   — t                                | pd¦  «        S )z<Bind the active approval session key to the current context.r   )r   Úset©r	   s    ú0/home/div/.hermes/hermes-agent/tools/approval.pyÚset_current_session_keyr   !   s   € å ×$Ò$ [Ð%6°BÑ7Ô7Ð7ó    Útokenc                 ó:   — t                                | ¦  «         dS )z/Restore the prior approval session key context.N)r   Úreset)r   s    r   Úreset_current_session_keyr   &   s   € å×Ò Ñ&Ô&Ð&Ð&Ð&r   r   c                 ó`   — t                                ¦   «         }|r|S ddlm}  |d| ¦  «        S )a  Return the active session key, preferring context-local state.

    Resolution order:
    1. approval-specific contextvars (set by gateway before agent.run)
    2. session_context contextvars (set by _set_session_env)
    3. os.environ fallback (CLI, cron, tests)
    r   )Úget_session_envÚHERMES_SESSION_KEY)r   ÚgetÚgateway.session_contextr   )r   r	   r   s      r   Úget_current_session_keyr   +   sH   € õ (×+Ò+Ñ-Ô-€KØð ØÐØ7Ð7Ð7Ð7Ð7Ð7Øˆ?Ð/°Ñ9Ô9Ð9r   z$(?:~|\$home|\$\{home\})/\.ssh(?:/|$)z\(?:~\/\.hermes/|(?:\$home|\$\{home\})/\.hermes/|(?:\$hermes_home|\$\{hermes_home\})/)\.env\bz;(?:(?:/|\.{1,2}/)?(?:[^\s/"\'`]+/)*\.env(?:\.[^/\s"\'`]+)*)z0(?:(?:/|\.{1,2}/)?(?:[^\s/"\'`]+/)*config\.yaml)z(?:/etc/|/dev/sd|Ú|ú)z(?:z(?:\s*(?:&&|\|\||;).*)?$zp(?:^|[;&|\n`]|\$\()\s*(?:sudo\s+(?:-[^\s]+\s+)*)?(?:env\s+(?:\w+=\S*\s+)*)?(?:(?:exec|nohup|setsid|time)\s+)*\s*)z&\brm\s+(-[^\s]*\s+)*(/|/\*|/ \*)(\s|$)z#recursive delete of root filesystem)z˜\brm\s+(-[^\s]*\s+)*(/home|/home/\*|/root|/root/\*|/etc|/etc/\*|/usr|/usr/\*|/var|/var/\*|/bin|/bin/\*|/sbin|/sbin/\*|/boot|/boot/\*|/lib|/lib/\*)(\s|$)z$recursive delete of system directory)z-\brm\s+(-[^\s]*\s+)*(~|\$HOME)(/?|/\*)?(\s|$)z"recursive delete of home directory)z\bmkfs(\.[a-z0-9]+)?\bzformat filesystem (mkfs))z9\bdd\b[^\n]*\bof=/dev/(sd|nvme|hd|mmcblk|vd|xvd)[a-z0-9]*zdd to raw block device)z.>\s*/dev/(sd|nvme|hd|mmcblk|vd|xvd)[a-z0-9]*\bzredirect to raw block device)z(:\(\)\s*\{\s*:\s*\|\s*:\s*&\s*\}\s*;\s*:z	fork bomb)z\bkill\s+(-[^\s]+\s+)*-1\búkill all processesz!(shutdown|reboot|halt|poweroff)\bzsystem shutdown/rebootzinit\s+[06]\bzinit 0/6 (shutdown/reboot)z*systemctl\s+(poweroff|reboot|halt|kexec)\bzsystemctl poweroff/rebootztelinit\s+[06]\bztelinit 0/6 (shutdown/reboot)Úcommandc                 óÈ   — t          | ¦  «                             ¦   «         }t          D ]8\  }}t          j        ||t          j        t          j        z  ¦  «        rd|fc S Œ9dS )z‡Check if a command matches the unconditional hardline blocklist.

    Returns:
        (is_hardline, description) or (False, None)
    T)FN)Ú _normalize_command_for_detectionÚlowerÚHARDLINE_PATTERNSÚreÚsearchÚ
IGNORECASEÚDOTALL)r   Ú
normalizedÚpatternÚdescriptions       r   Údetect_hardline_commandr*   Ž   sk   € õ 2°'Ñ:Ô:×@Ò@ÑBÔB€JÝ 1ð 'ð 'Ñˆ�ÝŒ9�W˜j­"¬-½"¼)Ñ*CÑDÔDð 	'Ø˜+Ð&Ð&Ð&Ð&ð	'àˆ=r   r)   c                 ó   — ddd| › d�dœS )z5Build the standard block result for a hardline match.FTzBLOCKED (hardline): uò   . This command is on the unconditional blocklist and cannot be executed via the agent â€” not even with --yolo, /yolo, approvals.mode=off, or cron approve mode. If you genuinely need to run it, run it yourself in a terminal outside the agent.)ÚapprovedÚhardlineÚmessage© )r)   s    r   Ú_hardline_block_resultr0   ›   s/   € ð Øð ;ð ð ð ð	ð ð r   )z\brm\s+(-[^\s]*\s+)*/zdelete in root path)z\brm\s+-[^\s]*rzrecursive delete)z\brm\s+--recursive\bzrecursive delete (long flag))z8\bchmod\s+(-[^\s]*\s+)*(777|666|o\+[rwx]*w|a\+[rwx]*w)\bz world/other-writable permissions)z8\bchmod\s+--recursive\b.*(777|666|o\+[rwx]*w|a\+[rwx]*w)z*recursive world/other-writable (long flag))z\bchown\s+(-[^\s]*)?R\s+rootzrecursive chown to root)z\bchown\s+--recursive\b.*rootz#recursive chown to root (long flag))z\bmkfs\bzformat filesystem)z\bdd\s+.*if=z	disk copy)z>\s*/dev/sdzwrite to block device)z\bDROP\s+(TABLE|DATABASE)\bzSQL DROP)z \bDELETE\s+FROM\b(?!.*\bWHERE\b)zSQL DELETE without WHERE)z\bTRUNCATE\s+(TABLE)?\s*\wzSQL TRUNCATE)z	>\s*/etc/zoverwrite system config)z8\bsystemctl\s+(-[^\s]+\s+)*(stop|restart|disable|mask)\bzstop/restart system service)z\bkill\s+-9\s+-1\br   )z\bpkill\s+-9\bzforce kill processes)z%\b(bash|sh|zsh|ksh)\s+-[^\s]*c(\s+|$)zshell command via -c/-lc flag)z)\b(python[23]?|perl|ruby|node)\s+-[ec]\s+zscript execution via -e/-c flag)z\b(curl|wget)\b.*\|\s*(ba)?sh\bzpipe remote content to shell)z1\b(bash|sh|zsh|ksh)\s+<\s*<?\s*\(\s*(curl|wget)\bz.execute remote script via process substitutionz\btee\b.*["\']?zoverwrite system file via teez>>?\s*["\']?z%overwrite system file via redirectionz["\']?z$overwrite project env/config via teez,overwrite project env/config via redirection)z\bxargs\s+.*\brm\bzxargs with rm)z\bfind\b.*-exec\s+(/\S*/)?rm\bzfind -exec rm)z\bfind\b.*-delete\bzfind -delete)z%\bhermes\s+gateway\s+(stop|restart)\bz2stop/restart hermes gateway (kills running agents))z\bhermes\s+update\bz6hermes update (restarts gateway, kills running agents))z4gateway\s+run\b.*(&\s*$|&\s*;|\bdisown\b|\bsetsid\b)úMstart gateway outside systemd (use 'systemctl --user restart hermes-gateway'))z\bnohup\b.*gateway\s+run\br1   )z1\b(pkill|killall)\b.*\b(hermes|gateway|cli\.py)\bz.kill hermes/gateway process (self-termination))z\bkill\b.*\$\(\s*pgrep\bz3kill process via pgrep expansion (self-termination))z\bkill\b.*`\s*pgrep\bz<kill process via backtick pgrep expansion (self-termination))z\b(cp|mv|install)\b.*\s/etc/zcopy/move file into /etc/z\b(cp|mv|install)\b.*\s["\']?z!overwrite project env/config file)z\bsed\s+-[^\s]*i.*\s/etc/zin-place edit of system config)z\bsed\s+--in-place\b.*\s/etc/z*in-place edit of system config (long flag))z#\b(python[23]?|perl|ruby|node)\s+<<zscript execution via heredoc)z\bgit\s+reset\s+--hard\bz/git reset --hard (destroys uncommitted changes))z\bgit\s+push\b.*--force\bz(git force push (rewrites remote history))z\bgit\s+push\b.*-f\bz3git force push short flag (rewrites remote history))z\bgit\s+clean\s+-[^\s]*fz.git clean with force (deletes untracked files))z\bgit\s+branch\s+-D\bzgit branch force delete)z\bchmod\s+\+x\b.*[;&|]+\s*\./z(chmod +x followed by immediate executionr(   c                 óT   — d| v r|                       d¦  «        d         n	| dd…         S )zIReproduce the old regex-derived approval key for backwards compatibility.z\bé   Né   )Úsplit)r(   s    r   Ú_legacy_pattern_keyr6   ô   s0   € à&+¨wÐ&6Ð&6ˆ7�=Š=˜ÑÔ Ô"Ð"¸GÀCÀRÀC¼LÐHr   Ú_PATTERN_KEY_ALIASESÚpattern_keyc                 ó:   — t                                | | h¦  «        S )zñReturn all approval keys that should match this pattern.

    New approvals use the human-readable description string, but older
    command_allowlist entries and session approvals may still contain the
    historical regex-derived key.
    )r7   r   ©r8   s    r   Ú_approval_key_aliasesr;     s   € õ  ×#Ò# K°+°Ñ?Ô?Ð?r   c                 ó~   — ddl m}  || ¦  «        } |                      dd¦  «        } t          j        d| ¦  «        } | S )a  Normalize a command string before dangerous-pattern matching.

    Strips ANSI escape sequences (full ECMA-48 via tools.ansi_strip),
    null bytes, and normalizes Unicode fullwidth characters so that
    obfuscation techniques cannot bypass the pattern-based detection.
    r   )Ú
strip_ansiú r   ÚNFKC)Útools.ansi_stripr=   ÚreplaceÚunicodedataÚ	normalize)r   r=   s     r   r    r      sQ   € ð ,Ð+Ð+Ð+Ð+Ð+ð ˆj˜Ñ!Ô!€Gà�oŠo˜f bÑ)Ô)€GåÔ# F¨GÑ4Ô4€GØ€Nr   c                 óÎ   — t          | ¦  «                             ¦   «         }t          D ];\  }}t          j        ||t          j        t          j        z  ¦  «        r	|}d||fc S Œ<dS )z�Check if a command matches any dangerous patterns.

    Returns:
        (is_dangerous, pattern_key, description) or (False, None, None)
    T)FNN)r    r!   ÚDANGEROUS_PATTERNSr#   r$   r%   r&   )r   Úcommand_lowerr(   r)   r8   s        r   Údetect_dangerous_commandrG   !  su   € õ 5°WÑ=Ô=×CÒCÑEÔE€MÝ 2ð 4ð 4Ñˆ�ÝŒ9�W˜m­R¬]½R¼YÑ-FÑGÔGð 	4Ø%ˆKØ˜+ {Ð3Ð3Ð3Ð3ð	4ð Ðr   Ú_pendingÚ_session_approvedÚ_session_yoloÚ_permanent_approvedc                   ó"   — e Zd ZdZdZdefd„ZdS )Ú_ApprovalEntryz@One pending dangerous-command approval inside a gateway session.)ÚeventÚdataÚresultrO   c                 óR   — t          j        ¦   «         | _        || _        d | _        d S ©N)Ú	threadingÚEventrN   rO   rP   )ÚselfrO   s     r   Ú__init__z_ApprovalEntry.__init__F  s#   € Ý”_Ñ&Ô&ˆŒ
ØˆŒ	Ø%)ˆŒˆˆr   N)Ú__name__Ú
__module__Ú__qualname__Ú__doc__Ú	__slots__ÚdictrV   r/   r   r   rM   rM   B  s:   € € € € € ØJÐJØ+€Ið*˜Tð *ð *ð *ð *ð *ð *r   rM   Ú_gateway_queuesÚ_gateway_notify_cbsc                 óZ   — t           5  |t          | <   ddd¦  «         dS # 1 swxY w Y   dS )ua  Register a per-session callback for sending approval requests to the user.

    The callback signature is ``cb(approval_data: dict) -> None`` where
    *approval_data* contains ``command``, ``description``, and
    ``pattern_keys``.  The callback bridges syncâ†’async (runs in the agent
    thread, must schedule the actual send on the event loop).
    N)Ú_lockr^   )r	   Úcbs     r   Úregister_gateway_notifyrb   P  sy   € õ 
ð .ð .Ø+-Õ˜KÑ(ð.ð .ð .ñ .ô .ð .ð .ð .ð .ð .ð .ð .øøøð .ð .ð .ð .ð .ð .ó   ˆ  $§$c                 óð   — t           5  t                               | d¦  «         t                               | g ¦  «        }|D ]}|j                             ¦   «          Œ	 ddd¦  «         dS # 1 swxY w Y   dS )zÁUnregister the per-session gateway approval callback.

    Signals ALL blocked threads for this session so they don't hang forever
    (e.g. when the agent run finishes or is interrupted).
    N)r`   r^   Úpopr]   rN   r   )r	   ÚentriesÚentrys      r   Úunregister_gateway_notifyrh   \  sÂ   € õ 
ð ð Ý×Ò ¨TÑ2Ô2Ð2Ý!×%Ò% k°2Ñ6Ô6ˆØð 	ð 	ˆEØŒK�OŠOÑÔÐÐð	ðð ð ñ ô ð ð ð ð ð ð ð øøøð ð ð ð ð ð s   ˆAA+Á+A/Á2A/FÚchoiceÚresolve_allc                 ó®  — t           5  t                               | ¦  «        }|s	 ddd¦  «         dS |r$t          |¦  «        }|                     ¦   «          n|                     d¦  «        g}|st                               | d¦  «         ddd¦  «         n# 1 swxY w Y   |D ]"}||_        |j                             ¦   «          Œ#t          |¦  «        S )aT  Called by the gateway's /approve or /deny handler to unblock
    waiting agent thread(s).

    When *resolve_all* is True every pending approval in the session is
    resolved at once (``/approve all``).  Otherwise only the oldest one
    is resolved (FIFO).

    Returns the number of approvals resolved (0 means nothing was pending).
    Nr   )
r`   r]   r   ÚlistÚclearre   rP   rN   r   Úlen)r	   ri   rj   ÚqueueÚtargetsrg   s         r   Úresolve_gateway_approvalrq   i  s1  € õ 
ð 
3ð 
3Ý×#Ò# KÑ0Ô0ˆØð 	Øð
3ð 
3ð 
3ñ 
3ô 
3ð 
3ð 
3ð 
3ð ð 	%Ý˜5‘k”kˆGØ�KŠK‰MŒMˆMˆMà—y’y ‘|”|�nˆGØð 	3Ý×Ò ¨TÑ2Ô2Ð2ð
3ð 
3ð 
3ñ 
3ô 
3ð 
3ð 
3ð 
3ð 
3ð 
3ð 
3øøøð 
3ð 
3ð 
3ð 
3ð ð ð ˆØˆŒØŒ�ŠÑÔÐÐÝˆw‰<Œ<Ðs   ˆB³ABÂBÂBc                 ó’   — t           5  t          t                               | ¦  «        ¦  «        cddd¦  «         S # 1 swxY w Y   dS )zFCheck if a session has one or more blocking gateway approvals waiting.N)r`   Úboolr]   r   r   s    r   Úhas_blocking_approvalrt   †  s…   € å	ð 6ð 6Ý•O×'Ò'¨Ñ4Ô4Ñ5Ô5ð6ð 6ð 6ð 6ñ 6ô 6ð 6ð 6ð 6ð 6ð 6ð 6øøøð 6ð 6ð 6ð 6ð 6ð 6s   ˆ'<¼A ÁA Úapprovalc                 óZ   — t           5  |t          | <   ddd¦  «         dS # 1 swxY w Y   dS )z/Store a pending approval request for a session.N)r`   rH   )r	   ru   s     r   Úsubmit_pendingrw   Œ  sv   € å	ð )ð )Ø (��Ñð)ð )ð )ñ )ô )ð )ð )ð )ð )ð )ð )ð )øøøð )ð )ð )ð )ð )ð )rc   c                 óº   — t           5  t                               | t          ¦   «         ¦  «                             |¦  «         ddd¦  «         dS # 1 swxY w Y   dS )z(Approve a pattern for this session only.N)r`   rI   Ú
setdefaultr   Úadd)r	   r8   s     r   Úapprove_sessionr{   ’  s¨   € å	ð Jð JÝ×$Ò$ [µ#±%´%Ñ8Ô8×<Ò<¸[ÑIÔIÐIðJð Jð Jñ Jô Jð Jð Jð Jð Jð Jð Jð Jøøøð Jð Jð Jð Jð Jð Js   ˆ;AÁAÁAc                 ó‚   — | sdS t           5  t                               | ¦  «         ddd¦  «         dS # 1 swxY w Y   dS )z,Enable YOLO bypass for a single session key.N)r`   rJ   rz   r   s    r   Úenable_session_yolor}   ˜  s�   € àð ØˆÝ	ð 'ð 'Ý×Ò˜+Ñ&Ô&Ð&ð'ð 'ð 'ñ 'ô 'ð 'ð 'ð 'ð 'ð 'ð 'ð 'øøøð 'ð 'ð 'ð 'ð 'ð 'ó   Œ4´8»8c                 ó‚   — | sdS t           5  t                               | ¦  «         ddd¦  «         dS # 1 swxY w Y   dS )z-Disable YOLO bypass for a single session key.N)r`   rJ   Údiscardr   s    r   Údisable_session_yolor�      s�   € àð ØˆÝ	ð +ð +Ý×Ò˜kÑ*Ô*Ð*ð+ð +ð +ñ +ô +ð +ð +ð +ð +ð +ð +ð +øøøð +ð +ð +ð +ð +ð +r~   c                 ó$  — | sdS t           5  t                               | d¦  «         t                               | ¦  «         t
                               | d¦  «         t                               | d¦  «         ddd¦  «         dS # 1 swxY w Y   dS )z7Remove all approval and yolo state for a given session.N)r`   rI   re   rJ   r€   rH   r]   r   s    r   Úclear_sessionrƒ   ¨  sÍ   € àð ØˆÝ	ð /ð /Ý×Ò˜k¨4Ñ0Ô0Ð0Ý×Ò˜kÑ*Ô*Ð*Ý�Š�[ $Ñ'Ô'Ð'Ý×Ò˜K¨Ñ.Ô.Ð.ð	/ð /ð /ñ /ô /ð /ð /ð /ð /ð /ð /ð /øøøð /ð /ð /ð /ð /ð /s   ŒA,BÂB	ÂB	c                 ó^   — | sdS t           5  | t          v cddd¦  «         S # 1 swxY w Y   dS )z?Return True when YOLO bypass is enabled for a specific session.FN)r`   rJ   r   s    r   Úis_session_yolo_enabledr…   ³  s�   € àð ØˆuÝ	ð ,ð ,Ø�mÐ+ð,ð ,ð ,ð ,ñ ,ô ,ð ,ð ,ð ,ð ,ð ,ð ,øøøð ,ð ,ð ,ð ,ð ,ð ,s   Œ	"¢&©&c                  ó<   — t          t          d¬¦  «        ¦  «        S )zEReturn True when the active approval session has YOLO bypass enabled.r   r   )r…   r   r/   r   r   Úis_current_session_yolo_enabledr‡   »  s   € å"Õ#:À2Ð#FÑ#FÔ#FÑGÔGÐGr   c                 ó6  ‡— t          |¦  «        }t          5  t          d„ |D ¦   «         ¦  «        r	 ddd¦  «         dS t                               | t          ¦   «         ¦  «        Št          ˆfd„|D ¦   «         ¦  «        cddd¦  «         S # 1 swxY w Y   dS )zßCheck if a pattern is approved (session-scoped or permanent).

    Accept both the current canonical key and the legacy regex-derived key so
    existing command_allowlist entries continue to work after key migrations.
    c              3   ó(   K  — | ]}|t           v V — Œd S rR   )rK   )Ú.0Úaliass     r   ú	<genexpr>zis_approved.<locals>.<genexpr>È  s(   è è € ÐAÐA°ˆuÕ+Ð+ÐAÐAÐAÐAÐAÐAr   NTc              3   ó    •K  — | ]}|‰v V — Œ	d S rR   r/   )rŠ   r‹   Úsession_approvalss     €r   rŒ   zis_approved.<locals>.<genexpr>Ë  s)   øè è € ÐCÐC°%�5Ð-Ð-ÐCÐCÐCÐCÐCÐCr   )r;   r`   ÚanyrI   r   r   )r	   r8   ÚaliasesrŽ   s      @r   Úis_approvedr‘   À  s  ø€ õ $ KÑ0Ô0€GÝ	ð Dð DÝÐAÐA¸ÐAÑAÔAÑAÔAð 	ØðDð Dð Dñ Dô Dð Dð Dð Dõ .×1Ò1°+½s¹u¼uÑEÔEÐÝÐCÐCÐCÐC¸7ÐCÑCÔCÑCÔCð	Dð Dð Dð Dñ Dô Dð Dð Dð Dð Dð Dð Døøøð Dð Dð Dð Dð Dð Ds   ˜BÁ ABÂBÂBc                 óz   — t           5  t                               | ¦  «         ddd¦  «         dS # 1 swxY w Y   dS )z)Add a pattern to the permanent allowlist.N)r`   rK   rz   r:   s    r   Úapprove_permanentr“   Î  s€   € å	ð -ð -Ý×Ò Ñ,Ô,Ð,ð-ð -ð -ñ -ô -ð -ð -ð -ð -ð -ð -ð -øøøð -ð -ð -ð -ð -ð -ó   ˆ0°4·4Úpatternsc                 óz   — t           5  t                               | ¦  «         ddd¦  «         dS # 1 swxY w Y   dS )z2Bulk-load permanent allowlist entries from config.N)r`   rK   Úupdate)r•   s    r   Úload_permanentr˜   Ô  s€   € å	ð -ð -Ý×"Ò" 8Ñ,Ô,Ð,ð-ð -ð -ñ -ô -ð -ð -ð -ð -ð -ð -ð -øøøð -ð -ð -ð -ð -ð -r”   c                  ó  — 	 ddl m}   | ¦   «         }t          |                     dg ¦  «        pg ¦  «        }|rt	          |¦  «         |S # t
          $ r3}t                               d|¦  «         t          ¦   «         cY d}~S d}~ww xY w)z»Load permanently allowed command patterns from config.

    Also syncs them into the approval module so is_approved() works for
    patterns added via 'always' in a previous session.
    r   ©Úload_configÚcommand_allowlistz&Failed to load permanent allowlist: %sN)Úhermes_cli.configr›   r   r   r˜   Ú	ExceptionÚloggerÚwarning)r›   Úconfigr•   Úes       r   Úload_permanent_allowlistr£   ß  s§   € ð	Ø1Ð1Ð1Ð1Ð1Ð1Ø�‘”ˆÝ�v—z’zÐ"5°rÑ:Ô:Ð@¸bÑAÔAˆØð 	%Ý˜8Ñ$Ô$Ð$ØˆøÝð ð ð Ý�ŠÐ?ÀÑCÔCÐCÝ‰uŒuˆˆˆˆˆˆøøøøðøøøs   ‚AA
 Á

BÁ(BÁ<BÂBc                 óÌ   — 	 ddl m}m}  |¦   «         }t          | ¦  «        |d<    ||¦  «         dS # t          $ r&}t
                               d|¦  «         Y d}~dS d}~ww xY w)z4Save permanently allowed command patterns to config.r   )r›   Úsave_configrœ   zCould not save allowlist: %sN)r�   r›   r¥   rl   rž   rŸ   r    )r•   r›   r¥   r¡   r¢   s        r   Úsave_permanent_allowlistr¦   ñ  s—   € ð:Ø>Ð>Ð>Ð>Ð>Ð>Ð>Ð>Ø�‘”ˆÝ&*¨8¡n¤nˆÐ"Ñ#Øˆ�FÑÔÐÐÐøÝð :ð :ð :Ý�ŠÐ5°qÑ9Ô9Ð9Ð9Ð9Ð9Ð9Ð9Ð9øøøøð:øøøs   ‚/3 ³
A#½AÁA#TÚtimeout_secondsÚallow_permanentc                 óv  ‡‡	— |€t          ¦   «         }|�D	  || |‰¬¦  «        S # t          $ r(}t                               d|d¬¦  «         Y d}~dS d}~ww xY wdt          j        d<   	 	 t          ¦   «          t          d	|› �¦  «         t          d
| › �¦  «         t          ¦   «          ‰rt          d¦  «         nt          d¦  «         t          ¦   «          t          j         	                    ¦   «          ddiŠ	ˆˆ	fd„}t          j        |d¬¦  «        }|                     ¦   «          |                     |¬¦  «         |                     ¦   «         rYt          d¦  «         	 dt          j        v rt          j        d= t          ¦   «          t          j         	                    ¦   «          dS ‰	d         }|dv rYt          d¦  «         	 dt          j        v rt          j        d= t          ¦   «          t          j         	                    ¦   «          dS |dv rYt          d¦  «         	 dt          j        v rt          j        d= t          ¦   «          t          j         	                    ¦   «          dS |dv r´‰sYt          d¦  «         	 dt          j        v rt          j        d= t          ¦   «          t          j         	                    ¦   «          dS t          d¦  «         	 dt          j        v rt          j        d= t          ¦   «          t          j         	                    ¦   «          dS t          d¦  «         	 dt          j        v rt          j        d= t          ¦   «          t          j         	                    ¦   «          dS # t          t           f$ rZ t          d¦  «         Y dt          j        v rt          j        d= t          ¦   «          t          j         	                    ¦   «          dS w xY w# dt          j        v rt          j        d= t          ¦   «          t          j         	                    ¦   «          w xY w)a  Prompt the user to approve a dangerous command (CLI only).

    Args:
        allow_permanent: When False, hide the [a]lways option (used when
            tirith warnings are present, since broad permanent allowlisting
            is inappropriate for content-level security findings).
        approval_callback: Optional callback registered by the CLI for
            prompt_toolkit integration. Signature:
            (command, description, *, allow_permanent=True) -> str.

    Returns: 'once', 'session', 'always', or 'deny'
    N)r¨   zApproval callback failed: %sT)Úexc_infoÚdenyÚ1ÚHERMES_SPINNER_PAUSEu     âš ï¸�  DANGEROUS COMMAND: z      z2      [o]nce  |  [s]ession  |  [a]lways  |  [d]enyz%      [o]nce  |  [s]ession  |  [d]enyri   r   c                  ó¼   •— 	 ‰rdnd} t          | ¦  «                             ¦   «                              ¦   «         ‰d<   d S # t          t          f$ r	 d‰d<   Y d S w xY w)Nz      Choice [o/s/a/D]: z      Choice [o/s/D]: ri   r   )ÚinputÚstripr!   ÚEOFErrorÚOSError)Úpromptr¨   rP   s    €€r   Ú	get_inputz,prompt_dangerous_approval.<locals>.get_input+  sz   ø€ ð*Ø;JÐhÐ7Ð7ÐPh�FÝ',¨V¡}¤}×':Ò':Ñ'<Ô'<×'BÒ'BÑ'DÔ'D�F˜8Ñ$Ð$Ð$øÝ ¥'Ð*ð *ð *ð *Ø')�F˜8Ñ$Ð$Ð$Ð$ð*øøøs   ƒ<A ÁAÁA)ÚtargetÚdaemon©Útimeoutu$   
      â�± Timeout - denying command)ÚoÚonceu         âœ“ Allowed oncerº   )ÚsÚsessionu"         âœ“ Allowed for this sessionr¼   )ÚaÚalwaysu&         âœ“ Added to permanent allowlistr¾   u         âœ— Deniedu   
      âœ— Cancelled)Ú_get_approval_timeoutrž   rŸ   ÚerrorÚosÚenvironÚprintÚsysÚstdoutÚflushrS   ÚThreadÚstartÚjoinÚis_aliver±   ÚKeyboardInterrupt)
r   r)   r§   r¨   Úapproval_callbackr¢   r´   Úthreadri   rP   s
      `     @r   Úprompt_dangerous_approvalrÎ      sb  øø€ ð  ÐÝ/Ñ1Ô1ˆàÐ$ð	Ø$Ð$ W¨kØ5DðFñ Fô Fð Føåð 	ð 	ð 	Ý�LŠLÐ7¸ÀTˆLÑJÔJÐJØ�6�6�6�6�6øøøøð	øøøð *-…B„JÐ%Ñ&ð6ð,	Ý‰GŒGˆGÝÐ?°+Ð?Ð?Ñ@Ô@Ð@ÝÐ$˜7Ð$Ð$Ñ%Ô%Ð%Ý‰GŒGˆGØð ?ÝÐJÑKÔKÐKÐKåÐ=Ñ>Ô>Ð>Ý‰GŒGˆGÝŒJ×ÒÑÔÐà �^ˆFð*ð *ð *ð *ð *ð *õ Ô%¨Y¸tÐDÑDÔDˆFØ�LŠL‰NŒNˆNØ�KŠK ˆKÑ0Ô0Ð0à�ŠÑ Ô ð ÝÐ=Ñ>Ô>Ð>Øð. "¥R¤ZÐ/Ð/Ý”
Ð1Ð2Ý‰ŒˆÝŒ
×ÒÑÔÐÐÐð1 ˜HÔ%ˆFØ˜Ð&Ð&ÝÐ.Ñ/Ô/Ð/Øð$ "¥R¤ZÐ/Ð/Ý”
Ð1Ð2Ý‰ŒˆÝŒ
×ÒÑÔÐÐÐð) Ð+Ð+Ð+ÝÐ:Ñ;Ô;Ð;Ø ð "¥R¤ZÐ/Ð/Ý”
Ð1Ð2Ý‰ŒˆÝŒ
×ÒÑÔÐÐÐð# ˜?Ð*Ð*Ø&ð %ÝÐ>Ñ?Ô?Ð?Ø$ð "¥R¤ZÐ/Ð/Ý”
Ð1Ð2Ý‰ŒˆÝŒ
×ÒÑÔÐÐÐõ Ð>Ñ?Ô?Ð?Øð "¥R¤ZÐ/Ð/Ý”
Ð1Ð2Ý‰ŒˆÝŒ
×ÒÑÔÐÐÐõ Ð(Ñ)Ô)Ð)Øð "¥R¤ZÐ/Ð/Ý”
Ð1Ð2Ý‰ŒˆÝŒ
×ÒÑÔÐÐÐøõ Õ'Ð(ð ð ð ÝÐ%Ñ&Ô&Ð&Øà!¥R¤ZÐ/Ð/Ý”
Ð1Ð2Ý‰ŒˆÝŒ
×ÒÑÔÐÐÐðøøøøð "¥R¤ZÐ/Ð/Ý”
Ð1Ð2Ý‰ŒˆÝŒ
×ÒÑÔÐÐøøøsX   –$ ¤
A®AÁAÁ)C;N Æ.N ÈN É0N ËN Ì(N Î O,Î!O/ Ï+O,Ï,O/ Ï/A	P8c                 ó¾   — t          | t          ¦  «        r| du rdndS t          | t          ¦  «        r*|                      ¦   «                              ¦   «         }|pdS dS )a"  Normalize approval mode values loaded from YAML/config.

    YAML 1.1 treats bare words like `off` as booleans, so a config entry like
    `approvals:
  mode: off` is parsed as False unless quoted. Treat that as the
    intended string mode instead of falling back to manual approvals.
    FÚoffÚmanual)Ú
isinstancers   Ústrr°   r!   )Úmoder'   s     r   Ú_normalize_approval_moderÕ   U  sc   € õ �$�ÑÔð 4Ø ˜˜ˆuˆu¨8Ð3Ý�$�ÑÔð &Ø—Z’Z‘\”\×'Ò'Ñ)Ô)ˆ
ØÐ%˜XÐ%Øˆ8r   c                  ó¼   — 	 ddl m}   | ¦   «         }|                     di ¦  «        pi S # t          $ r'}t                               d|¦  «         i cY d}~S d}~ww xY w)zLRead the approvals config block. Returns a dict with 'mode', 'timeout', etc.r   rš   Ú	approvalsz"Failed to load approval config: %sN)r�   r›   r   rž   rŸ   r    )r›   r¡   r¢   s      r   Ú_get_approval_configrØ   d  s€   € ðØ1Ð1Ð1Ð1Ð1Ð1Ø�‘”ˆØ�zŠz˜+ rÑ*Ô*Ð0¨bÐ0øÝð ð ð Ý�ŠÐ;¸QÑ?Ô?Ð?Øˆ	ˆ	ˆ	ˆ	ˆ	ˆ	øøøøðøøøs   ‚'* ª
A´AÁAÁAc                  ód   — t          ¦   «                              dd¦  «        } t          | ¦  «        S )zHRead the approval mode from config. Returns 'manual', 'smart', or 'off'.rÔ   rÑ   )rØ   r   rÕ   )rÔ   s    r   Ú_get_approval_moderÚ   o  s+   € åÑ!Ô!×%Ò% f¨hÑ7Ô7€DÝ# DÑ)Ô)Ð)r   c                  ó’   — 	 t          t          ¦   «                              dd¦  «        ¦  «        S # t          t          f$ r Y dS w xY w)z>Read the approval timeout from config. Defaults to 60 seconds.r¸   é<   )ÚintrØ   r   Ú
ValueErrorÚ	TypeErrorr/   r   r   r¿   r¿   u  sS   € ðÝÕ'Ñ)Ô)×-Ò-¨i¸Ñ<Ô<Ñ=Ô=Ð=øÝ�	Ð"ð ð ð Øˆrˆrðøøøs   ‚.1 ±AÁAc                  ó  — 	 ddl m}   | ¦   «         }t          |                     di ¦  «                             dd¦  «        ¦  «                             ¦   «                              ¦   «         }|dv rdS dS # t          $ r Y dS w xY w)zERead the cron approval mode from config. Returns 'deny' or 'approve'.r   rš   r×   Ú	cron_moder«   )ÚapproverÐ   ÚallowÚyesrâ   )r�   r›   rÓ   r   r!   r°   rž   )r›   r¡   rÔ   s      r   Ú_get_cron_approval_moderå   }  sŸ   € ðØ1Ð1Ð1Ð1Ð1Ð1Ø�‘”ˆÝ�6—:’:˜k¨2Ñ.Ô.×2Ò2°;ÀÑGÔGÑHÔH×NÒNÑPÔP×VÒVÑXÔXˆØÐ5Ð5Ð5Ø�9ØˆvøÝð ð ð Øˆvˆvðøøøs   ‚A/A5 Á5
BÂBc                 óD  — 	 ddl m} d| › d|› d�} |dd|dœgdd	¬
¦  «        }|j        d         j        j        pd                     ¦   «                              ¦   «         }d|v rdS d|v rdS dS # t          $ r&}t           	                    d|¦  «         Y d}~dS d}~ww xY w)a  Use the auxiliary LLM to assess risk and decide approval.

    Returns 'approve' if the LLM determines the command is safe,
    'deny' if genuinely dangerous, or 'escalate' if uncertain.

    Inspired by OpenAI Codex's Smart Approvals guardian subagent
    (openai/codex#13860).
    r   )Úcall_llmz‹You are a security reviewer for an AI coding agent. A terminal command was flagged by pattern matching as potentially dangerous.

Command: z
Flagged reason: ul  

Assess the ACTUAL risk of this command. Many flagged commands are false positives â€” for example, `python -c "print('hello')"` is flagged as "script execution via -c flag" but is completely harmless.

Rules:
- APPROVE if the command is clearly safe (benign script execution, safe file operations, development tools, package installs, git operations, etc.)
- DENY if the command could genuinely damage the system (recursive delete of important paths, overwriting system files, fork bombs, wiping disks, dropping databases, etc.)
- ESCALATE if you're uncertain

Respond with exactly one word: APPROVE, DENY, or ESCALATEru   Úuser)ÚroleÚcontenté   )ÚtaskÚmessagesÚtemperatureÚ
max_tokensr   ÚAPPROVErâ   ÚDENYr«   Úescalatez1Smart approvals: LLM call failed (%s), escalatingN)
Úagent.auxiliary_clientrç   Úchoicesr.   rê   r°   Úupperrž   rŸ   Údebug)r   r)   rç   r³   ÚresponseÚanswerr¢   s          r   Ú_smart_approverù   Š  s  € ð#Ø3Ð3Ð3Ð3Ð3Ð3ð=à
ð=ð =ð ð=ð =ð =ˆð �8ØØ%°&Ð9Ð9Ð:ØØð	
ñ 
ô 
ˆð Ô" 1Ô%Ô-Ô5Ð;¸×BÒBÑDÔD×JÒJÑLÔLˆà˜ÐÐØ�9Ø�vÐÐØ�6à�:øåð ð ð Ý�ŠÐHÈ!ÑLÔLÐLØˆzˆzˆzˆzˆzøøøøðøøøs   ‚A#A/ Á'A/ Á/
BÁ9BÂBÚenv_typec           
      óŽ  — |dv rdddœS t          | ¦  «        \  }}|r3t                               d|| dd…         ¦  «         t          |¦  «        S t	          j        d¦  «        st          ¦   «         rdddœS t          | ¦  «        \  }}}|sdddœS t          ¦   «         }t          ||¦  «        rdddœS t	          j        d¦  «        }	t	          j        d	¦  «        }
|	s6|
s4t	          j        d
¦  «        rt          ¦   «         dk    r	dd|› d�dœS dddœS |
st	          j        d¦  «        r$t          || ||dœ¦  «         d|d| |d|› d| › d�dœS t          | ||¬¦  «        }|dk    rdd|› d�||dœS |dk    rt          ||¦  «         n9|dk    r3t          ||¦  «         t          |¦  «         t          t           ¦  «         dddœS )aç  Check if a command is dangerous and handle approval.

    This is the main entry point called by terminal_tool before executing
    any command. It orchestrates detection, session checks, and prompting.

    Args:
        command: The shell command to check.
        env_type: Terminal backend type ('local', 'ssh', 'docker', etc.).
        approval_callback: Optional CLI callback for interactive prompts.

    Returns:
        {"approved": True/False, "message": str or None, ...}
    ©ÚdockerÚsingularityÚmodalÚdaytonaTN©r,   r.   ú Hardline block: %s (command: %s)éÈ   ÚHERMES_YOLO_MODEÚHERMES_INTERACTIVEÚHERMES_GATEWAY_SESSIONÚHERMES_CRON_SESSIONr«   Fú'BLOCKED: Command flagged as dangerous (úË) but cron jobs run without a user present to approve it. Find an alternative approach that avoids this command. To allow dangerous commands in cron jobs, set approvals.cron_mode: approve in config.yaml.ÚHERMES_EXEC_ASK)r   r8   r)   Úapproval_requiredu.   âš ï¸� This command is potentially dangerous (z3). Asking the user for approval.

**Command:**
```
ú
```©r,   r8   Ústatusr   r)   r.   )rÌ   zBBLOCKED: User denied this potentially dangerous command (matched 'zL' pattern). Do NOT retry this command - the user has explicitly rejected it.©r,   r.   r8   r)   r¼   r¾   )r*   rŸ   r    r0   rÁ   Úgetenvr‡   rG   r   r‘   rå   rw   rÎ   r{   r“   r¦   rK   )r   rú   rÌ   Úis_hardlineÚhardline_descÚis_dangerousr8   r)   r	   Úis_cliÚ
is_gatewayri   s               r   Úcheck_dangerous_commandr  ¹  sÏ  € ð Ð@Ð@Ð@Ø ¨TÐ2Ð2Ð2õ "9¸Ñ!AÔ!AÑ€K�Øð 5Ý�ŠÐ9¸=È'ÐRVÐSVÐRVÌ-ÑXÔXÐXÝ% mÑ4Ô4Ð4õ 
„yÐ#Ñ$Ô$ð 3Õ(GÑ(IÔ(Ið 3Ø ¨TÐ2Ð2Ð2å-EÀgÑ-NÔ-NÑ*€L�+˜{Øð 3Ø ¨TÐ2Ð2Ð2å)Ñ+Ô+€KÝ�; Ñ,Ô,ð 3Ø ¨TÐ2Ð2Ð2åŒYÐ+Ñ,Ô,€FÝ”Ð3Ñ4Ô4€Jàð 3˜*ð 3åŒ9Ð*Ñ+Ô+ð 	Ý&Ñ(Ô(¨FÒ2Ð2à %ðGÀ+ð Gð Gð Gð	ð 	ð 	ð !¨TÐ2Ð2Ð2àð 
•R”YÐ0Ñ1Ô1ð 
Ý�{ØØ&Ø&ð%
ð %
ñ 	ô 	ð 	ð Ø&Ø)ØØ&ðVÀð Vð VØGNðVð Vð Vð

ð 

ð 
	
õ ' w°Ø9JðLñ Lô L€Fð �ÒÐàð vÐ\gð  vð  vð  vØ&Ø&ð	
ð 
ð 	
ð �ÒÐÝ˜ [Ñ1Ô1Ð1Ð1Ø	�8Ò	Ð	Ý˜ [Ñ1Ô1Ð1Ý˜+Ñ&Ô&Ð&Ý Õ!4Ñ5Ô5Ð5à¨Ð.Ð.Ð.r   Útirith_resultc           	      ó  — |                       d¦  «        pg }|s|                       d¦  «        pd}d|› �S g }|D ]‘}|                      dd¦  «        }|                      dd¦  «        }|                      dd¦  «        }|r*|r(|                     |rd	|› d
|› d|› �n|› d|› �¦  «         Œp|r|                     |rd	|› d
|› �n|¦  «         Œ’|s|                       d¦  «        pd}d|› �S dd                     |¦  «        z   S )z²Build a human-readable description from tirith findings.

    Includes severity, title, and description for each finding so users
    can make an informed approval decision.
    ÚfindingsÚsummaryzsecurity issue detectedzSecurity scan: Úseverityr   Útitler)   Ú[z] z: u   Security scan â€” ú; )r   ÚappendrÉ   )r  r  r  ÚpartsÚfr  r  Údescs           r   Ú_format_tirith_descriptionr#     so  € ð × Ò  Ñ,Ô,Ð2°€HØð +Ø×#Ò# IÑ.Ô.ÐKÐ2KˆØ* Ð*Ð*Ð*à€EØð Ið IˆØ—5’5˜ RÑ(Ô(ˆØ—’�g˜rÑ"Ô"ˆØ�uŠu�] BÑ'Ô'ˆØð 	I�Tð 	IØ�LŠL¸HÐ\Ð8˜XÐ8Ð8¨Ð8Ð8°$Ð8Ð8Ð8ÈUÐJ\ÐJ\ÐVZÐJ\ÐJ\Ñ]Ô]Ð]Ð]Øð 	IØ�LŠL°HÐGÐ0˜XÐ0Ð0¨Ð0Ð0Ð0À%ÑHÔHÐHøØð +Ø×#Ò# IÑ.Ô.ÐKÐ2KˆØ* Ð*Ð*Ð*à $§)¢)¨EÑ"2Ô"2Ñ2Ð2r   c           
      ó  — |dv rdddœS t          | ¦  «        \  }}|r3t                               d|| dd…         ¦  «         t          |¦  «        S t	          ¦   «         }t          j        d¦  «        st          ¦   «         s|dk    rdddœS t          j        d	¦  «        }t          j        d
¦  «        }t          j        d¦  «        }|sM|sK|sIt          j        d¦  «        r0t          ¦   «         dk    rt          | ¦  «        \  }	}
}|	r	dd|› d�dœS dddœS dg ddœ}	 ddl
m}  || ¦  «        }n# t          $ r Y nw xY wt          | ¦  «        \  }	}}g }t          ¦   «         }|d         dv rs|                     d¦  «        pg }|r|d                              dd¦  «        nd}d|› �}t          |¦  «        }t!          ||¦  «        s|                     ||df¦  «         |	r(t!          ||¦  «        s|                     ||df¦  «         |sdddœS |dk    r¨d                     d„ |D ¦   «         ¦  «        }t'          | |¦  «        }|dk    rD|D ]\  }}}t)          ||¦  «         Œt                               d | dd!…         |¦  «         ddd|d"œS |dk    r)d                     d#„ |D ¦   «         ¦  «        }dd$|› d%�dd&œS d                     d'„ |D ¦   «         ¦  «        }|d         d         }d(„ |D ¦   «         }t-          d)„ |D ¦   «         ¦  «        }|s|�rbd}t.          5  t0                               |¦  «        }ddd¦  «         n# 1 swxY w Y   |��ÿ| |||d*œ}t3          |¦  «        }t.          5  t4                               |g ¦  «                             |¦  «         ddd¦  «         n# 1 swxY w Y   	  ||¦  «         n©# t8          $ rœ} t                               d+| ¦  «         t.          5  t4                               |g ¦  «        }!||!v r|!                     |¦  «         |!st4                               |d¦  «         ddd¦  «         n# 1 swxY w Y   dd,||d-œcY d} ~ S d} ~ ww xY wt?          ¦   «                              d.d/¦  «        }"	 tA          |"¦  «        }"n# tB          tD          f$ r d/}"Y nw xY w	 dd0l#m$}# n# t8          $ r d}#Y nw xY wtK          j&        ¦   «         }$|$tO          |"d¦  «        z   }%|$|$d1œ}&d}'	 |%tK          j&        ¦   «         z
  }(|(dk    rn;|j(         )                    tU          d2|(¦  «        ¬3¦  «        rd}'n|#� |#|&d4¦  «         ŒXt.          5  t4                               |g ¦  «        }!||!v r|!                     |¦  «         |!st4                               |d¦  «         ddd¦  «         n# 1 swxY w Y   |j+        })|'r|)�|)dk    r|'sd5nd6}*dd7|*› d8�||d-œS |D ]^\  }}}+|)d9k    s|)d:k    r|+rt)          ||¦  «         Œ%|)d:k    r3t)          ||¦  «         tY          |¦  «         t[          t\          ¦  «         Œ_ddd|d;œS t_          || |||d*œ¦  «         d|d<| |d=|› d>| › d?�d@œS ta          | || |¬A¦  «        })|)dk    rddB||d-œS |D ]^\  }}}+|)d9k    s|)d:k    r|+rt)          ||¦  «         Œ%|)d:k    r3t)          ||¦  «         tY          |¦  «         t[          t\          ¦  «         Œ_ddd|d;œS )CaC  Run all pre-exec security checks and return a single approval decision.

    Gathers findings from tirith and dangerous-command detection, then
    presents them as a single combined approval request. This prevents
    a gateway force=True replay from bypassing one check when only the
    other was shown to the user.
    rü   TNr  r  r  r  rÐ   r  r  r
  r  r«   Fr  r	  rã   r   )Úactionr  r  r   )Úcheck_command_securityr%  )ÚblockÚwarnr  Úrule_idÚunknownztirith:Úsmartr  c              3   ó"   K  — | ]
\  }}}|V — Œd S rR   r/   ©rŠ   Ú_r"  s      r   rŒ   z+check_all_command_guards.<locals>.<genexpr>ž  s(   è è € Ð)JÐ)J±:°1°d¸A¨$Ð)JÐ)JÐ)JÐ)JÐ)JÐ)Jr   râ   z'Smart approval: auto-approved '%s' (%s)rÜ   )r,   r.   Úsmart_approvedr)   c              3   ó"   K  — | ]
\  }}}|V — Œd S rR   r/   r-  s      r   rŒ   z+check_all_command_guards.<locals>.<genexpr>ª  s(   è è € Ð-NÐ-N±z°q¸$À¨dÐ-NÐ-NÐ-NÐ-NÐ-NÐ-Nr   zBLOCKED by smart approval: z@. The command was assessed as genuinely dangerous. Do NOT retry.)r,   r.   Úsmart_deniedc              3   ó"   K  — | ]
\  }}}|V — Œd S rR   r/   r-  s      r   rŒ   z+check_all_command_guards.<locals>.<genexpr>¶  s(   è è € Ð>Ð>¡z q¨$°˜dÐ>Ð>Ð>Ð>Ð>Ð>r   c                 ó   — g | ]\  }}}|‘Œ	S r/   r/   )rŠ   Úkeyr.  s      r   ú
<listcomp>z,check_all_command_guards.<locals>.<listcomp>¸  s   € Ð.Ð.Ð.™	˜˜Q �Ð.Ð.Ð.r   c              3   ó"   K  — | ]
\  }}}|V — Œd S rR   r/   )rŠ   r.  Úis_ts      r   rŒ   z+check_all_command_guards.<locals>.<genexpr>¹  s(   è è € Ð5Ð5™j˜a  D�TÐ5Ð5Ð5Ð5Ð5Ð5r   )r   r8   Úpattern_keysr)   z"Gateway approval notify failed: %sz?BLOCKED: Failed to send approval request to user. Do NOT retry.r  Úgateway_timeouti,  )Útouch_activity_if_due)Ú
last_touchrÈ   g      ð?r·   zwaiting for user approvalz	timed outzdenied by userzBLOCKED: Command z. Do NOT retry this command.r¼   r¾   )r,   r.   Úuser_approvedr)   r  u   âš ï¸� z2. Asking the user for approval.

**Command:**
```
r  r  )r¨   rÌ   z#BLOCKED: User denied. Do NOT retry.)1r*   rŸ   r    r0   rÚ   rÁ   r  r‡   rå   rG   Útools.tirith_securityr&  ÚImportErrorr   r   r#  r‘   r  rÉ   rù   r{   rö   r�   r`   r^   rM   r]   ry   rž   Úremovere   rØ   rÝ   rÞ   rß   Útools.environments.baser:  ÚtimeÚ	monotonicÚmaxrN   ÚwaitÚminrP   r“   r¦   rK   rw   rÎ   ),r   rú   rÌ   r  r  Úapproval_moder  r  Úis_askr  Ú_pkr)   r  r&  r8   Úwarningsr	   r  r)  Ú
tirith_keyÚtirith_descÚcombined_desc_for_llmÚverdictr4  r.  Úcombined_descÚprimary_keyÚall_keysÚ
has_tirithÚ	notify_cbÚapproval_datarg   Úexcro   r¸   r:  Ú_nowÚ	_deadlineÚ_activity_stateÚresolvedÚ
_remainingri   ÚreasonÚ	is_tiriths,                                               r   Úcheck_all_command_guardsr\  ;  sà
  € ð Ð@Ð@Ð@Ø ¨TÐ2Ð2Ð2õ "9¸Ñ!AÔ!AÑ€K�Øð 5Ý�ŠÐ9¸=È'ÐRVÐSVÐRVÌ-ÑXÔXÐXÝ% mÑ4Ô4Ð4õ 'Ñ(Ô(€MÝ	„yÐ#Ñ$Ô$ð 3Õ(GÑ(IÔ(Ið 3È]Ð^cÒMcÐMcØ ¨TÐ2Ð2Ð2åŒYÐ+Ñ,Ô,€FÝ”Ð3Ñ4Ô4€JÝŒYÐ(Ñ)Ô)€Fð ð 3˜*ð 3¨Vð 3åŒ9Ð*Ñ+Ô+ð 	Ý&Ñ(Ô(¨FÒ2Ð2å1IÈ'Ñ1RÔ1RÑ.�˜c ;Øð 
à$)ðKÀkð Kð Kð Kð	ð 	ð 	ð !¨TÐ2Ð2Ð2ð  '°BÀ2ÐFÐF€MðØ@Ð@Ð@Ð@Ð@Ð@Ø.Ð.¨wÑ7Ô7ˆˆøÝð ð ð Øˆðøøøõ .FÀgÑ-NÔ-NÑ*€L�+˜{ð
 €Hå)Ñ+Ô+€Kð �XÔÐ"3Ð3Ð3Ø ×$Ò$ ZÑ0Ô0Ð6°BˆØ;CÐR�(˜1”+—/’/ )¨YÑ7Ô7Ð7ÈˆØ(˜wÐ(Ð(ˆ
Ý0°Ñ?Ô?ˆÝ˜;¨
Ñ3Ô3ð 	=Ø�OŠO˜Z¨°dÐ;Ñ<Ô<Ð<àð ?Ý˜;¨Ñ4Ô4ð 	?Ø�OŠO˜[¨+°uÐ=Ñ>Ô>Ð>ð ð 3Ø ¨TÐ2Ð2Ð2ð ˜ÒÐØ $§	¢	Ð)JÐ)JÀÐ)JÑ)JÔ)JÑ JÔ JÐÝ  Ð*?Ñ@Ô@ˆØ�iÒÐà%ð 2ð 2‘	��Q˜Ý ¨SÑ1Ô1Ð1Ð1Ý�LŠLÐBØ   " œÐ'<ñ>ô >ð >à $°Ø&*Ø#8ð:ð :ð :ð ˜ÒÐØ$(§I¢IÐ-NÐ-NÀXÐ-NÑ-NÔ-NÑ$NÔ$NÐ!à!ð\Ð9Nð \ð \ð \à $ð	ð ð ð —I’IÐ>Ð>°XÐ>Ñ>Ô>Ñ>Ô>€MØ˜1”+˜a”.€KØ.Ð. XÐ.Ñ.Ô.€HÝÐ5Ð5¨HÐ5Ñ5Ô5Ñ5Ô5€Jð ð |
�Vñ |
Øˆ	Ýð 	=ð 	=Ý+×/Ò/°Ñ<Ô<ˆIð	=ð 	=ð 	=ñ 	=ô 	=ð 	=ð 	=ð 	=ð 	=ð 	=ð 	=øøøð 	=ð 	=ð 	=ð 	=ð Ñ ð
 #Ø*Ø (Ø,ð	ð ˆMõ # =Ñ1Ô1ˆEÝð Jð JÝ×*Ò*¨;¸Ñ;Ô;×BÒBÀ5ÑIÔIÐIðJð Jð Jñ Jô Jð Jð Jð Jð Jð Jð Jøøøð Jð Jð Jð JðØ�	˜-Ñ(Ô(Ð(Ð(øÝð ð ð Ý—’ÐCÀSÑIÔIÐIÝð ?ð ?Ý+×/Ò/°¸RÑ@Ô@�EØ �~�~ØŸš UÑ+Ô+Ð+Ø ð ?Ý'×+Ò+¨K¸Ñ>Ô>Ð>ð?ð ?ð ?ñ ?ô ?ð ?ð ?ð ?ð ?ð ?ð ?øøøð ?ð ?ð ?ð ?ð !&Ø`Ø#.Ø#0ð	ð ð ð ð ð ð ð øøøøðøøøõ. +Ñ,Ô,×0Ò0Ð1BÀCÑHÔHˆGðÝ˜g™,œ,��øÝ¥	Ð*ð ð ð Ø���ðøøøð-ØIÐIÐIÐIÐIÐIÐIøÝð -ð -ð -Ø(,Ð%Ð%Ð%ð-øøøõ ”>Ñ#Ô#ˆDØ�s 7¨A™œÑ.ˆIØ-1¸DÐAÐAˆOØˆHðØ&­¬Ñ)9Ô)9Ñ9�
Ø ’?�?Øð ”;×#Ò#­C°°ZÑ,@Ô,@Ð#ÑAÔAð Ø#�HØØ(Ð4Ø)Ð)Ø'Ð)Dñô ð ðõ  ð ;ð ;Ý'×+Ò+¨K¸Ñ<Ô<�Ø˜E�>�>Ø—L’L Ñ'Ô'Ð'Øð ;Ý#×'Ò'¨°TÑ:Ô:Ð:ð;ð ;ð ;ñ ;ô ;ð ;ð ;ð ;ð ;ð ;ð ;øøøð ;ð ;ð ;ð ;ð ”\ˆFØð ˜v˜~°¸6Ò1AÐ1AØ,4ÐJ˜˜Ð:J�à %ØW°6ÐWÐWÐWØ#.Ø#0ð	ð ð ð &.ð Bð BÑ!��Q˜	Ø˜YÒ&Ð&¨6°XÒ+=Ð+=À)Ð+=Ý# K°Ñ5Ô5Ð5Ð5Ø˜xÒ'Ð'Ý# K°Ñ5Ô5Ð5Ý% cÑ*Ô*Ð*Ý,Õ-@ÑAÔAÐAøð !%°Ø%)¸-ðIð Ið Iõ
 	�{ØØ&Ø$Ø(ð	%
ð %
ñ 	ô 	ð 	ð Ø&Ø)ØØ(àm˜-ÐmÐmÐ_fÐmÐmÐmð	
ð 	
ð 		
õ ' w°Ø;E°~Ø9JðLñ Lô L€Fð �ÒÐàØ<Ø&Ø(ð	
ð 
ð 	
ð &ð :ð :ÑˆˆQ�	Ø�YÒÐ 6¨XÒ#5Ð#5¸)Ð#5å˜K¨Ñ-Ô-Ð-Ð-Ø�xÒÐå˜K¨Ñ-Ô-Ð-Ý˜cÑ"Ô"Ð"Ý$Õ%8Ñ9Ô9Ð9øà¨Ø!°-ðAð Að As°   ÄD0 Ä0
D=Ä<D=ÌMÍM
ÍM
Í1/N,Î,N0Î3N0Î8O Ï
Q*Ï"Q%Ï0AQÑQ%ÑQ	ÑQ%ÑQ	Ñ	Q%ÑQ*Ñ%Q*ÒR  Ò R6Ò5R6Ò:S ÓSÓSÕ AV>Ö>W×W)F)NTNrR   )WrZ   ÚcontextvarsÚloggingrÁ   r#   rÄ   rS   rA  rB   Útypingr   Ú	getLoggerrW   rŸ   Ú
ContextVarr   rÓ   Ú__annotations__ÚTokenr   r   r   Ú_SSH_SENSITIVE_PATHÚ_HERMES_ENV_PATHÚ_PROJECT_ENV_PATHÚ_PROJECT_CONFIG_PATHÚ_SENSITIVE_WRITE_TARGETÚ_PROJECT_SENSITIVE_WRITE_TARGETÚ_COMMAND_TAILÚ_CMDPOSr"   Útupler*   r\   r0   rE   r6   r7   r   Ú_patternÚ_descriptionÚ_legacy_keyÚ_canonical_keyry   r—   r;   r    rG   ÚLockr`   rH   rI   rJ   rK   rM   r]   rl   r^   Úobjectrb   rh   rs   rÝ   rq   rt   rw   r{   r}   r�   rƒ   r…   r‡   r‘   r“   r˜   r£   r¦   rÎ   rÕ   rØ   rÚ   r¿   rå   rù   r  r#  r\  r/   r   r   ú<module>rs     s$  ððð ð ð Ð Ð Ð Ø €€€Ø 	€	€	€	Ø 	€	€	€	Ø 
€
€
€
Ø Ð Ð Ð Ø €€€Ø Ð Ð Ð Ø Ð Ð Ð Ð Ð à	ˆÔ	˜8Ñ	$Ô	$€ð 6L°[Ô5KØØð6ñ 6ô 6Ð �{Ô-¨cÔ2ð ð ñ ð8¨ð 8°Ô1BÀ3Ô1Gð 8ð 8ð 8ð 8ð
' [Ô%6°sÔ%;ð 'Àð 'ð 'ð 'ð 'ð
:ð : Sð :¸ð :ð :ð :ð :ð  >Ð ðð ð SÐ ØJÐ ðØðð àðð ð ð ð
 #UÐ):Ð"TÐ"TÐ=QÐ"TÐ"TÐ"TÐ Ø+€ðFð ð Wð JØ\à;à\ØWà>à9ð
 Ð3Ñ3Ð5MÐNØÐÑÐ!=Ð>ØÐ<Ñ<Ð>YÐZØÐ"Ñ"Ð$CÐDð+Ð ð2
 Sð 
¨Uð 
ð 
ð 
ð 
ð¨ð °ð ð ð ð ð(BØ5ðBà,ðBð >ðBð fð	Bð
 pðBð AðBð NðBð 'ðBð #ðBð .ðBð 1ðBð FðBð 4ðBð .ðBð aðBð  2ð!Bð" 0ð#Bð$ ?ð%Bð( Pð)Bð* Vð+Bð, Ið-Bð. mð/Bð0 2Ð/Ð1Ð1Ð3RÐSð1Bð2 /Ð,Ð.Ð.Ð0WÐXð3Bð4 OÐ7ÐNÐN¸}ÐNÐNÐPvÐwð5Bð6 LÐ4ÐKÐK¸MÐKÐKÐM{Ð|ð7Bð8 -ð9Bð: 9ð;Bð< -ð=BðD eðEBðF WðGBðJ OðKBðL uðMBðP mðQBðZ Yð[Bð\ _ð]Bð` CðaBðb ]Ð&EÐ\Ð\È]Ð\Ð\ð  _Bð  CðcBðd EðeBðf UðgBðl MðmBðr UðsBðt OðuBðv UðwBðx TðyBðz :ð{BðB SðCBÐ ðJI ð I¨ð Ið Ið Ið Ið
 -/Ð �d˜3  C¤˜=Ô)Ð .Ð .Ñ .Ø0ð ^ð ^Ñ€HˆlØ%Ð% hÑ/Ô/€KØ!€NØ×#Ò# N°C°C±E´EÑ:Ô:×AÒAÀ>ÐS^ÐB_Ñ`Ô`Ð`Ø×#Ò# K°°±´Ñ7Ô7×>Ò>ÀÈ^Ð?\Ñ]Ô]Ð]Ð]ð@ sð @¨s°3¬xð @ð @ð @ð @ð¨cð °cð ð ð ð ð$ cð ¨eð ð ð ð ð$ 	ˆ	ŒÑÔ€Ø€ˆ$ˆs�DˆyŒ/Ð Ð Ñ Ø$&Ð �4˜˜S˜”>Ð &Ð &Ñ &Ø˜#™%œ%€ˆs�3ŒxÐ Ð Ñ Ø˜3™5œ5Ð �SÐ  Ð  Ñ  ð*ð *ð *ð *ð *ñ *ô *ð *ð $&€��c˜4�i”Ð %Ð %Ñ %Ø)+Ð �T˜#˜v˜+Ô&Ð +Ð +Ñ +ð	.¨ð 	.°Tð 	.ð 	.ð 	.ð 	.ð
¨3ð 
°4ð 
ð 
ð 
ð 
ð 27ðð ¨#ð °sð Ø*.ðØ;>ðð ð ð ð:6 sð 6¨tð 6ð 6ð 6ð 6ð) ð )¨tð )ð )ð )ð )ðJ ð J°3ð Jð Jð Jð Jð' Sð '¨Tð 'ð 'ð 'ð 'ð+ cð +¨dð +ð +ð +ð +ð/˜sð / tð /ð /ð /ð /ð,¨ð ,°ð ,ð ,ð ,ð ,ðH¨ð Hð Hð Hð Hð
D˜Sð D¨sð D°tð Dð Dð Dð Dð- 3ð -ð -ð -ð -ð-˜Sð -ð -ð -ð -ð #ð ð ð ð ð$: sð :ð :ð :ð :ð  =AØ6:Ø04ðRð R sð R¸ð RØ/2°T©zðRà/3ðRð :=ðRð Rð Rð Rðj cð ð ð ð ð˜dð ð ð ð ð*˜Cð *ð *ð *ð *ð˜sð ð ð ð ð
 ð 
ð 
ð 
ð 
ð,˜Cð ,¨cð ,°cð ,ð ,ð ,ð ,ð` /3ð`/ð `/ Sð `/°Cð `/Ø7;ð`/ð `/ð `/ð `/ðN3¨dð 3°sð 3ð 3ð 3ð 3ð8 04ð\Að \A cð \A°Sð \AØ8<ð\Að \Að \Að \Að@	 Ð Ñ Ô Ð Ð Ð r   