
    (Gj+                       U d Z ddlm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
 ddlmZmZ ddlmZ ddlmZ ddlmZ dd	lmZmZ  ej        e          Z ed
           G d d                      Zeegef         Z edd          Zded<    ed          Zh dZ dZ!dZ"dZ#dDdZ$dEdZ%dFdZ&dGd Z'dHd$Z(dId'Z)dId(Z*dJd+Z+dId,Z,dKd/Z-dLd1Z.dMdNd5Z/dOd6Z0dPd7Z1	 	 dQdRdCZ2dS )Sa  Pre-execution ACP edit approval helpers.

This module is intentionally isolated from the generic tool registry.  ACP binds
an edit approval requester in a ContextVar for the duration of one ACP agent run;
CLI, gateway, and other sessions leave it unset and therefore bypass this guard.
    )annotationsN)TimeoutError)
ContextVarToken)	dataclass)count)Path)AnyCallableT)frozenc                  F    e Zd ZU dZded<   ded<   ded<   ded<   ded	<   d
S )EditProposalz?A proposed single-file edit that can be shown to an ACP client.str	tool_namepath
str | Noneold_textnew_textdict[str, Any]	argumentsN)__name__
__module____qualname____doc____annotations__     ?/home/rurouni/.hermes/hermes-agent/acp_adapter/edit_approval.pyr   r      sK         IINNNIIIMMMr   r   ACP_EDIT_APPROVAL_REQUESTER)defaultz(ContextVar[EditApprovalRequester | None]_EDIT_APPROVAL_REQUESTER   >   .env
.env.local.env.productionid_rsa
id_ed25519askworkspace_sessionsession	requesterEditApprovalRequester | Nonereturnr   c                6    t                               |           S )z<Bind an ACP edit approval requester for the current context.r!   set)r+   s    r   set_edit_approval_requesterr1   3   s     $''	222r   tokenNonec                :    t                               |            dS )z3Restore a previous edit approval requester binding.N)r!   reset)r2   s    r   reset_edit_approval_requesterr6   9   s     ""5)))))r   c                 :    t                               d           dS )z5Clear the current requester; primarily used by tests.Nr/   r   r   r   clear_edit_approval_requesterr8   ?   s       &&&&&r   c                 4    t                                           S N)r!   getr   r   r   get_edit_approval_requesterr<   E   s    #'')))r   r   r   r   c                    t          |                                           }|                                sd S |                                st	          d|            |                    dd          S )NzCannot edit non-file path: zutf-8replace)encodingerrors)r	   
expanduserexistsis_fileOSError	read_text)r   ps     r   _read_text_if_existsrG   I   sk    T

A88:: t99;; <:D::;;;;;	;:::r   r   r   c           	     .   t          |                     d          pd          }|st          d          |                     d          }|t          d          t          d|t	          |          t          |          t          |                     S )Nr    path requiredcontentzcontent required
write_filer   r   r   r   r   )r   r;   
ValueErrorr   rG   dict)r   r   rK   s      r   _proposal_for_write_filerP   R   s    y}}V$$*++D *)))mmI&&G+,,,%d++Wy//   r   c                N   t          |                     d          pd          }|st          d          |                     d          }|                     d          }||t          d          t          |          }|t          d|           dd	lm}  ||t          |          t          |          t          |                     d
d                              \  }}}}	|	s|dk    rt          |	pd|           t          d|||t          |                     S )Nr   rI   rJ   
old_string
new_stringz"old_string and new_string requiredzFailed to read file: r   )fuzzy_find_and_replacereplace_allFz'Could not find match for old_string in patchrM   )	r   r;   rN   rG   tools.fuzzy_matchrT   boolr   rO   )
r   r   rR   rS   r   rT   r   match_count	_strategyerrors
             r   _proposal_for_patch_replacer\   b   sT   y}}V$$*++D *)))|,,J|,,JZ/=>>>#D))H777888888888.D.DJJY]]=%0011	/ /+Hk9e  Tq  R"RD"R"RSSSy//   r   
