+
    iG                        R t ^ RIt^ RIt^ RIt^ RIt^ RIt^ RIt^ RIt^ RIt^ RI	t	^ RI
t
]P                  ! 4       R8H  t^ RIHtHt ^ RIHtHt ^ RIHtHtHtHt ^ RIHt ]P4                  ! ]4      t]! 4       R,          tRtR	t^@t ] ! R
 R4      4       t! ! R R4      t"]"! 4       t#^ RI$H%t% RRRRRRRRRRRR. R,ORR/RRRRR/RRRRR/RRR RR!R"^/R#RR RR$/R%RR RR&R"^//R'R.//t&R( t']%PP                  ! RR)]&]'R*R+7       R# )-a  
Process Registry -- In-memory registry for managed background processes.

Tracks processes spawned via terminal(background=true), providing:
  - Output buffering (rolling 200KB window)
  - Status polling and log retrieval
  - Blocking wait with interrupt support
  - Process killing
  - Crash recovery via JSON checkpoint file
  - Session-scoped tracking for gateway reset protection

Background processes execute THROUGH the environment interface -- nothing
runs on the host machine unless TERMINAL_ENV=local. For Docker, Singularity,
Modal, Daytona, and SSH backends, the command runs inside the sandbox.

Usage:
    from tools.process_registry import process_registry

    # Spawn a background process (called from terminal_tool)
    session = process_registry.spawn(env, "pytest -v", task_id="task_123")

    # Poll for status
    result = process_registry.poll(session.id)

    # Block until done
    result = process_registry.wait(session.id, timeout=300)

    # Kill it
    process_registry.kill(session.id)
