+
    iڕ                       a  Rs t>0 t R t^ RIt^ RIt^ RIt^ RIt^ RIt^ RIHt ^ RI	H
t
 ^ RIHt ]P                  ! ]4      t]P                   ]P"                  ]P$                  0tRtRs] ^ k R R ltRt]! 0 Rtm4      tR	 R
 ltRutRR0tR R ltR R lt]P>                  ! 4       t / t!] ^k ]P>                  ! 4       t"/ t#] ^k RvR R llt$RwR R llt%RxR R llt&RvR R llt'RwR R llt(RwR R llt)RvR R  llt*R! R" lt+R# R$ lt,RvR% R& llt-RyR) R* llt.RzR- R. llt/R/R0R1]&/R/R2R1]-/R/R3R1]./R/R4R1]//.t0R5 t1^ R6I2H3t3 R7 t4R/R0R8R9R:R;R<R=R>R;R?R8R@/RAR;RBR8RCR^RD^/RER;RBR8RFRRRGRH//RIR>.//t5R/R2R8RJR:R;R<R=R>R;R?R8RK/R+R;R?R8RL//RIR>R+.//t6R/R3R8RMR:R;R<R=RNR;R?ROR'R3.R8RPRR'/R>R;R?R8RQ/RRR;R?R8RS/RTR;R?R8RU/RVR;RWR8RXRR(/R3R;R?R8RY//RIRN.//t7R/R4R8RZR:R;R<R=R[R;R?R8R\/R]R;R?ROR+R^.R8R_RR+/R>R;R?R8R`RR,/RaR;R?R8Rb/RER;RBR8RcR^2/RAR;RBR8RdR^ /ReR;R?RO. R{OR8RfRR+/RgR;RBR8RhR^ //RIR[.//t8Ri t9Rj t:Rk t;Rl t<]3Pz                  ! R0Rm]5]9]4RnRo7       ]3Pz                  ! R2Rm]6]:]4RpRo7       ]3Pz                  ! R3Rm]7];]4RqRo7       ]3Pz                  ! R4Rm]8]<]4RrRo7       R# )|z6File Tools Module - LLM agent file manipulation tools.N)Path)ShellFileOperations)redact_sensitive_texti c                $    V ^8  d   QhR\         /# )   return)int)formats   "-/home/ubuntu/hermes-agent/tools/file_tools.py__annotate__r      s     " "S "    c                    \         e   \         #  ^ RIHp  V ! 4       pVP                  R4      p\	        V\
        \        34      '       d   V^ 8  d   \        V4      s \         # \        s \         #   \         d     \        s \         # i ; i)zReturn the configured max characters per file read.

Reads ``file_read_max_chars`` from config.yaml on first call, caches
the result for the lifetime of the process.  Falls back to the
built-in default if the config is missing or invalid.
)load_configfile_read_max_chars)	_max_read_chars_cachedhermes_cli.configr   get
isinstancer   float	Exception_DEFAULT_MAX_READ_CHARS)r   cfgvals      r
   _get_max_read_charsr      s     )%%1mgg+,cC<((S1W%(X")) 5!!  4!!s   AA. .BBi  c                0    V ^8  d   QhR\         R\        /# )r   filepathr   strbool)r	   s   "r
   r   r   I   s        r   c                    \         P                  P                  V 4      pV\        9   d   R# VP	                  R4      '       d   VP                  R4      '       d   R# R# )u3  Return True if the path would hang the process (infinite output or blocking input).

Uses the *literal* path — no symlink resolution — because the model
specifies paths directly and realpath follows symlinks all the way
through (e.g. /dev/stdin → /proc/self/fd/0 → /dev/pts/0), defeating
the check.
Tz/proc/F)z/fd/0z/fd/1z/fd/2)ospath
expanduser_BLOCKED_DEVICE_PATHS
startswithendswith)r   
normalizeds   & r
   _is_blocked_devicer'   I   sR     ##H-J**X&&:+>+>#, , r   z/var/run/docker.sockz/run/docker.sockc                >    V ^8  d   QhR\         R\         R,          /# )r   r   r   Nr   )r	   s   "r
   r   r   b   s      C C$J r   c                ,    \         P                  P                  \         P                  P                  V 4      4      p\         F"  pVP                  V4      '       g   K  RV  R2u # 	  V\        9   d   RV  R2# R#   \        \
        3 d    T p LTi ; i)zHReturn an error message if the path targets a sensitive system location.z,Refusing to write to sensitive system path: zD