patch_body	list[str]c                
   g }t          j        d| t           j                  D ]@}|                    d                                          }|r|                    |           At          j        d| t           j                  D ]~}|                    d                                          }|                    d                                          }|r|                    |           |r|                    |           |S )Nz/^\*\*\*\s+(?:Update|Add|Delete)\s+File:\s*(.+)$r"   z+^\*\*\*\s+Move\s+File:\s*(.+?)\s*->\s*(.+)$   )refinditer	MULTILINEgroupstripappend)r]   pathsmatchr   srcdsts         r   _extract_v4a_patch_pathsrk      s    E:
   
 {{1~~##%% 	LL6
  
 

 kk!nn""$$kk!nn""$$ 	LL 	LLLr   c           	        |                      d          }t          |t                    r|st          d          t	          |          }|st          d          t          |          dk    r|d         nd                    |          }t          |          dk    rt          |d                   nd }t          d|||t          |                     S )NrV   zpatch content requiredz no file paths found in V4A patchr"   r   z, rM   )
r;   
isinstancer   rN   rk   lenjoinrG   r   rO   )r   r]   rg   proposal_pathr   s        r   _proposal_for_patch_v4arq      s    w''Jj#&& 3j 31222$Z00E =;<<< #E

aE!HHTYYu5E5EM14Uq#E!H---dH y//	 	 	 	r   r   EditProposal | Nonec                    | dk    rt          |          S | dk    r@|                    dd          }|dk    rt          |          S |dk    rt          |          S dS )z:Return an edit proposal for supported file mutation calls.rL   rV   moder>   N)rP   r;   r\   rq   )r   r   rt   s      r   build_edit_proposalru      sp     L  '	222G}}VY//9.y9997??*95554r   rX   c                    t          |                                           j        }d |D             }d|v sd|v rdS t          |           j                                        t
          v S )Nc                6    h | ]}|                                 S r   )lower).0parts     r   	<setcomp>z2_is_sensitive_auto_approve_path.<locals>.<setcomp>   s     ...tzz||...r   z.gitz.sshT)r	   rA   partsnamerx   SENSITIVE_AUTO_APPROVE_NAMES)r   r|   lowereds      r   _is_sensitive_auto_approve_pathr      si    JJ!!##)E.....GFg--t::?  ""&BBBr   proposalpolicycwdc                   t          |pt                                                    }|t          k    st          | j                  rdS t          | j                                                                      d          }|t          k    rdS |t          k    rt          t          j                                                  d          }	 |                    |           dS # t          $ r Y nw xY w|r^t          |                                                              d          }	 |                    |           dS # t          $ r Y dS w xY wdS )zReturn whether an ACP edit proposal may bypass the prompt for this session.

    This is intentionally session-scoped and conservative: sensitive paths still
    ask even under autonomous policies.
    F)strictT)r   AUTO_APPROVE_ASKre   r   r   r	   rA   resolveAUTO_APPROVE_SESSIONAUTO_APPROVE_WORKSPACEtempfile
