
    `gj=                        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Zddl	m
Z
mZ ddlmZmZ ddlmZ dZe
 G d d	e             Ze
 G d
 d             Z G d d      ZddZdef	 	 	 	 	 ddZy)a  Codex app-server JSON-RPC client.

Speaks the protocol documented in codex-rs/app-server/README.md (codex 0.125+).
Transport is newline-delimited JSON-RPC 2.0 over stdio: spawn `codex app-server`,
do an `initialize` handshake, then drive `thread/start` + `turn/start` and
consume streaming `item/*` notifications until `turn/completed`.

This module is the wire-level speaker only. Higher-level concerns (event
projection into Hermes' display, approval bridging, transcript projection into
AIAgent.messages, plugin migration) live in sibling modules.

Status: optional opt-in runtime gated behind `model.openai_runtime ==
"codex_app_server"`. Hermes' default tool dispatch is unchanged when this
runtime is not selected.
    )annotationsN)	dataclassfield)AnyOptional)hermes_subprocess_env)r   }   r   c                  <    e Zd ZU dZded<   ded<   dZded<   d
d	Zy)CodexAppServerErrorz.Raised on JSON-RPC errors from the app-server.intcodestrmessageNOptional[Any]datac                :    d| j                    d| j                   S )Nzcodex app-server error : r   r   selfs    T/root/.hermes/venv/lib/python3.12/site-packages/agent/transports/codex_app_server.py__str__zCodexAppServerError.__str__+   s    (2dll^DD    )returnr   )__name__
__module____qualname____doc____annotations__r   r    r   r   r   r   #   s    8
ILD-Er   r   c                  R    e Zd ZU ded<   ded<    eej
                        Zded<   y)	_Pendingzqueue.Queuequeuer   method)default_factoryfloatsent_atN)r   r   r   r   r   timer'   r    r   r   r"   r"   /   s     K4995GU5r   r"   c                     e Zd ZdZ	 	 	 	 d	 	 	 	 	 	 	 	 	 ddZ	 	 	 	 	 d	 	 	 	 	 	 	 	 	 	 	 ddZdddZddZddZ	 	 d	 	 	 	 	 	 	 ddZ	dd d	Z
d!d
Z	 d	 	 	 	 	 	 	 	 	 d"dZd#d$dZd#d$dZd%d&dZd'dZd(dZd)dZd*dZd+dZd*dZy),CodexAppServerClienta  Minimal JSON-RPC 2.0 client for `codex app-server` over stdio.

    Threading model:
      - Spawning thread (caller) drives request/response pairs synchronously.
      - One reader thread parses stdout, dispatches replies to the right
        pending future, and routes notifications + server-initiated requests
        to bounded queues that the caller drains on their own cadence.
      - One reader thread captures stderr for diagnostics; codex emits
        tracing logs there at RUST_LOG-controlled levels.

    Intentionally NOT async. AIAgent.run_conversation() is synchronous and
    runs on the main thread; layering asyncio just to drive a stdio child
    creates surprising interrupt semantics. We use blocking queues with
    timeouts and rely on `turn/interrupt` for cancellation.
    Nc                   || _         t        d      }|r|j                  |       |r||d<   t        |xs g       }|j	                  d      r|j	                  d      }|rt
        j                  j                  |      n\|j	                  dt
        j                  j                  |j	                  dt
        j                  j                  d            d	            }|j                  d
dd
d| dd
dg       |dg|z   }	|j                  dd       t        j                  |	t        j                  t        j                  t        j                  d|      | _        d| _        i | _        t%        j&                         | _        t+        j,                         | _        t+        j,                         | _        g | _        t%        j&                         | _        d| _        d| _        t%        j:                  | j<                  d      | _        | j>                  jA                          t%        j:                  | jB                  d      | _"        | jD                  jA                          y )NT)inherit_credentials
CODEX_HOMEHERMES_KANBAN_TASKHERMES_KANBAN_DBHERMES_KANBAN_ROOTHERMES_HOMEz	~/.hermeskanbanz-czsandbox_mode="workspace-write"z)sandbox_workspace_write.writable_roots=["z"]z,sandbox_workspace_write.network_access=falsez
app-serverRUST_LOGwarnr   )stdinstdoutstderrbufsizeenv   F)targetdaemon)#
_codex_binr   updatelistgetospathdirnamejoin
expanduserextend
setdefault
subprocessPopenPIPE_proc_next_id_pending	threadingLock_pending_lockr#   Queue_notifications_server_requests_stderr_lines_stderr_lock_closed_initializedThread_read_stdout_readerstart_read_stderr_stderr_reader)
r   	codex_bin
codex_home
extra_argsr9   	spawn_envapp_server_args	kanban_dbkanban_rootcmds
             r   __init__zCodexAppServerClient.__init__G   s    $ *dC	S!&0Il#z/R0 ==-.!&89I  	*]](GGLL!mRWW5G5G5TU   ""4?}BOB	 ,'/9Z0%%//????

 -/&^^-+0;;=-2[[](*%NN,! ''t/@/@N'..d6G6GPTU!!#r   c                    | j                   rt        d      |||d|xs i d}| j                  d||      }| j                  d       d| _         |S )zSend `initialize` + `initialized` handshake. Returns the server's
        InitializeResponse (userAgent, codexHome, platformFamily, platformOs).zalready initialized)nametitleversion)
clientInfocapabilities
initializetimeoutinitializedT)rW   RuntimeErrorrequestnotify)r   client_nameclient_titleclient_versionrl   ro   paramsresults           r   rm   zCodexAppServerClient.initialize   si     455 $%)
 ).B
 lFGDM" r   c                .   | j                   ryd| _         	 | j                  j                  rD| j                  j                  j                  s$| j                  j                  j	                          	 | j                  j                          | j                  j                  |       y# t
        $ r Y Cw xY w# t        j                  $ rJ 	 | j                  j                          | j                  j                  d       Y y# t
        $ r Y Y yw xY ww xY w)zDClose stdin and wait for the subprocess to exit, escalating to kill.NTrn   g      ?)rV   rK   r5   closedclose	Exception	terminatewaitrH   TimeoutExpiredkillr   ro   s     r   r{   zCodexAppServerClient.close   s    <<	zz

(8(8(?(?

  &&(	JJ  "JJOOGO,	  		
 (( 	

!

, 		s<   AB( 16B7 (	B43B47D6D	DDDDc                    | S Nr    r   s    r   	__enter__zCodexAppServerClient.__enter__   s    r   c                $    | j                          y r   )r{   )r   excs     r   __exit__zCodexAppServerClient.__exit__   s    

r   c                   | j                         }t        j                  d      }| j                  5  t	        ||      | j
                  |<   ddd       | j                  |||xs i d       	 |j                  |      }d
|v rB|d
   }t        |j                  dd      |j                  dd      |j                  d            |j                  di       S # 1 sw Y   xY w# t        j                  $ rQ | j                  5  | j
                  j                  |d       ddd       n# 1 sw Y   nxY wt        d|d| d	      w xY w)zsSend a JSON-RPC request and block on the response. Returns `result`,
        raises CodexAppServerError on `error`.r:   )maxsize)r#   r$   N)idr$   rw   rn   zcodex app-server method z timed out after serrorr   r    r   )r   r   r   rx   )_take_idr#   rQ   rP   r"   rM   _sendr@   EmptypopTimeoutErrorr   )r   r$   rw   ro   ridqmsgerrs           r   rr   zCodexAppServerClient.request   s?    mmoQ/ 	B!)&!ADMM#	B

#6<RHI	%%%(C c>g,C%WWVR(	2.WWV_ 
 wwx$$%	B 	B
 {{ 	## -!!#t,- - -*6*4EgYaP 	s/   C0C& C#&E
D+"	E
+D4	0E
c                4    | j                  ||xs i d       y)z;Send a JSON-RPC notification (no id, no response expected).)r$   rw   Nr   )r   r$   rw   s      r   rs   zCodexAppServerClient.notify   s    

f"=>r   c                ,    | j                  ||d       y)z<Reply to a server-initiated request (e.g. approval prompts).)r   rx   Nr   )r   
request_idrx   s      r   respondzCodexAppServerClient.respond   s    

*78r   c                D    ||d}|||d<   | j                  ||d       y)z2Reply to a server-initiated request with an error.r   Nr   )r   r   r   )r   r   r   r   r   r   s         r   respond_errorz"CodexAppServerClient.respond_error   s.     (,@CK

*s34r   c                    	 |dk  r| j                   j                         S | j                   j                  |      S # t        j                  $ r Y yw xY w)zPop the next streaming notification, or return None on timeout.

        timeout=0.0 means non-blocking. Use small positive timeouts inside the
        AIAgent turn loop to interleave reads with interrupt checks.r   rn   N)rR   
get_nowaitr@   r#   r   r   s     r   take_notificationz&CodexAppServerClient.take_notification   sS    
	!|**5577&&**7*;;{{ 		   = = AAc                    	 |dk  r| j                   j                         S | j                   j                  |      S # t        j                  $ r Y yw xY w)zFPop the next server-initiated request (e.g. exec/applyPatch approval).r   rn   N)rS   r   r@   r#   r   r   s     r   take_server_requestz(CodexAppServerClient.take_server_request  sS    	!|,,7799((,,W,=={{ 		r   c                x    | j                   5  t        | j                  | d       cddd       S # 1 sw Y   yxY w)z:Return last n lines of codex's stderr (for error reports).N)rU   r?   rT   )r   ns     r   stderr_tailz CodexAppServerClient.stderr_tail  s7     	1**A23/0	1 	1 	1s   09c                :    | j                   j                         d u S r   )rK   pollr   s    r   is_alivezCodexAppServerClient.is_alive  s    zz D((r   c                H    | j                   }| xj                   dz  c_         |S )Nr:   )rL   )r   r   s     r   r   zCodexAppServerClient._take_id   s      mm
r   c                   | j                   rt        d      | j                  j                  t        d      	 | j                  j                  j	                  t        j                  |      dz   j                  d             | j                  j                  j                          y # t        t        f$ r}t        d|       |d }~ww xY w)Nz!codex app-server client is closedz$codex app-server stdin not available
utf-8z,codex app-server stdin closed unexpectedly: )rV   rq   rK   r5   writejsondumpsencodeflushBrokenPipeError
ValueError)r   objr   s      r   r   zCodexAppServerClient._send(  s    <<BCC::#EFF	JJ""DJJsOd$:#B#B7#KLJJ""$, 	>seD	s   A.B) )C8CCc           	     |   | j                   j                  y 	 t        | j                   j                  j                  d      D ]@  }|s y |j	                         }|s	 t        j                  |      }| j                  |       B y # t
        j                  $ rF | j                  5  | j                  j                  d|d d        d d d        n# 1 sw Y   nxY wY w xY w# t        $ rO}| j                  5  | j                  j                  d|        d d d        n# 1 sw Y   nxY wY d }~y Y d }~y d }~ww xY w)Nr   z<non-json on stdout>    z<stdout reader error> )rK   r6   iterreadlinestripr   loadsJSONDecodeErrorrU   rT   append	_dispatchr|   )r   liner   r   s       r   rY   z!CodexAppServerClient._read_stdout5  s6   ::$	JTZZ..77= $zz|	**T*C s#!$ ++  ** **113D#J>B     	J"" J"")),B3%*HIJ J J J J	Jsq   0C# 
C# B3C# C &"C	C CC C# C  C# #	D;,D68D 	D6 D)	%D66D;c                   d|v r\d|v sd|v rT| j                   5  | j                  j                  |d   d       }d d d        	 |j                  j	                  |       y y d|v r d|v r| j                  j                  |       y d|v r| j                  j                  |       y y # 1 sw Y   nxY w# t        j
                  $ r Y y w xY w)Nr   rx   r   r$   )	rP   rM   r   r#   
put_nowaitFullrS   putrR   )r   r   pendings      r   r   zCodexAppServerClient._dispatchN  s    3;HOw#~## =--++CIt<="MM,,S1 3;8s?!!%%c*s?##C( = =
 zz s    B&B2 &B/2CCc                   | j                   j                  y 	 t        | j                   j                  j                  d      D ]  }|s y | j                  5  | j
                  j                  |j                  dd      j                                t        | j
                        dkD  r| j
                  dd  | _        d d d         y # 1 sw Y   xY w# t        $ r Y y w xY w)Nr   r   replacei  i)rK   r7   r   r   rU   rT   r   decoderstriplenr|   )r   r   s     r   r\   z!CodexAppServerClient._read_stderra  s    ::$	TZZ..77= 	G&& G&&--GY7>>@ 4--.4-1-?-?-F*G G	GG G  		s0   0C 
C A&C<
C C	C 	CC)codexNNN)
r^   r   r_   zOptional[str]r`   zOptional[list[str]]r9   zOptional[dict[str, str]]r   None)hermeszHermes Agentz0.1Ng      $@)rt   r   ru   r   rv   r   rl   Optional[dict]ro   r&   r   dict)g      @)ro   r&   r   r   )r   z'CodexAppServerClient')r   r   r   r   )Ng      >@)r$   r   rw   r   ro   r&   r   r   r   )r$   r   rw   r   r   r   )r   r   rx   r   r   r   )
r   r   r   r   r   r   r   r   r   r   )g        )ro   r&   r   r   )   )r   r   r   z	list[str])r   bool)r   r   )r   r   r   r   )r   r   )r   r   r   r   )r   r   r   r   rf   rm   r{   r   r   rr   rs   r   r   r   r   r   r   r   r   rY   r   r\   r    r   r   r*   r*   6   sO   $ !$(*.(,P$P$ "P$ (	P$
 &P$ 
P$l $*#'+  	
 %  
2( "&	%% % 	%
 
%<?9
 OS55%(5365>K5	5
1
)
J2)&r   r*   c                    ddl }|j                  d| xs d      }|syt        |j                  d            t        |j                  d            t        |j                  d            fS )zFParse `codex --version` output. Returns (major, minor, patch) or None.r   Nz(\d+)\.(\d+)\.(\d+)r   r:         )researchr   group)outputr   matchs      r   parse_codex_versionr   s  sV     II,fl;EAU[[^!4c%++a.6IJJr   r   c           
     J   	 t        j                  | dgdddt         j                        }|j
                  d	k7  r,dd
|j
                   d|j                  j                          fS t        |j                        }|dd|j                  fS ||k  rEdddj                  t        t        |             ddj                  t        t        |             dfS ddj                  t        t        |            fS # t        $ r dd| dfcY S t         j                  $ r Y yw xY w)z~Verify codex CLI is installed and meets minimum version.

    Returns (ok, message). Used by setup wizard and runtime startup.z	--versionT
   )capture_outputtextro   r5   Fzcodex CLI not found at z&. Install with: npm i -g @openai/codex)Fzcodex --version timed outr   zcodex --version exited r   z$could not parse codex version from: zcodex .z is older than required z. Run: npm i -g @openai/codex)rH   runDEVNULLFileNotFoundErrorr   
returncoder7   r   r   r6   rD   mapr   )r^   min_versionprocrj   s       r   check_codex_binaryr   ~  sA   2~~$$$
 !//@4;;CTCTCVBWXXX!$++.G<T[[OLLLSXXc#w/011IxxC-.//LN
 	
 #c7+,,,#  
%i] 3% &
 	
 $$ 212s   *C: :D"D"!D")r   r   r   zOptional[tuple[int, int, int]])r^   r   r   ztuple[int, int, int]r   ztuple[bool, str])r   
__future__r   r   rA   r#   rH   rN   r(   dataclassesr   r   typingr   r   tools.environments.localr   MIN_CODEX_VERSIONrq   r   r"   r*   r   r   r    r   r   <module>r      s     #  	     (   :    E, E E 6 6 6z zz	K BS--+?--r   