Use the terminal tool with sudo if you need to modify system files.N)	r    r!   realpathr"   OSError
ValueError_SENSITIVE_PATH_PREFIXESr$   _SENSITIVE_EXACT_PATHS)r   resolvedprefixs   &  r
   _check_sensitive_pathr2   b   s    77##BGG$6$6x$@A +v&&>xj IV V + )):8* ER R	
  Z  s   <A= =BBc                0    V ^8  d   QhR\         R\        /# )r   excr   )r   r   )r	   s   "r
   r   r   v   s      i D r   c                    \        V \        4      '       d   R# \        V \        4      '       d   V P                  \        9   d   R# R# )zFReturn True for expected write denials that should not hit error logs.TF)r   PermissionErrorr,   errno_EXPECTED_WRITE_ERRNOS)r4   s   &r
   _is_expected_write_exceptionr9   v   s1    #''#wCII1G$Gr   defaultc                0    V ^8  d   QhR\         R\        /# r   task_idr   )r   r   )r	   s   "r
   r   r      s     v v3 v/B vr   c                   ^ RI HpHpHpHpHpHpHpHp ^ RI	p	\        ;_uu_ 4        \        P                  V 4      p
RRR4       X
ee   T;_uu_ 4        W9   d   V	P                  4       WP&   V
uuRRR4       # \        ;_uu_ 4        \        P                  V R4       RRR4       RRR4       T;_uu_ 4        W9  d   \        P                  ! 4       Wp&   Wp,          pRRR4       X;_uu_ 4        T;_uu_ 4        W9   d   V	P                  4       WP&   W,          pMRpRRR4       XEfm   ^ RI Hp V! 4       pVR,          pVP                  V / 4      pVR8X  d$   VP                  R4      ;'       g
    VR,          pMVR8X  d$   VP                  R4      ;'       g
    VR,          pMVVR	8X  d$   VP                  R
4      ;'       g
    VR
,          pM,VR8X  d$   VP                  R4      ;'       g
    VR,          pMRpVP                  R4      ;'       g
    VR,          p\"        P%                  RWR,          4       RpVR+9   d]   RVP                  R^4      RVP                  RR4      RVP                  RR4      RVP                  RR4      RVP                  R. 4      /pRpVR8X  d]   RVP                  RR4      RVP                  RR4      RVP                  R^4      R VP                  R!R4      R"VP                  R#R$4      /pRpVR%8X  d   R"VP                  R&R$4      /pV! VVVVR',          VVVV VP                  R(4      R)7	      pV;_uu_ 4        WV &   V	P                  4       WP&   RRR4       V! 4        \"        P%                  R*WR,          4       RRR4       \'        X4      p\        ;_uu_ 4        V\        V &   RRR4       V#   + '       g   i     EL; i  + '       g   i     ELL; i  + '       g   i     ELX; i  + '       g   i     EL4; i  + '       g   i     EL; i  + '       g   i     L; i  + '       g   i     L; i  + '       g   i     T# ; i),a  Get or create ShellFileOperations for a terminal environment.

Respects the TERMINAL_ENV setting -- if the task_id doesn't have an
environment yet, creates one using the configured backend (local, docker,
modal, etc.) rather than always defaulting to local.

Thread-safe: uses the same per-task creation locks as terminal_tool to
prevent duplicate sandbox creation from concurrent tool calls.
)_active_environments	_env_lock_create_environment_get_env_config_last_activity_start_cleanup_thread_creation_locks_creation_locks_lockN)_task_env_overridesenv_typedockerdocker_imagesingularitysingularity_imagemodalmodal_imagedaytonadaytona_image cwdz*Creating new %s environment for task %s...:N   Ncontainer_cpucontainer_memoryi   container_diski   container_persistentTdocker_volumessshhostssh_hostuserssh_userportssh_portkeyssh_key
persistentssh_persistentFlocallocal_persistenttimeouthost_cwd)	rH   imagerR   rf   
ssh_configcontainer_configlocal_configr=   rg   z %s environment ready for task %s)rI   rK   rM   rO   )tools.terminal_toolr?   r@   rA   rB   rC   rD   rE   rF   time_file_ops_lock_file_ops_cacher   pop	threadingLockrG   loggerinfor   )r=   r?   r@   rA   rB   rC   rD   rE   rF   rm   cached	task_lockterminal_envrG   configrH   	overridesrh   rR   rj   ri   rk   file_opss   &                      r
   _get_file_opsr{      s       
 $$W- 
Y.*.))+' Y $^#''6 $  
	)'0~~'7O$#,	 

 
