
    `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#d&dZ$d'dZ%d(dZ&d)dZ'd*dZ(d+dZ)d+dZ*d,dZ+d+dZ,d-d Z-d.d!Z.d/d0d"Z/d1d#Z0d2d$Z1	 	 d3	 	 	 	 	 	 	 	 	 	 	 d4d%Z2y)5a  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                  D    e Zd ZU dZded<   ded<   ded<   ded<   ded	<   y
)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__     L/root/.hermes/venv/lib/python3.12/site-packages/acp_adapter/edit_approval.pyr   r      s     IN
IM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sessionc                ,    t         j                  |       S )z<Bind an ACP edit approval requester for the current context.r!   set)	requesters    r   set_edit_approval_requesterr/   3   s     $''	22r   c                .    t         j                  |        y)z3Restore a previous edit approval requester binding.N)r!   reset)tokens    r   reset_edit_approval_requesterr3   9   s     ""5)r   c                 .    t         j                  d       y)z5Clear the current requester; primarily used by tests.Nr,   r   r   r   clear_edit_approval_requesterr5   ?   s       &r   c                 *    t         j                         S N)r!   getr   r   r   get_edit_approval_requesterr9   E   s    #''))r   c                    t        |       j                         }|j                         sy |j                         st	        d|        |j                  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_existsrD   I   sO    T
A88:99;3D6:;;;;	;::r   c           	         t        | j                  d      xs d      }|st        d      | j                  d      }|t        d      t        d|t	        |      t        |      t        |             S )Nr    path requiredcontentzcontent required
write_filer   r   r   r   r   )r   r8   
ValueErrorr   rD   dict)r   r   rH   s      r   _proposal_for_write_filerM   R   sp    y}}V$*+D))mmI&G+,,%d+Wy/ r   c                   t        | j                  d      xs d      }|st        d      | j                  d      }| j                  d      }||t        d      t        |      }|t        d|       dd	lm}  ||t        |      t        |      t        | j                  d
d                  \  }}}}	|	s|dk(  rt        |	xs d|       t        d|||t        |             S )Nr   rF   rG   
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 patchrJ   )	r   r8   rK   rD   tools.fuzzy_matchrQ   boolr   rL   )
r   r   rO   rP   r   rQ   r   match_count	_strategyerrors
             r   _proposal_for_patch_replacerY   b   s    y}}V$*+D))|,J|,JZ/=>>#D)H07888.DJJY]]=%01	/+Hk9e q R$KD6"RSSy/ r   c                   g }t        j                  d| t         j                        D ]5  }|j                  d      j	                         }|s%|j                  |       7 t        j                  d| t         j                        D ]g  }|j                  d      j	                         }|j                  d      j	                         }|r|j                  |       |sW|j                  |       i |S )Nz/^\*\*\*\s+(?:Update|Add|Delete)\s+File:\s*(.+)$r"   z+^\*\*\*\s+Move\s+File:\s*(.+?)\s*->\s*(.+)$   )refinditer	MULTILINEgroupstripappend)
patch_bodypathsmatchr   srcdsts         r   _extract_v4a_patch_pathsrg      s    E:
 
 {{1~##%LL 6
 

 kk!n""$kk!n""$LLLL
 Lr   c           	     D   | j                  d      }t        |t              r|st        d      t	        |      }|st        d      t        |      dk(  r|d   ndj                  |      }t        |      dk(  rt        |d         nd }t        d|||t        |             S )NrS   zpatch content requiredz no file paths found in V4A patchr"   r   z, rJ   )
r8   
isinstancer   rK   rg   lenjoinrD   r   rL   )r   rb   rc   proposal_pathr   s        r   _proposal_for_patch_v4arm      s    w'Jj#&j122$Z0E;<< #E
aE!HTYYu5EM14Uq#E!H-dH y/	 	r   c                    | dk(  rt        |      S | dk(  r2|j                  dd      }|dk(  rt        |      S |dk(  rt        |      S y)z:Return an edit proposal for supported file mutation calls.rI   rS   moder;   N)rM   r8   rY   rm   )r   r   ro   s      r   build_edit_proposalrp      sW     L '	22G}}VY/9.y997?*955r   c                    t        |       j                         j                  }|D ch c]  }|j                          }}d|v sd|v ryt        |       j                  j                         t
        v S c c}w )Nz.gitz.sshT)r	   r>   partslowernameSENSITIVE_AUTO_APPROVE_NAMES)r   rr   partlowereds       r   _is_sensitive_auto_approve_pathrx      sg    J!!#))E(-.tzz|.G.Fg-:??  "&BBB /s   A3c                J   t        |xs t              j                         }|t        k(  st        | j                        ryt        | j                        j                         j                  d      }|t        k(  ry|t        k(  r@t        t        j                               j                  d      }	 |j                  |       yy# t        $ r Y nw xY w|st        |      j                         j                  d      }	 |j                  |       y# t        $ r Y yw xY w)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_ASKr`   rx   r   r	   r>   resolveAUTO_APPROVE_SESSIONAUTO_APPROVE_WORKSPACEtempfile