gettempdirrelative_torN   )r   r   r   r   tmp_rootroots         r   should_auto_approve_editr      si    ++,,2244F!!!%DX]%S%S!u))++3353AAD%%%t''' +--..66e6DD	X&&&4 	 	 	D	 	99''))111??D  &&&t   uu5s$   C( (
C54C50E 
EEc                   t                      }|dS 	 t          | |          }nO# t          $ rB}t                              d| |           t          j        dd| did          cY d}~S d}~ww xY w|dS 	 t           ||                    }n4# t          $ r'}t                              d|           d}Y d}~nd}~ww xY w|rdS t          j        dd	id          S )
zRun ACP edit approval if bound.

    Returns a JSON tool-error string when the edit must be blocked, otherwise
    ``None`` so dispatch can continue.  Requester exceptions deny by default.
    Nz5Could not build ACP edit approval proposal for %s: %sr[   z.Edit approval denied: could not prepare diff ()F)ensure_asciiz&ACP edit approval requester failed: %sz:Edit approval denied by ACP client; file was not modified.)r<   ru   	ExceptionloggerwarningjsondumpsrX   )r   r   r+   r   excapproveds         r   maybe_require_edit_approvalr      s=    ,--Itr&y)<< r r rNPY[^___z7$[UX$[$[$[\kpqqqqqqqqqr t		(++,,   ?EEE  t:w \]lqrrrrs2   % 
A17A,&A1,A19B 
CB>>Cc                    ddl }dt          t                     }|                    |d| j         dd|                    | j        | j        | j                  g| j        | j	        d	          S )
z<Build the ToolCallUpdate payload for ACP request_permission.r   Nzedit-approval-zApprove edit: editpending)r   r   r   )toolr   )titlekindstatusrK   	raw_input)
acpnext_PERMISSION_REQUEST_IDSupdate_tool_callr   tool_diff_contentr   r   r   r   )r   r   tool_call_ids      r   build_acp_edit_tool_callr     s     JJJCD)@$A$ACCL.x}..!!]!*!* "  
 $-H<NOO     r         N@request_permission_fnr   loopasyncio.AbstractEventLoop
session_idtimeoutfloatauto_approve_getter+Callable[[], tuple[str, str | None]] | NoneEditApprovalRequesterc                $     d fd}|S )zGReturn a sync requester that bridges edit proposals to ACP permissions.r   r   r-   rX   c                   ddl m} ddlm} o	              \  }}t	          | ||          r#t
                              d|| j                   dS n,# t          $ r t
          	                    dd           Y nw xY w |ddd	
           |ddd
          g}t          |           } ||          } ||t
          d          }|dS 	 |                              }	nN# t          t          f$ r:}
|                                 t
                              d|
           Y d }
~
dS d }
~
ww xY wt          |	dd           }t          |dd           dk    ot          |dd           dk    S )Nr   )PermissionOption)safe_schedule_threadsafez*Auto-approved ACP edit under policy %s: %sTz*ACP edit auto-approval policy check failed)exc_info
allow_oncez
Allow edit)	option_idr   r}   denyreject_onceDeny)r   	tool_calloptionsz1Edit approval request: failed to schedule on loop)r   log_messageF)r   z-Edit approval request timed out or failed: %soutcomeselectedr   )
acp.schemar   agent.async_utilsr   r   r   infor   r   debugr   resultFutureTimeoutcancelr   getattr)r   r   r   r   r   r   r   corofutureresponser   r   r   r   r   r   r   s               r   
_requesterz4make_acp_edit_approval_requester.<locals>._requester'  s   //////>>>>>>*Z1133+HfcBB  KK LfV^Vcddd4   Z Z ZITXYYYYYZ |,\ZZZvMOOO
 -X66	$$!
 
 

 *)K	
 
 
 >5	}}W}55HHy) 	 	 	MMOOONNJCPPP55555	 (It44GY--; Dd33|C	
s)   ?A &A<;A<C( (D39/D..D3)r   r   r-   rX   r   )r   r   r   r   r   r   s   ````` r    make_acp_edit_approval_requesterr     sB    )
 )
 )
 )
 )
 )
 )
 )
 )
 )
V r   )r+   r,   r-   r   )r2   r   r-   r3   )r-   r3   )r-   r,   )r   r   r-   r   )r   r   r-   r   )r]   r   r-   r^   )r   r   r   r   r-   rr   )r   r   r-   rX   r:   )r   r   r   r   r   r   r-   rX   )r   r   r   r   r-   r   )r   r   )r   N)r   r   r   r   r   r   r   r   r   r   r-   r   )3r   
__future__r   asyncior   loggingra   r   concurrent.futuresr   r   contextvarsr   r   dataclassesr   	itertoolsr   pathlibr	   typingr
   r   	getLoggerr   r   r   rX   r   r!   r   r   r~   r   r   r   r1   r6   r8   r<   rG   rP   r\   rk   rq   ru   r   r   r   r   r   r   r   r   <module>r      s     # " " " " "    				  < < < < < < ) ) ) ) ) ) ) ) ! ! ! ! ! !                            		8	$	$ $        !,!56 EOZ!F F F       %((   a``  ,   3 3 3 3* * * *' ' ' '* * * *; ; ; ;       B   0   .   C C C C    Bs s s s>   4 GK4 4 4 4 4 4 4r   