Y.*.))+'3<#  ?$&Fj)H+//<I8#!n5OO9O]*!&9:YYfEX>YW$!m4MM}8MY&!o6QQ&:Q--&77&-CKKDhXZP[\#HH#VZZ%C&

3Et(L$fjj1A5&I*FJJ7Mt,T$fjj1A2&F$  J5 FJJz26FJJz26FJJz266::i4 &**-=u"E
  L7" &**-?"G  /!y)%!1)J/
L 0<W-*.))+'  "#KK:HbkRS 
X #<0H	#+  
OE 
 $^^ YY 
		 YYD I 
Z 
Os   OO48O4O 	O47&P/Q:$P	AQ2)Q)Q)Q0$QD?QP0	+,Q8
QO	 O1+	O44P	P	P-'	Q0Q ;QQ	Q'	c                $    V ^8  d   QhR\         /# r   r=   r)   )r	   s   "r
   r   r     s     $ $# $r   c                    \         ;_uu_ 4        V '       d   \        P                  V R4       M\        P                  4        RRR4       R#   + '       g   i     R# ; i)z Clear the file operations cache.N)rn   ro   rp   clearr=   s   &r
   clear_file_ops_cacher     s2    	.!!#	 
   4AA	  c          
      T    V ^8  d   QhR\         R\        R\        R\         R\         /# )r   r!   offsetlimitr=   r   r   r   )r	   s   "r
   r   r     s>     \A \A \Ac \Ac \A# \A^a \Ar   c                    \        V 4      '       d   \        P                  ! RRV  R2/4      # ^ RIp^ RIHp VP                  V 4      P                  4       P                  4       pV! 4       P                  4       pVR,          R,          R,          VR,          R,          .pV F1  p	 VP                  V	4       \        P                  ! RR	V  R