gettempdirrelative_torK   )proposalpolicycwdr   tmp_rootroots         r   should_auto_approve_editr      s    ++,224F!!%DX]]%S))+3353AD%%'' ++-.66e6D	X&   		9'')111?D  & s$   5C 	CCD 	D"!D"c                   t               }|y	 t        | |      }|y	 t         ||            }|ryt        j                  dd	id      S # t        $ r>}t        j	                  d| |       t        j                  dd| did      cY d}~S d}~ww xY w# t        $ r"}t        j	                  d|       d}Y d}~d}~ww xY w)
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: %srX   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.)r9   rp   	ExceptionloggerwarningjsondumpsrU   )r   r   r.   r   excapproveds         r   maybe_require_edit_approvalr      s     ,-Ir&y)<
 	(+,
 ::w \]lqrr  rNPY[^_zz7&TUXTYYZ$[\kpqqr  ?Es4   A B 	B3BBB	CB<<Cc                   ddl }dt        t               }|j                  |d| j                   dd|j                  | 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statusrH   	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     #D)@$A#BCLx}}o.!!]]!**!** " 
 $--H<N<NO    r   c                $     d fd}|S )zGReturn a sync requester that bridges edit proposals to ACP permissions.c                h   ddl m} ddlm} ;	         \  }}t	        | ||      r"t
        j                  d|| j                         y	  |ddd	
       |ddd
      g}t        |       } ||      } ||t
        d      }|y	 |j                        }	t        |	dd       }t        |dd       dk(  xr t        |dd       dk(  S # t        $ r t
        j                  dd       Y w xY w# t        t        f$ r0}
|j                          t
        j                  d|
       Y d }
~
yd }
~
ww xY w)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   rt   denyreject_onceDeny)
session_id	tool_calloptionsz1Edit approval request: failed to schedule on loop)r   log_messageF)timeoutz-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   auto_approve_getterlooprequest_permission_fnr   r   s               r   
_requesterz4make_acp_edit_approval_requester.<locals>._requester'  sX   />*Z13+HfcBKK LfV^VcVcd C |,\ZvMO
 -X6	$!

 *K	
 >	}}W}5H
 (It4GY-; Dd3|C	
9  ZITXYZ. y) 	MMONNJCP	s)   8C C2  C/.C/2D1&D,,D1)r   r   returnrU   r   )r   r   r   r   r   r   s   ````` r    make_acp_edit_approval_requesterr     s    )
 )
V r   )r.   EditApprovalRequester | Noner   r   )r2   r   r   None)r   r   )r   r   )r   r   r   r   )r   r   r   r   )rb   r   r   z	list[str])r   r   r   r   r   zEditProposal | None)r   r   r   rU   r7   )r   r   r   r   r   r   r   rU   )r   r   r   r   r   r   )r   r   )g      N@N)r   r   r   zasyncio.AbstractEventLoopr   r   r   floatr   z+Callable[[], tuple[str, str | None]] | Noner   EditApprovalRequester)3r   
__future__r   asyncior   loggingr\   r   concurrent.futuresr   r   contextvarsr   r   dataclassesr   	itertoolsr   pathlibr	   typingr
   r   	getLoggerr   r   r   rU   r   r!   r   r   ru   r{   r~   r}   r/   r3   r5   r9   rD   rM   rY   rg   rm   rp   rx   r   r   r   r   r   r   r   <module>r      sE   #    	  < ) !    			8	$ $   !,!56 EO!F B   (   a  ,   3*'*; B0.CBs>4 GK4#4
#4 4 	4
 E4 4r   