NWindows)_find_shell_sanitize_subprocess_env)	dataclassfield)AnyDictListOptional)get_hermes_homezprocesses.jsoni@ i  c                      a  ] tR t^>t o RtRtRtRtRtRt	Rt
RtRtRtRt]tRtRtRtRt^ t]! ]P0                  R7      t]! RRR7      t]! RRR7      tV 3R ltR	tV tR# )
ProcessSessionz3A tracked background process with output buffering. Ng        F)default_factory)defaultreprc                  < V ^8  d   Qh/ S[ ;R&   S[ ;R&   S[ ;R&   S[ ;R&   S[S[,          ;R&   S[S[P                  ,          ;R&   S[;R&   S[S[ ,          ;R&   S[;R	&   S[;R
&   S[S[,          ;R&   S[ ;R&   S[;R&   S[;R&   S[ ;R&   S[ ;R&   S[ ;R&   S[;R&   S[P                  ;R&   S[S[P                  ,          ;R&   S[;R&   # )   idcommandtask_idsession_keypidprocessenv_refcwd
started_atexited	exit_codeoutput_buffermax_output_charsdetachedwatcher_platformwatcher_chat_idwatcher_thread_idwatcher_interval_lock_reader_thread_pty)strr
   int
subprocessPopenr   floatbool	threadingLockThread)format__classdict__s   "3/home/ubuntu/hermes-agent/tools/process_registry.py__annotate__ProcessSession.__annotate__>   s:     	G  L	 
     
#  j&&'.    
#      }#    ,   ! $ % & ' ( ) * + , >>A- . Y--.P/ 0 /1      )__name__
__module____qualname____firstlineno____doc__r   r   r   r   r   r   r   r   r   r   MAX_OUTPUT_CHARSr    r!   r"   r#   r$   r%   r   r/   r0   r&   r'   r(   __annotate_func____static_attributes____classdictcell__r3   s   @r4   r   r   >   s     = GKC*.GGCJF#IM,HO!)..AE16t%1PNd/D1  r7   r   c                     a  ] tR t^Yt o RtR.tR t]V 3R lR l4       tR/V 3R lR llt	R0V 3R lR	 llt
V 3R
 lR ltV 3R lR ltV 3R lR ltV 3R lR ltV 3R lR ltV 3R lR ltR1V 3R lR lltR2V 3R lR lltV 3R lR ltV 3R lR ltR3V 3R lR lltR2V 3R  lR! lltV 3R" lR# ltV 3R$ lR% ltR2V 3R& lR' lltR( tR) tR* tV 3R+ lR, ltR-tV tR# )4ProcessRegistrya  
In-memory registry of running and finished background processes.

Thread-safe. Accessed from:
  - Executor threads (terminal_tool, process tool handlers)
  - Gateway asyncio loop (watcher tasks, session reset checks)
  - Cleanup thread (sandbox reaping coordination)
c                d    / V n         / V n        \        P                  ! 4       V n        . V n        R # N)_running	_finishedr/   r0   r&   pending_watchersselfs   &r4   __init__ProcessRegistry.__init__k   s(    3546^^%
 79r7   c                &   < V ^8  d   QhRS[ RS[ /# )r   textreturn)r)   )r2   r3   s   "r4   r5   ProcessRegistry.__annotate__t   s            r7   c                6  a V P                  R4      oS'       dp   \        ;QJ d-    V3R l\        P                   4       F  '       g   K   RM"	  RM! V3R l\        P                   4       4      '       d   SP	                  ^ 4       Kw  RP                  S4      # )z:Strip shell startup warnings from the beginning of output.
c              3   :   <"   T F  qS^ ,          9   x  K  	  R# 5i)    Nr8   ).0noiseliness   & r4   	<genexpr>5ProcessRegistry._clean_shell_noise.<locals>.<genexpr>w   s     c;b%U1X-;bs   TF)splitanyrD   _SHELL_NOISE_SUBSTRINGSpopjoin)rO   rX   s   &@r4   _clean_shell_noise"ProcessRegistry._clean_shell_noises   s\     

4 c?;b;bcc?;b;bcccIIaLyyr7   Nc                D   < V ^8  d   QhRS[ RS[ RS[ RS[ RS[RS[RS[/# )r   r   r   r   r   env_varsuse_ptyrP   )r)   dictr.   r   )r2   r3   s   "r4   r5   rQ   }   sZ     n nn n 	n
 n n n 
nr7   c                   \        R\        P                  ! 4       P                  R,           2TTTT;'       g    \        P
                  ! 4       \        P                  ! 4       R7      pV'       Ed    \        '       d   ^ RIH	p M^ RI
H	p \        4       p	\        \        P                  V4      p
RV
R&   VP                  V	RV.VP                  V
RR7      pVP                   Vn        Wn        \$        P&                  ! V P(                  V3R	R
VP*                   2R7      pWn        VP/                  4        V P0                  ;_uu_ 4        V P3                  4        WpP4                  VP*                  &   RRR4       V P7                  4        V# \        4       p	\        \        P                  V4      pRVR&   \@        PB                  ! V	RV.R	VP                  TRR\@        PD                  \@        PF                  \@        PD                  \        '       d   RM\        PH                  R7
      pWn%        VP                   Vn        \$        P&                  ! V PL                  V3R	RVP*                   2R7      pWn        VP/                  4        V P0                  ;_uu_ 4        V P3                  4        WpP4                  VP*                  &   RRR4       V P7                  4        V#   + '       g   i     ELu; i  \8         d    \:        P=                  R4        EL\>         d"   p\:        P=                  RT4        Rp?ELRp?ii ; i  + '       g   i     L; i)a?  
Spawn a background process locally.

Only for TERMINAL_ENV=local. Other backends use spawn_via_env().

Args:
    use_pty: If True, use a pseudo-terminal via ptyprocess for interactive
             CLI tools (Codex, Claude Code, Python REPL). Falls back to
             subprocess.Popen if ptyprocess is not installed.
proc_N   N)r   r   r   r   r   r   )
PtyProcess1PYTHONUNBUFFEREDz-lic)r   env
dimensionsTzproc-pty-reader-targetargsdaemonnameNz3ptyprocess not installed, falling back to pipe modez0PTY spawn failed (%s), falling back to pipe modeutf-8replace)	rO   r   rm   encodingerrorsstdoutstderrstdin
preexec_fnzproc-reader-)   x   )'r   uuiduuid4hexosgetcwdtime_IS_WINDOWSwinptyrj   
ptyprocessr   r   environspawnr   r   r(   r/   r1   _pty_reader_loopr   r'   startr&   _prune_if_neededrG   _write_checkpointImportErrorloggerwarning	Exceptionr+   r,   PIPESTDOUTsetsidr   _reader_loop)rK   r   r   r   r   rc   rd   session_PtyProcessCls
user_shellpty_envpty_procreaderebg_envprocs   &&&&&&&         r4   spawn_localProcessRegistry.spawn_local}   s   & !tzz|'',-.#""ryy{yy{
 7&V;CG(]
22::xH.1*+)//1(	 0  'll' #))00!+GJJ<8	 *0&ZZZ))+07MM'**-   &&( !]
 *"**h?%(!")??$$//*{t		
 hh !!$$

|,	
 "(ZZZ!!#(/MM'**%  	 i  ZZ  VTU VQSTUUVL ZsO   3K3 ?CK3 
)K3K3 )MK0	*	K3 3 MMM L<<MM	c                D   < V ^8  d   QhRS[ RS[RS[RS[RS[RS[RS[/# )r   rm   r   r   r   r   timeoutrP   )r   r)   r*   r   )r2   r3   s   "r4   r5   rQ      sZ     E EE E 	E
 E E E 
Er7   c           
        \        R\        P                  ! 4       P                  R,           2VVVV\        P                  ! 4       VR7      pRVP
                   R2pRVP
                   R2p	\        P                  ! V4      p
RV
 RV R	V	 R
V	 2p VP                  WR7      pVP                  RR4      P                  4       pVP                  4        F;  pVP                  4       pVP                  4       '       g   K+  \        V4      Vn         M	   VP                   '       gK   \&        P(                  ! V P*                  WqW3RRVP
                   2R7      pVVn        VP/                  4        V P0                  ;_uu_ 4        V P3                  4        WpP4                  VP
                  &   RRR4       V P7                  4        V#   \         d#   pRTn        RTn        RT 2Tn         Rp?LRp?ii ; i  + '       g   i     LR; i)a  
Spawn a background process through a non-local environment backend.

For Docker/Singularity/Modal/Daytona/SSH: runs the command inside the sandbox
using the environment's execute() interface. We wrap the command to
capture the in-sandbox PID and redirect output to a log file inside
the sandbox, then poll the log via subsequent execute() calls.

This is less capable than local spawn (no live stdout pipe, no stdin),
but it ensures the command runs in the correct sandbox context.
rg   rh   )r   r   r   r   r   r   r   z/tmp/hermes_bg_z.logz.pidznohup bash -c z > z 2>&1 & echo $! > z && cat r   outputr   TzFailed to start: Nzproc-poller-ro   )r   r~   r   r   r   r   shlexquoteexecutegetstrip
splitlinesisdigitr*   r   r   r   r   r   r/   r1   _env_poller_loopr'   r   r&   r   rG   r   )rK   rm   r   r   r   r   r   r   log_pathpid_pathquoted_command
bg_commandresultr   liner   r   s   &&&&&&&          r4   spawn_via_envProcessRegistry.spawn_via_env   s   ( !tzz|'',-.#yy{
 %WZZL5$WZZL5W-^,Cz :!
(8*6 	
	<[[[=FZZ"-335F))+zz|<<>>"%d)GK	 , ~~~%%,,H7#GJJ<0	F &,G"LLNZZZ!!#(/MM'**%  	 +  	<!GN "G&7s$;G!!	<  Zs1   A(G ?G G )G7G4G//G47H	c                    < V ^8  d   QhRS[ /# r   r   r   )r2   r3   s   "r4   r5   rQ   6  s     ( (N (r7   c                   Rp  VP                   P                  P                  R4      pV'       g   MV'       d   V P                  V4      pRpVP                  ;_uu_ 4        V;P
                  V,          un        \        VP
                  4      VP                  8  d    VP
                  VP                  ) R Vn        RRR4       K   VP                   P                  ^R7       RVn        VP                   P                  Vn        V P                  V4       R#   + '       g   i     EK)  ; i  \         d!   p\        P                  RT4        Rp?LRp?ii ; i  \         d!   p\        P                  RT4        Rp?LRp?ii ; i)z:Background thread: read stdout from a local Popen process.T   FNzProcess stdout reader ended: %sr   z$Process wait timed out or failed: %s)r   rx   readr`   r&   r   lenr    r   r   debugwaitr   
returncoder   _move_to_finished)rK   r   first_chunkchunkr   s   &&   r4   r   ProcessRegistry._reader_loop6  s6   	?..33D9 33E:E"'K]]]))U2)7001G4L4LL070E0EwG_G_F_F`0a- #]	DOO   + #OO66w' #]]  	?LL:A>>	?  	DLL?CC	DsR   -D0 D0 'D0 "AD?
D0 
E D-	&
D0 0E;EEF	)FF	c                2   < V ^8  d   QhRS[ RS[RS[RS[/# )r   r   rm   r   r   )r   r   r)   )r2   r3   s   "r4   r5   rQ   Q  s-     * *%*,/*;>*JM*r7   c                6   VP                   '       Eg   \        P                  ! ^4        VP                  RV R2^
R7      pVP	                  RR4      pV'       dg   VP
                  ;_uu_ 4        Wan        \        VP                  4      VP                  8  d    VP                  VP                  ) R Vn        RRR4       VP                  RV R2^R7      pVP	                  RR4      P                  4       pV'       d   VP                  4       R,          P                  4       R	8w  d   VP                  R
V R2^R7      p	V	P	                  RR4      P                  4       p
 \        V
P                  4       R,          P                  4       4      Vn        RVn         V P                  V4       R# EK  EK  R#   + '       g   i     EL; i  \        \        3 d    RTn         LPi ; i  \          d$    RTn         RTn        T P                  T4        R# i ; i)zBBackground thread: poll a sandbox log file for non-local backends.zcat  2>/dev/nullr   r   r   Nzkill -0 $(cat z" 2>/dev/null) 2>/dev/null; echo $?0zwait $(cat Tr   )r   r   sleepr   r   r&   r   r   r    r   r   r*   r   
ValueError
IndexErrorr   r   )rK   r   rm   r   r   r   
new_outputcheckcheck_outputexit_resultexit_strs   &&&&&      r4   r    ProcessRegistry._env_poller_loopQ  s    ...JJqM$tH:\%BBO#ZZ"5
 0:-w4458P8PP4;4I4I7KcKcJcJd4eG1 ' $XJ.PQ $   %yy26<<>L$;$;$=b$A$G$G$IS$P"%++%hZ/QR ! #. #K  +x<BBDH/,/0C0C0Eb0I0O0O0Q,R) &*GN**73 %Q<% ! '( '
3 /,.)/  !%$&!&&w/s\   0G* G* 0A
F8:AG* A!G* #3G G* 8G			G* G'$G* &G''G* **HHc                    < V ^8  d   QhRS[ /# r   r   )r2   r3   s   "r4   r5   rQ   }  s     ( ( (r7   c                   VP                   p VP                  4       '       d    VP                  R4      pV'       d   \        V\        4      '       d   TMVP                  RRR7      pVP                  ;_uu_ 4        V;P                  V,          un        \        VP                  4      VP                  8  d    VP                  VP                  ) R Vn        RRR4       K  K    VP                  4        RVn        \!        VR	4      '       d   VP"                  MR
Vn        V P'                  V4       R#   + '       g   i     EK;  ; i  \         d     Ks  \         d     K  i ; i  \         d!   p\        P                  RT4        Rp?LRp?ii ; i  \         d!   p\        P                  RT4        Rp?LRp?ii ; i)z2Background thread: read output from a PTY process.r   rt   ru   )rw   NzPTY stdout reader ended: %sz PTY wait timed out or failed: %sT
exitstatusr   )r(   isaliver   
isinstancer)   decoder&   r   r   r    EOFErrorr   r   r   r   r   hasattrr   r   r   )rK   r   ptyr   rO   r   s   &&    r4   r    ProcessRegistry._pty_reader_loop}  sc   ll	;++--HHTNE(25#(>(>uELLQXajLDk$]]]#11T91"7#8#89G<T<TT8?8M8MwOgOgNgNh8i 5 +]   $	@HHJ .5c<.H.HCNNbw'% +]]      	;LL6::	;  	@LL;Q??	@s   E+ E ?E =AD7E "E+ (F 7E		E E+ 	E E(E+ E(#E($E+ 'E((E+ +F6FFG$F??Gc                    < V ^8  d   QhRS[ /# r   r   )r2   r3   s   "r4   r5   rQ     s     ! ! !r7   c                   V P                   ;_uu_ 4        V P                  P                  VP                  R4       WP                  VP                  &   RRR4       V P                  4        R#   + '       g   i     L"; i)z(Move a session from running to finished.N)r&   rG   r^   r   rH   r   )rK   r   s   &&r4   r   !ProcessRegistry._move_to_finished  sN    ZZZMMgjj$/)0NN7::&  	  Zs   ?A..A>	c                6   < V ^8  d   QhRS[ RS[S[,          /# r   
session_idrP   )r)   r
   r   )r2   r3   s   "r4   r5   rQ     s$     S Sc Sh~&> Sr7   c                    V P                   ;_uu_ 4        V P                  P                  V4      ;'       g    V P                  P                  V4      uuRRR4       #   + '       g   i     R# ; i)z*Get a session by ID (running or finished).N)r&   rG   r   rH   )rK   r   s   &&r4   r   ProcessRegistry.get  s@    ZZZ==$$Z0RRDNN4F4Fz4R ZZZs   >AA/	c                &   < V ^8  d   QhRS[ RS[/# r   r)   re   )r2   r3   s   "r4   r5   rQ     s      s t r7   c                T   ^ RI Hp V P                  V4      pVf
   RRRRV 2/# VP                  ;_uu_ 4        VP                  '       d   V! VP                  RR 4      MRpRRR4       RVP
                  R	VP                  RVP                  '       d   R
MRRVP                  R\        \        P                  ! 4       VP                  ,
          4      RX/pVP                  '       d   VP                  VR&   VP                  '       d   RVR&   RVR&   V#   + '       g   i     L; i)z9Check status and get new output for a background process.
strip_ansiNstatus	not_founderrorNo process with ID r   r   r   r   runningr   uptime_secondsoutput_previewr   Tr!   z=Process recovered after restart -- output history unavailablenote)tools.ansi_stripr   r   r&   r   r   r   r   r   r*   r   r   r   r!   )rK   r   r   r   r   r   s   &&    r4   pollProcessRegistry.poll  s    /((:&?k76I*4VWW]]]JQJ_J_J_Z(=(=ef(EFegN  '**w'...hi7;;c$))+0B0B"BCn
 >>>")"3"3F;!%F:\F6N! ]s   *DD'	c                2   < V ^8  d   QhRS[ RS[RS[RS[/# )r   r   offsetlimitrP   r)   r*   re   )r2   r3   s   "r4   r5   rQ     s)     
 
3 
 
 
d 
r7   c                   ^ RI Hp V P                  V4      pVf
   RRRRV 2/# VP                  ;_uu_ 4        V! VP                  4      pRRR4       XP                  4       p\        V4      pV^ 8X  d   V^ 8  d   Ws) R p	M
WrW#,            p	RVP                  RVP                  '       d   RMR	R
RP                  V	4      RVR\        V	4       R2/#   + '       g   i     L; i)z;Read the full output log with optional pagination by lines.r   Nr   r   r   r   r   r   r   r   rS   total_linesshowingz lines)
r   r   r   r&   r   r   r   r   r   r_   )
rK   r   r   r   r   r   full_outputrX   r   selecteds
   &&&&      r4   read_logProcessRegistry.read_log  s    /((:&?k76I*4VWW]]]$W%:%:;K  &&(%j Q;519VW~HFN3H '**'...hidii);#h-/
 	
 ]s   CC"	c                ,   < V ^8  d   QhRS[ RS[RS[/# )r   r   r   rP   r   )r2   r3   s   "r4   r5   rQ     s'     B Bs BS BD Br7   c           
        ^ RI Hp ^ RIHp \	        \
        P                  ! RR4      4      pTpTpRpV'       d   Wv8  d   Tp	RV RV R2pMT;'       g    Tp	V P                  V4      p
V
f
   R	R
RRV 2/# \        P                  ! 4       V	,           p\        P                  ! 4       V8  d   V
P                  '       d4   R	RRV
P                  RV! V
P                  RR 4      /pV'       d   WR&   V# VP                  4       '       d*   R	RRV! V
P                  RR 4      RR/pV'       d   WR&   V# \        P                  ! ^4       K  R	RRV! V
P                  RR 4      /pV'       d   WR&   V# RV	 R2VR&   V# )a  
Block until a process exits, timeout, or interrupt.

Args:
    session_id: The process to wait for.
    timeout: Max seconds to block. Falls back to TERMINAL_TIMEOUT config.

Returns:
    dict with status ("exited", "timeout", "interrupted", "not_found")
    and output snapshot.
r   )_interrupt_eventTERMINAL_TIMEOUT180NzRequested wait of z%s was clamped to configured limit of sr   r   r   r   r   r   r   timeout_noteinterruptedr   z+User sent a new message -- wait interruptedr   zWaited zs, process still runningi0r   )r   r   tools.terminal_toolr  r*   r   getenvr   r   	monotonicr   r   r   is_setr   )rK   r   r   r   r  default_timeoutmax_timeoutrequested_timeoutr  effective_timeoutr   deadliner   s   &&&          r4   r   ProcessRegistry.wait  s    	08bii(:EBC%#!2!@ +$%6$7 8**5a9 
 !2 @ @[((:&?k76I*4VWW>>#&77nn)~~~h!2!2j)>)>uv)FG
  -9>*&&((mj)>)>uv)FGI
  -9>*JJqM ij!6!6uv!>?
 %1>"  (//@.AAY%ZF>"r7   c                &   < V ^8  d   QhRS[ RS[/# r   r   )r2   r3   s   "r4   r5   rQ   #  s     '8 '8s '8t '8r7   c                d   V P                  V4      pVf
   RRRRV 2/# VP                  '       d   RRRVP                  /#  VP                  '       d    VP                  P	                  RR	7       MVP                  '       dw    \        '       d   VP                  P	                  4        M\        P                  ! \        P                  ! VP                  P                  4      \        P                  4       MOVP"                  '       d>   VP                  '       d,   VP"                  P%                  R
VP                   R2^R7       RVn        RVn        V P'                  V4       V P)                  4        RRRVP*                  /#   \
         dE    TP                  '       d0   \        P                  ! TP                  \        P                  4        Li ; i  \        \         3 d    TP                  P                  4         Li ; i  \
         d   pRRR\-        T4      /u Rp?# Rp?ii ; i)zKill a background process.Nr   r   r   r   already_exitedr   T)forcezkill r   r   killedr   i)r   r   r   r(   	terminater   r   r   killsignalSIGTERMr   r   killpggetpgidProcessLookupErrorPermissionErrorr   r   r   r   r   r)   )rK   r   r   r   s   &&  r4   kill_processProcessRegistry.kill_process#  s   ((:&?k76I*4VWW>>>*W.. 	8|||=LL***6 +"{113		"**W__-@-@"A6>>R W[[[''%}L(IST'U!GN #G""7+""$hgjjAA' ! ={{{V^^<= +O< +OO((*+  	8gwA77	8s   H F 1H G G +H ,AG 9H H A)H G%/GH GH +HH HH H/H*$H/*H/c                ,   < V ^8  d   QhRS[ RS[ RS[/# r   r   datarP   r   )r2   r3   s   "r4   r5   rQ   L  s"     8 8c 8 8 8r7   c                   V P                  V4      pVf
   RRRRV 2/# VP                  '       d   RRRR/# \        VR4      '       dg   VP                  '       dU    \	        V\
        4      '       d   VP                  R	4      MTpVP                  P                  V4       RR
R\        V4      /# VP                  '       d   VP                  P                  '       g   RRRR/#  VP                  P                  P                  V4       VP                  P                  P                  4        RR
R\        V4      /#   \         d   pRRR\        T4      /u Rp?# Rp?ii ; i  \         d   pRRR\        T4      /u Rp?# Rp?ii ; i)zASend raw data to a running process's stdin (no newline appended).Nr   r   r   r   r  zProcess has already finishedr(   rt   okbytes_writtenz?Process stdin not available (non-local backend or stdin closed))r   r   r   r(   r   r)   encodewriter   r   r   rz   flush)rK   r   r"  r   pty_datar   s   &&&   r4   write_stdinProcessRegistry.write_stdinL  s\   ((:&?k76I*4VWW>>>.9WXX 7F##<3=dC3H3H4;;w/d""8, $TCC
 goo&;&;&;gw0qrr	8OO!!''-OO!!'')dOSY??  < '7CF;;<  	8gwA77	8s>   AD< $AE# <E EE E #F.F<FFc                ,   < V ^8  d   QhRS[ RS[ RS[/# r!  r   )r2   r3   s   "r4   r5   rQ   g  s"     9 9s 9# 9t 9r7   c                2    V P                  WR,           4      # )zGSend data + newline to a running process's stdin (like pressing Enter).rS   )r*  )rK   r   r"  s   &&&r4   submit_stdinProcessRegistry.submit_stding  s    
4K88r7   c                &   < V ^8  d   QhRS[ RS[/# r   r   rP   )r)   list)r2   r3   s   "r4   r5   rQ   k  s      S D r7   c                   V P                   ;_uu_ 4        \        V P                  P                  4       4      \        V P                  P                  4       4      ,           pRRR4       V'       d$   X Uu. uF  q3P
                  V8X  g   K  VNK  	  pp. pX EF#  pRVP                  RVP                  R,          RVP                  RVP                  R\        P                  ! R\        P                  ! VP                  4      4      R	\        \        P                  ! 4       VP                  ,
          4      R
VP                  '       d   RMRRVP                   '       d   VP                   RR MR/pVP                  '       d   VP"                  VR&   VP$                  '       d   RVR&   VP'                  V4       EK&  	  V#   + '       g   i     ELj; iu upi )z1List all running and recently-finished processes.Nr   r   :N   Nr   r   r   z%Y-%m-%dT%H:%M:%Sr   r   r   r   r   r   r   Tr!   i8)r&   r2  rG   valuesrH   r   r   r   r   r   r   strftime	localtimer   r*   r   r   r   r!   append)rK   r   all_sessionsr  r   entrys   &&    r4   list_sessionsProcessRegistry.list_sessionsk  sR   ZZZ 4 4 67$t~~?T?T?V:WWL  '3L|!yyG7KAA|LLAadd199T?quuquudmm,?PQP\P\A]^ #diikALL&@"Aahhh(I AOOO!//$%"8QS	E xxx%&[[k"zzz$(j!MM%    / ZZ Ms   AG5GGG	c                &   < V ^8  d   QhRS[ RS[/# r1  r)   r.   )r2   r3   s   "r4   r5   rQ     s      C D r7   c                >  a V P                   ;_uu_ 4        \        ;QJ d7    V3R lV P                  P                  4        4       F  '       g   K   RM,	  RM(! V3R lV P                  P                  4        4       4      uuRRR4       #   + '       g   i     R# ; i)z<Check if there are active (running) processes for a task_id.c              3   t   <"   T F-  pVP                   S8H  ;'       d    VP                  '       * x  K/  	  R # 5irF   )r   r   )rV   r  r   s   & r4   rY   7ProcessRegistry.has_active_processes.<locals>.<genexpr>  s2      /A 		W$55QXX5/   88TFNr&   r\   rG   r5  )rK   r   s   &fr4   has_active_processes$ProcessRegistry.has_active_processes  \    ZZZ3 --/333 --/  ZZZ   8B/BB	c                &   < V ^8  d   QhRS[ RS[/# )r   r   rP   r>  )r2   r3   s   "r4   r5   rQ     s      # $ r7   c                >  a V P                   ;_uu_ 4        \        ;QJ d7    V3R lV P                  P                  4        4       F  '       g   K   RM,	  RM(! V3R lV P                  P                  4        4       4      uuRRR4       #   + '       g   i     R# ; i)z>Check if there are active processes for a gateway session key.c              3   t   <"   T F-  pVP                   S8H  ;'       d    VP                  '       * x  K/  	  R # 5irF   )r   r   )rV   r  r   s   & r4   rY   9ProcessRegistry.has_active_for_session.<locals>.<genexpr>  s2      /A ,==QXX=/rB  TFNrC  )rK   r   s   &fr4   has_active_for_session&ProcessRegistry.has_active_for_session  rF  rG  c                &   < V ^8  d   QhRS[ RS[/# r1  )r)   r*   )r2   r3   s   "r4   r5   rQ     s       s r7   c                   V P                   ;_uu_ 4        V P                  P                  4        Uu. uF0  pVe   VP                  V8X  g   K  VP                  '       d   K.  VNK2  	  ppRRR4       ^ pX F?  pV P                  VP                  4      pVP                  R4      R9   g   K6  V^,          pKA  	  V# u upi   + '       g   i     L^; i)zQKill all running processes, optionally filtered by task_id. Returns count killed.Nr   )r  r  )r&   rG   r5  r   r   r  r   r   )rK   r   r  targetsr  r   r   s   &&     r4   kill_allProcessRegistry.kill_all  s    ZZZ==//11aOqyyG'; EFXX 1    G&&wzz2Fzz(#'CC!   Zs(   C B;B;"B;(C ;C  C	c                  a  \         P                   ! 4       pS P                  P                  4        UUu. uF$  w  r#WP                  ,
          \        8  g   K"  VNK&  	  pppV F  pS P                  V K  	  \        S P                  4      \        S P                  4      ,           pV\        8  d?   S P                  '       d+   \        S P                  V 3R lR7      pS P                  V R# R# R# u uppi )zGRemove oldest finished sessions if over MAX_PROCESSES. Must hold _lock.c                 >   < SP                   V ,          P                  # rF   )rH   r   )sidrK   s   &r4   <lambda>2ProcessRegistry._prune_if_needed.<locals>.<lambda>  s    DNN3<O<Z<Zr7   )keyN)	r   rH   itemsr   FINISHED_TTL_SECONDSr   rG   MAX_PROCESSESmin)rK   nowrU  r  expiredtotal	oldest_ids   f      r4   r    ProcessRegistry._prune_if_needed  s     iik"nn224
4FCll"&:: C4 	 
 Cs#  DMM"S%88M!dnnnDNN0Z[Iy) '5!
s   C1C1c                    V P                   ;_uu_ 4        V P                  4        RRR4       R#   + '       g   i     R# ; i)z1Public method to prune expired finished sessions.N)r&   r   rJ   s   &r4   cleanup_expiredProcessRegistry.cleanup_expired  s#    ZZZ!!# ZZZs	   0A	c                    V P                   ;_uu_ 4        . pV P                  P                  4        F  pVP                  '       d   K  VP	                  RVP
                  RVP                  RVP                  RVP                  RVP                  RVP                  RVP                  RVP                  R	VP                  R
VP                  RVP                  /4       K  	  RRR4       ^ RIHp V! \$        X4       R#   + '       g   i     L%; i  \&         d$   p\(        P+                  RTRR7        Rp?R# Rp?ii ; i)z=Write running process metadata to checkpoint file atomically.r   r   r   r   r   r   r   r"   r#   r$   r%   Natomic_json_writez#Failed to write checkpoint file: %sTexc_info)r&   rG   r5  r   r8  r   r   r   r   r   r   r   r"   r#   r$   r%   utilsrg  CHECKPOINT_PATHr   r   r   )rK   entriesr  rg  r   s   &    r4   r   !ProcessRegistry._write_checkpoint  s    	R--/A888(!$$%qyy!155!155(!,,%qyy)1==.0B0B-q/@/@/1D1D.0B0B(  0 & 0ow7) *  	RLL>DLQQ	Rs5   D /D 
BD #D  D	D ED<<Ec                    < V ^8  d   QhRS[ /# )r   rP   )r*   )r2   r3   s   "r4   r5   rQ     s     B B Br7   c                n   \         P                  4       '       g   ^ #  \        P                  ! \         P	                  RR7      4      p^ pT EF  pTP                  R4      pT'       g   K  Rp \        P                  ! T^ 4       RpT'       g   KE  \        TR,          TP                  RR4      TP                  R	R
4      TP                  RR
4      TTP                  R4      TP                  R\        P                  ! 4       4      RTP                  RR
4      TP                  RR
4      TP                  RR
4      TP                  R^ 4      R7      pT P                  ;_uu_ 4        Y`P                  TP                  &   RRR4       T^,          p\         P#                  RTP$                  R,          T4       TP&                  ^ 8  g   EK  T P(                  P+                  RTP                  RTP&                  RTP,                  RTP.                  RTP0                  RTP2                  /4       EK  	   ^ RIHp T! \         . 4       T#   \
         d     ^ # i ; i  \        \        3 d     ELi ; i  + '       g   i     L; i  \
         d$   p\         P9                  RTRR7        Rp?T# Rp?ii ; i)zn
On gateway startup, probe PIDs from checkpoint file.

Returns the number of processes recovered as detached.
rt   )rv   r   FTr   r   unknownr   r   r   r   r   r"   r#   r$   r%   )r   r   r   r   r   r   r   r!   r"   r#   r$   r%   Nz'Recovered detached process: %s (pid=%d):N<   Ncheck_intervalplatformchat_id	thread_idrf  z#Could not clear checkpoint file: %srh  )rk  existsjsonloads	read_textr   r   r   r  r  r  r   r   r&   rG   r   r   infor   r%   rI   r8  r   r"   r#   r$   rj  rg  r   )	rK   rl  	recoveredr:  r   aliver   rg  r   s	   &        r4   recover_from_checkpoint'ProcessRegistry.recover_from_checkpoint  sN    %%''	jj!:!:G!:!LMG 	E))E"C EQ u(\*!IIi;!IIi4 %		- <		%($yytyy{C!%*YY/A2%F$)II.?$D&+ii0CR&H%*YY/A1%E ZZZ07MM'**-  Q	EwWZG[]`a ++a/))00$gjj('*B*B%w':':"G$<$<!7#:#:#W%>%>2 G Z	R/or2 o  		 '8 $  Z(  	RLL>DLQQ	RsG   *I	 .I%I34J 	III0/I03JJ4J//J4)rH   r&   rG   rI   )z'bash: cannot set terminal process groupz"bash: no job control in this shellzno job control in this shellz!cannot set terminal process groupz)tcsetattr: Inappropriate ioctl for device)Nr   r   NF)Nr   r   
   )rU   r4  rF   )r   ) r9   r:   r;   r<   r=   r]   rL   staticmethodr`   r   r   r   r   r   r   r   r   r   r   r  r*  r.  r;  rD  rL  rQ  r   rc  r   r}  r@   rA   rB   s   @r4   rD   rD   Y   s     9    n n`E ER( (6* *X( (<! !S S
 4
 
8B BH'8 '8R8 869 9 :   "*"$R6B Br7   rD   )registryrs   r   descriptionaF  Manage background processes started with terminal(background=true). Actions: 'list' (show all), 'poll' (check status + new output), 'log' (full output with pagination), 'wait' (block until done or timeout), 'kill' (terminate), 'write' (send raw stdin data without newline), 'submit' (send data + Enter, for answering prompts).
parameterstypeobject
propertiesactionstringenumz)Action to perform on background processesr   z]Process session ID (from terminal background output). Required for all actions except 'list'.r"  z@Text to send to process stdin (for 'write' and 'submit' actions)r   integerzJMax seconds to block for 'wait' action. Returns partial output on timeout.minimumr   z6Line offset for 'log' action (default: last 200 lines)r   z$Max lines to return for 'log' actionrequiredc                 z   ^ RI pVP                  R4      pV P                  RR4      pV P                  R4      e   \        V P                  RR4      4      MRpVR8X  d*   VP                  R\        P                  VR7      /R	R
7      # VR9   Ed   V'       g   VP                  RRV 2/R	R
7      # VR8X  d'   VP                  \        P                  V4      R	R
7      # VR8X  dI   VP                  \        P                  WPP                  R^ 4      V P                  R^4      R7      R	R
7      # VR8X  d7   VP                  \        P                  WPP                  R4      R7      R	R
7      # VR8X  d'   VP                  \        P                  V4      R	R
7      # VR8X  dA   VP                  \        P                  V\        V P                  RR4      4      4      R	R
7      # VR8X  dA   VP                  \        P                  V\        V P                  RR4      4      4      R	R
7      # VP                  RRV R2/R	R
7      # )rU   Nr   r  r   r   r2  	processes)r   F)ensure_asciir   logr   r  r'  submitr   zsession_id is required for r   r   )r   r   r   r   r"  zUnknown process action: z1. Use: list, poll, log, wait, kill, write, submit)r   r  r   r  r'  r  )rw  r   r)   dumpsprocess_registryr;  r   r   r   r  r*  r.  )rq   kw_jsonr   r  r   s   &,    r4   _handle_processr  W  s#   ffYGXXh#F48HH\4J4VTXXlB/0\^J{{K)9)G)GPW)G)XYhm{nn	E	E;;+Fvh)OP_d;eeV;;/44Z@u;UUu_;;/8888Ha#8RU@V  9  Xfk  m mv;;/44ZR[I\4]lq;rrv;;/<<ZHW\;]]w;;/;;JDHHU[]_L`Habqv;wwx;;/<<ZTXXV\^`MaIbcrw;xx;;#;F8Ct!uv  FK;  L  Lr7   terminalu   ⚙️)rs   toolsetschemahandleremoji)r2  r   r  r   r  r'  r  ))r=   rw  loggingr   rs  r   r  r+   r/   r   r~   systemr   tools.environments.localr   r   dataclassesr   r   typingr   r   r	   r
   hermes_cli.configr   	getLoggerr9   r   rk  r>   rZ  r[  r   rD   r  tools.registryr  PROCESS_SCHEMAr  registerr8   r7   r4   <module>r     s  >   	       oo9, J ( , , -			8	$ "#&66    0 0 04E ER #$  $ I	? RJ
 ~ a 	k1
 	W 	E1/
: 	XJ? *ZL8 	  	
r7   