2/4      u # 	  \        V4      p
WV3p\        ;_uu_ 4        \        P                  VRRR^ R\        4       R/ /4      pVP!                  R/ 4      P!                  V4      pRRR4       XeF    \"        P$                  P'                  V
4      pW8X  d   \        P                  ! RRRV RR/RR7      #  \+        V4      pVP-                  WV4      pVP/                  4       p\1        VP2                  ;'       g    R4      pVP!                  R^ 4      p\5        4       pVV8  d?   VP!                  RR4      p\        P                  ! RRVR RVR RV R2RV RVRV/RR7      # VP2                  '       d*   \7        VP2                  4      Vn        VP2                  VR&   V'       dA   V\8        8  d6   V^8  d/   VP!                  R4      '       d   VP                  RR VR R!24       R"WV3p\        ;_uu_ 4        RX9  d   / VR&   VR,          P;                  WV34       VR,          V8X  d   VR;;,          ^,          uu&   M
VVR&   ^VR&   VR,          p \"        P$                  P'                  V
4      pVVR,          V&   VVP                  R#/ 4      V
&   RRR4       X^8  d#   \        P                  ! RR$V R%2RV R&V/RR7      # V^8  d
   R'V R(2VR)&   \        P                  ! VRR7      #   \         d     EK5  i ; i  + '       g   i     EL; i  \(         d     ELpi ; i  \(         d     Li ; i  + '       g   i     L; i  \<         d.   p\        P                  ! R\        T4      /RR7      u Rp?# Rp?ii ; i)*z-Read a file with pagination and line numbers.errorzCannot read 'zE': this is a device file that would block or produce infinite output.N)get_hermes_homeskillsz.hubzindex-cachezAccess denied: z is an internal Hermes cache file and cannot be read directly to prevent prompt injection. Use the skills_list or skill_view tools instead.last_keyconsecutiveread_historydedupcontentu   File unchanged since last read. The content from the earlier read_file result in this conversation is still current — refer to that instead of re-reading.r!   TFensure_asciirQ   	file_sizetotal_linesunknownzRead produced ,z, characters which exceeds the safety limit (zD chars). Use offset and limit to read a smaller range. The file has z lines total.	truncated_hintzThis file is large (zj bytes). Consider reading only the section you need with offset and limit to keep context usage efficient.readread_timestampsz.BLOCKED: You have read this exact file region z| times in a row. The content has NOT changed. You already have this information. STOP re-reading and proceed with your task.already_readz%You have read this exact file region z times consecutively. The content has not changed since your last read. Use the information you already have. If you are stuck in a loop, stop reading and proceed with writing or responding._warning)r'   jsondumpspathlibhermes_constantsr   r   r"   resolverelative_tor-   r   _read_tracker_lock_read_tracker
setdefaultsetr   r    r!   getmtimer,   r{   	read_fileto_dictlenr   r   r   _LARGE_FILE_HINT_BYTESaddr   )r!   r   r   r=   _pathlib_get_hh	_resolved_hermes_home_blocked_dirs_blockedresolved_str	dedup_key	task_datacached_mtimecurrent_mtimerz   resultresult_dictcontent_lenr   	max_charsr   read_keycount
_mtime_nowes   &&&&                      r
   read_file_toolr     s   ZA d##::#D6 *8 8   	#?MM$'224<<>	y((*8#f,}<8#f,
 &H
%%h/zz)$ 0K K#   &" 9~!51	%00D-w; I %=="599)DL   # " 0 0 > 0::!U ' %*+ + 1 !)##D%8nn& &....B/OOK3	')	"%//-CK::$[O 4))21 6$$/=?
 {Y
 "
# 
# >>>26>>BFN%+^^K	" )&<<CKOOK00""7&ym 43 3 D%0i'%'	'"n%))4*?@$0-(A-((0	*%+,	-(m,EWW--l;
0:	'"9-LV	$$%6;LI'  . A:::DUG LB B  "# # aZ7w ?c c 
# zz+E::q    (  L  )  T  Azz7CF+%@@As   ,P' A8P' (,O
P' P' 6AO>P' AO1 B-P' <P' 0P' ?(P' (*P' AP1A P10P' "'P' 
OP' OP' O.	(	P' 1P <P' ?P  P' PPPPP$	P' 'Q2"QQQc                0    V ^8  d   QhR\         R\        /# r<   )r   list)r	   s   "r
   r   r     s     
 
C 
 
r   c           
        \         ;_uu_ 4        \        P                  V / 4      pVP                  R\        4       4      p/ pV F:  w  rEpWC9  d   . W4&   W4,          P	                  RV RWV,           ^,
           24       K<  	  \        VP                  4       4       UUu. uF  w  rxRVRV/NK  	  uppuuRRR4       # u uppi   + '       g   i     R# ; i)zReturn a list of files read in this session for the given task.

Used by context compression to preserve file-read history across
compression boundaries.
r   zlines -r!   regionsN)r   r   r   r   appendsorteditems)	r=   r   r   
seen_pathsr!   r   r   pr   s	   &        r
   get_read_files_summaryr     s     
	!%%gr2	 }}^SU;
%1!T5%#%
 ##fVHAfnq6H5I$JK &2 %Z%5%5%78
8
 Q	7+8
 
	
 
		s   BCB=
/C=CC	c                $    V ^8  d   QhR\         /# r}   r)   )r	   s   "r
   r   r     s     " " "r   c                    \         ;_uu_ 4        V '       d   \        P                  V R4       M\        P                  4        RRR4       R#   + '       g   i     R# ; i)zClear the read tracker.

Call with a task_id to clear just that task, or without to clear all.
Should be called when a session is destroyed to prevent memory leaks
in long-running gateway processes.
N)r   r   rp   r   r   s   &r
   clear_read_trackerr     s8     
	gt,!	 
			r   c                $    V ^8  d   QhR\         /# r}   r)   )r	   s   "r
   r   r     s     / /c /r   c                Z   \         ;_uu_ 4        V '       d=   \        P                  V 4      pV'       d   RV9   d   VR,          P                  4        M;\        P	                  4        F#  pRV9   g   K  VR,          P                  4        K%  	  RRR4       R#   + '       g   i     R# ; i)u  Clear the deduplication cache for file reads.

Called after context compression — the original read content has been
summarised away, so the model needs the full content if it reads the
same file again.  Without this, reads after compression would return
a "file unchanged" stub pointing at content that no longer exists in
context.

Call with a task_id to clear just that task, or without to clear all.
r   N)r   r   r   r   valuesr=   r   s   & r
   reset_file_dedupr     st     
	%))'2IW	1'"((**113	i'g&,,. 4 
			s   %B:B4BB*	c                $    V ^8  d   QhR\         /# r}   r)   )r	   s   "r
   r   r     s     ) )C )r   c                    \         ;_uu_ 4        \        P                  V 4      pV'       d   RVR&   ^ VR&   RRR4       R#   + '       g   i     R# ; i)uu  Reset consecutive read/search counter for a task.

Called by the tool dispatcher (model_tools.py) whenever a tool OTHER
than read_file / search_files is executed.  This ensures we only warn
or block on *truly consecutive* repeated reads — if the agent does
anything else in between (write, patch, terminal, etc.) the counter
resets and the next read is treated as fresh.
Nr   r   )r   r   r   r   s   & r
   notify_other_tool_callr     s@     
	!%%g.	$(Ij!'(Im$	 
			s   (AA	c                4    V ^8  d   QhR\         R\         RR/# r   r   r=   r   Nr)   )r	   s   "r
   r   r     s&     R RS R3 R4 Rr   c                    \        \        V 4      P                  4       P                  4       4      p\        P
                  P                  V4      p\        ;_uu_ 4        \        P                  T4      pTe   Y4P                  R/ 4      T&   RRR4       R#   \        \        3 d     R# i ; i  + '       g   i     R# ; i)u  Record the file's current modification time after a successful write.

Called after write_file and patch so that consecutive edits by the
same task don't trigger false staleness warnings — each write
refreshes the stored timestamp to match the file's new state.
Nr   )r   r   r"   r   r    r!   r   r,   r-   r   r   r   r   )r   r=   r0   r   r   s   &&   r
   _update_read_timestampr     s    tH~002::<=((2 
	!%%g.	 DQ  !2B7A 
	 Z  			s   AB .B/B,+B,/C 	c                J    V ^8  d   QhR\         R\         R\         R,          /# r   r)   )r	   s   "r
   r   r     s%      C # #* r   c                    \        \        V 4      P                  4       P                  4       4      p\        ;_uu_ 4        \        P                  T4      pT'       g    RRR4       R# TP                  R/ 4      P                  T4      pRRR4       Xf   R#  \        P                  P                  T4      pYT8w  d   RT  R2# R#   \        \
        3 d     R# i ; i  + '       g   i     L\; i  \         d     R# i ; i)u
  Check whether a file was modified since the agent last read it.

Returns a warning string if the file is stale (mtime changed since
the last read_file call for this task), or None if the file is fresh
or was never read.  Does not block — the write still proceeds.
Nr   z	Warning: z was modified since you last read it (external edit or concurrent agent). The content you read may be stale. Consider re-reading the file to verify before writing.)r   r   r"   r   r,   r-   r   r   r   r    r!   r   )r   r=   r0   r   
read_mtimer   s   &&    r
   _check_file_stalenessr     s    tH~002::<= 
	!%%g.	 
	 ]]#4b9==hG
	 

 ((2 "z "L L	

 ' Z  		  s5   0C  C)!CC1 CCC.	1D ?D c                H    V ^8  d   QhR\         R\         R\         R\         /# )r   r!   r   r=   r   r)   )r	   s   "r
   r   r   /  s.     A A# A Ac A# Ar   c                p   \        V 4      pV'       d   \        P                  ! RV/RR7      #  \        W4      p\	        V4      pVP                  W4      pVP                  4       pV'       d   WGR&   \        W4       \        P                  ! VRR7      #   \         d   p\        T4      '       d,   \        P                  R\        T4      P                  T4       M,\        P                  R\        T4      P                  TRR7       \        P                  ! R\        T4      /RR7      u R	p?# R	p?ii ; i)
zWrite content to a file.r   Fr   r   z"write_file expected denial: %s: %szwrite_file error: %s: %sT)exc_infoN)r2   r   r   r   r{   
write_filer   r   r   r9   rs   debugtype__name__r   r   )	r!   r   r=   sensitive_errstale_warningrz   r   r   r   s	   &&&      r
   write_file_toolr   /  s    )$/Mzz7M2GGA-d< )$$T3nn&&3
# 	t-zz+E:: A'**LL=tAw?O?OQRSLL3T!W5E5EqSWLXzz7CF+%@@As   A%B D5 B
D0*D50D5replaceFc                x    V ^8  d   QhR\         R\         R\         R\         R\        R\         R\         R\         /# )	r   moder!   
old_string
new_stringreplace_allpatchr=   r   r   )r	   s   "r
   r   r   G  sS     6A 6AS 6AC 6AC 6A6A486AIL6A6A,/6Ar   c                >   . pV'       d   VP                  V4       V R8X  d^   V'       dV   ^ RIpVP                  RWXP                  4       F1  p	VP                  V	P	                  ^4      P                  4       4       K3  	  V F2  p
\        V
4      pV'       g   K  \        P                  ! RV/RR7      u # 	   . pV F)  p
\        W4      pV'       g   K  VP                  V4       K+  	  \        V4      pV R8X  dT   V'       g   \        P                  ! RR/4      # Ve   Vf   \        P                  ! RR	/4      # VP                  WW44      pMTV R8X  d3   V'       g   \        P                  ! RR
/4      # VP                  V4      pM\        P                  ! RRV  2/4      # VP                  4       pV'       d.   \        V4      ^8X  d
   V^ ,          MRP                  V4      VR&   VP!                  R4      '       g   V F  p
\#        W4       K  	  \        P                  ! VRR7      pVP!                  R4      '       d!   R\%        VR,          4      9   d
   VR,          pV#   \&         d.   p\        P                  ! R\%        T4      /RR7      u Rp?# Rp?ii ; i)z4Patch a file using replace mode or V4A patch format.r   Nz/^\*\*\*\s+(?:Update|Add|Delete)\s+File:\s*(.+)$r   Fr   r   zpath requiredz"old_string and new_string requiredzpatch content requiredzUnknown mode: z | r   zCould not findzp

[Hint: old_string not found. Use read_file to verify the current content, or search_files to locate the text.])r   refinditer	MULTILINEgroupstripr2   r   r   r   r{   patch_replace	patch_v4ar   r   joinr   r   r   r   )r   r!   r   r   r   r   r=   _paths_to_check_re_m_pr   stale_warnings_swrz   r   r   result_jsonr   s   &&&&&&&            r
   
patch_toolr   G  s>   
 Ot$w5,,QSXZgZghB""288A;#4#4#67 i-b1=::w6UKK &A!B'4Cs%%c* "
 !)9zz7O"<==!Z%7zz7,P"QRR++DjVFW_zz7,D"EFF''.F::w.(?@AAnn&;>~;NRS;SnQ&7Y^YcYcdrYsK
# w''%&r3 &jj5A ??7##(8CG@T<U(U  P  PK Azz7CF+%@@As\   9I$ .I$ I$ I$ <!I$ I$ 6,I$ #I$ ;AI$ ?AI$ !I$ $J/"JJJr   .c                    V ^8  d   QhR\         R\         R\         R\         R\        R\        R\         R\        R	\         R
\         /
# )r   patterntargetr!   	file_globr   r   output_modecontextr=   r   r   )r	   s   "r
   r   r     su     AA AA AAc AAS AAAA.1AA@CAA AA7:AA AA .1AAr   c	                    RT T\        V4      T;'       g    RVV3p	\        ;_uu_ 4        \        P                  VRRR^ R\	        4       /4      p
V
R,          V	8X  d   V
R;;,          ^,          uu&   M	WR&   ^V
R&   V
R,          pRRR4       X^8  d#   \
        P                  ! RRV R	2R
V RV/RR7      # \        V4      pVP                  WWWEWgR7      p\        VR4      '       dV   VP                   FE  p\        VR4      '       g   K  VP                  '       g   K+  \        VP                  4      Vn        KG  	  VP                  4       pV^8  d
   RV R2VR&   \
        P                  ! VRR7      pVP                  R4      '       d   WT,           pVRV R2,          pV#   + '       g   i     EL'; i  \         d.   p\
        P                  ! R\        T4      /RR7      u Rp?# Rp?ii ; i)zSearch for content or files.searchrQ   r   Nr   r   r   z(BLOCKED: You have run this exact search z times in a row. The results have NOT changed. You already have this information. STOP re-searching and proceed with your task.r  already_searchedFr   )r  r!   r  r  r   r   r  r  matchesr   zYou have run this exact search zY times consecutively. The results have not changed. Use the information you already have.r   r   z'

[Hint: Results truncated. Use offset=zC to see more, or narrow with a more specific pattern or file_glob.])r   r   r   r   r   r   r   r{   r	  hasattrr  r   r   r   r   r   )r  r  r!   r  r   r   r  r  r=   
search_keyr   r   rz   r   mr   r   next_offsetr   s   &&&&&&&&&          r
   search_toolr    s   
<A
 IOO

  %00D-NCE; I $
2-(A-((2*%+,	-(m,E   A:::>ug FD D 7"E "# # !)vK ! 
 69%%^^1i((QYYY 5aii @AI $ nn&A:1% 9V V 
#
 jj5A ??;'' .KF{m  TW  X  XK[  \  Azz7CF+%@@AsT   F9 F9 AF%0F9 8AF9 F9 !A,F9 F9 %F6	0	F9 9G1"G,&G1,G1namer   functionr   r   search_filesc                     \         # )z&Get the list of file tool definitions.)
FILE_TOOLS r   r
   get_file_toolsr    s    r   )registryc                     ^ RI Hp  V ! 4       # )z=Lazy wrapper to avoid circular import with tools/__init__.py.check_file_requirements)toolsr  r  s    r
   _check_file_reqsr    s    -"$$r   descriptionu  Read a text file with line numbers and pagination. Use this instead of cat/head/tail in terminal. Output format: 'LINE_NUM|CONTENT'. Suggests similar filenames if not found. Use offset and limit for large files. Reads exceeding ~100K characters are rejected; use offset and limit to read specific sections of large files. NOTE: Cannot read images or binary files — use vision_analyze for images.
parametersr   object
propertiesr!   stringz8Path to the file to read (absolute, relative, or ~/path)r   integerz9Line number to start reading from (1-indexed, default: 1)minimumr   z9Maximum number of lines to read (default: 500, max: 2000)maximumi  requiredu   Write content to a file, completely replacing existing content. Use this instead of echo/cat heredoc in terminal. Creates parent directories automatically. OVERWRITES the entire file — use 'patch' for targeted edits.zWPath to the file to write (will be created if it doesn't exist, overwritten if it does)z%Complete content to write to the fileai  Targeted find-and-replace edits in files. Use this instead of sed/awk in terminal. Uses fuzzy matching (9 strategies) so minor whitespace/indentation differences won't break it. Returns a unified diff. Auto-runs syntax checks after editing.

Replace mode (default): find a unique string and replace it.
Patch mode: apply V4A multi-file patches for bulk changes.r   enumzVEdit mode: 'replace' for targeted find-and-replace, 'patch' for V4A multi-file patchesz/File path to edit (required for 'replace' mode)r   zText to find in the file (required for 'replace' mode). Must be unique in the file unless replace_all=true. Include enough surrounding context to ensure uniqueness.r   z_Replacement text (required for 'replace' mode). Can be empty string to delete the matched text.r   booleanzLReplace all occurrences instead of requiring a unique match (default: false)zV4A format patch content (required for 'patch' mode). Format:
*** Begin Patch
*** Update File: path/to/file
@@ context hint @@
 context line
-removed line
+added line
*** End Patchu  Search file contents or find files by name. Use this instead of grep/rg/find/ls in terminal. Ripgrep-backed, faster than shell equivalents.

Content search (target='content'): Regex search inside files. Output modes: full matches with line numbers, file paths only, or match counts.

File search (target='files'): Find files by glob pattern (e.g., '*.py', '*config*'). Also use this instead of ls — results sorted by modification time.r  zPRegex pattern for content search, or glob pattern (e.g., '*.py') for file searchr  fileszK'content' searches inside file contents, 'files' searches for files by namezCDirectory or file to search in (default: current working directory)r  zOFilter files by pattern in grep mode (e.g., '*.py' to only search Python files)z1Maximum number of results to return (default: 50)z0Skip first N results for pagination (default: 0)r  zOutput format for grep mode: 'content' shows matching lines with line numbers, 'files_only' lists file paths, 'count' shows match counts per filer  zDNumber of context lines before and after each match (grep mode only)c                     VP                  R 4      ;'       g    Rp\        V P                  RR4      V P                  R^4      V P                  RR4      VR7      # )r=   r:   r!   rQ   r   r   r   )r!   r   r   r=   )r   r   argskwtids   &, r
   _handle_read_filer/    sP    
&&

(
(yCtxx3DHHXq<QY]YaYabiknYoy|}}r   c                     VP                  R 4      ;'       g    Rp\        V P                  RR4      V P                  RR4      VR7      # )r=   r:   r!   rQ   r   )r!   r   r=   )r   r   r+  s   &, r
   _handle_write_filer1  !  s?    
&&

(
(yC 4dhhyRT>U_bccr   c           
         VP                  R 4      ;'       g    Rp\        V P                  RR4      V P                  R4      V P                  R4      V P                  R4      V P                  RR4      V P                  R	4      VR
7      # )r=   r:   r   r   r!   r   r   r   Fr   )r   r!   r   r   r   r   r=   )r   r   r+  s   &, r
   _handle_patchr3  &  ss    
&&

(
(yCXXfi(txx/?88L)dhh|6LHH]E2$((7:KUXZ Zr   c                    VP                  R 4      ;'       g    RpRRRR/pV P                  RR4      pVP                  WD4      p\        V P                  RR4      WPP                  R	R
4      V P                  R4      V P                  R^24      V P                  R^ 4      V P                  RR4      V P                  R^ 4      VR7	      # )r=   r:   grepr   findr)  r  r  rQ   r!   r  r  r   r   r  r  )	r  r  r!   r  r   r   r  r  r=   )r   r  )r,  r-  r.  
target_map
raw_targetr  s   &,    r
   _handle_search_filesr9  .  s    
&&

(
(yC)VW5J(I.J^^J3FB'XXfc=R((;'txx/DTXXV^`aMbHH]I6TU@V`ce er   fileu   📖)r  toolsetschemahandlercheck_fnemojiu   ✍️u   🔧u   🔎c                    V ^8  d   Qh/ ^ \         9   d   \        R,          ;R&   ^\         9   d
   \        ;R&   ^\         9   d
   \        ;R&   # )r   Nr   ro   r   )__conditional_annotations__r   dict)r	   s   "r
   r   r      sF     < <4 * )d
 )5 =|   } =`  t a =r   >   /dev/tty	/dev/fd/0	/dev/fd/1	/dev/fd/2	/dev/full	/dev/zero
/dev/stdin/dev/random/dev/stderr/dev/stdout/dev/console/dev/urandom)z/etc/z/boot/z/usr/lib/systemd/)r:   )N)   r   r:   )r   NNNFNr:   )r   r  N2       r   rQ  r:   )r   
files_onlyr   )?rA  __doc__r7   r   loggingr    rq   r   r   tools.file_operationsr   agent.redactr   	getLoggerr   rs   EACCESEPERMEROFSr8   r   r   r   r   	frozensetr#   r'   r.   r/   r2   r9   rr   rn   ro   r   r   r{   r   r   r   r   r   r   r   r   r   r   r  r  r  tools.registryr  r  READ_FILE_SCHEMAWRITE_FILE_SCHEMAPATCH_SCHEMASEARCH_FILES_SCHEMAr/  r1  r3  r9  registerr   )rA  s   @r
   <module>rb     s   <    	   5 .			8	$  ,,U[[A  " %)  )"0 !  " 	# 	 * D 02DE ( ! " ^^%  vr$\A~
("/,) R$>A06ArAAJ [*n5\:7Wj*-^Z5	
 $% K  cVX}6pqvy-9tv  BC  EN  PQ  Rfi8su~  AD  FO  QU  V

 	VH  L  pVX}  7P  Q-9`a
 	VY'  G  BVXv	7/C]  Um  ox  zC  DVX}6gh68]  =c  d68]  =^  _FI}  ?M  OX  Z_  `fh  8u  v
 	VH$ N  O-  :L  Mvx)W1E}  Wd  fo  qz  {VX}6{  ~G  IL  M&(M  <M  Nfi8kmvxz{vy-9kmvxyzFHf6XZg  j}  H  JS  T	=  ;A  CL  NO  P	
 	YK (~
d
Ze 	  {F;KUf  rB  JP  Q   |V<MWi  uE  MU  V   w|]eu  ~D  E   ~v>Q[o  {K  SY  Zr   