
    `gjݷ                    R   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mZm	Z	 ddl
mZmZmZ ddlmZ ddlmZ ddlmZmZ dd	lmZ  ej.                  e      Zd
ZdddddZe G d d             ZdZddZdZddZ e G d d             Z! G d d      Z"d dZ#d dZ$d!dZ%d"dZ&d#dZ'y)$uI  Session adapter for codex app-server runtime.

Owns one Codex thread per Hermes session. Drives `turn/start`, consumes
streaming notifications via CodexEventProjector, handles server-initiated
approval requests (apply_patch, exec command), translates cancellation,
and returns a clean turn result that AIAgent.run_conversation() can splice
into its `messages` list.

Lifecycle:
    session = CodexAppServerSession(cwd="/home/x/proj")
    session.ensure_started()                              # spawns + handshake + thread/start
    result = session.run_turn(user_input="hello")         # blocks until turn/completed
    # result.final_text          → assistant text returned to caller
    # result.projected_messages  → list of {role, content, ...} for messages list
    # result.tool_iterations     → how many tool-shaped items completed (skill nudge counter)
    # result.interrupted         → True if Ctrl+C / interrupt_requested fired mid-turn
    session.close()                                       # tears down subprocess

Threading model: the adapter is single-threaded from the caller's perspective.
The underlying CodexAppServerClient owns its own reader threads but exposes
blocking-with-timeout queues that this adapter polls in a loop, so the run_turn
call is synchronous and behaves like AIAgent's existing chat_completions loop.
    )annotationsN)	dataclassfield)AnyCallableOptional)_format_responses_error)redact_sensitive_text)CodexAppServerClientCodexAppServerError)CodexEventProjector   workspace-writezread-only-with-approvalzfull-access)autozapproval-requiredunrestrictedyoloc                      e Zd ZU dZdZded<    ee      Zded<   dZ	d	ed
<   dZ
ded<   dZded<   dZded<   dZded<   dZded<   dZded<   dZded<   dZded<   dZded<   y)
TurnResultuH   Result of one user→assistant→tool turn through the codex app-server. str
final_text)default_factoryz
list[dict]projected_messagesr   inttool_iterationsFboolinterruptedNOptional[str]errorturn_id	thread_idzOptional[dict[str, Any]]token_usage_lasttoken_usage_totalzOptional[int]model_context_window	compactedshould_retire)__name__
__module____qualname____doc__r   __annotations__r   listr   r   r   r   r    r!   r"   r#   r$   r%   r&        \/root/.hermes/venv/lib/python3.12/site-packages/agent/transports/codex_app_server_session.pyr   r   @   s    RJ%*4%@
@OSKE=!G]!#I}#15.526/6*.-.It  M4r.   r   )z<turn_aborted>z<turn_aborted/>c                p   t        | t              r| S t        | t              rg }| D ]  }t        |t              r"|j                         r|j	                  |       5t        |t
              s||j	                  t        |             b|j                  d      }|dv rF|j                  d      xs |j                  d      xs d}|s|j	                  t        |             |dv s|j	                  d        dj                  d	 |D              j                         }|xs d
S | dS t        |       S )aQ  Collapse Hermes/OpenAI rich content into app-server text input.

    The current `turn/start` path sends text items only. TUI image attachment
    can hand us OpenAI-style content parts, so keep the text/path hints and
    replace opaque image payloads with a small marker instead of putting a
    Python list into the `text` field.
    type>   text
input_textr2   contentr   >   image	image_urlinput_imagez[image attached]z

c              3  &   K   | ]	  }|s|  y wNr-   .0ps     r/   	<genexpr>z*_coerce_turn_input_text.<locals>.<genexpr>{   s     1q11   zWhat do you see in this image?)
isinstancer   r,   stripappenddictgetjoin)
user_inputpartsitem	item_typer2   s        r/   _coerce_turn_input_textrI   _   s    *c"*d# 	1D$$::<LL&dD)#LLT+(I22xx'D488I+>D"LLT+CC/0	1  {{1e11779777#28Z8r.   )invalid_grantzinvalid grantzrefresh tokenrefresh_tokenztoken refreshtoken_refreshztoken has expiredexpired_tokenzexpired tokenznot authenticatedunauthenticatedunauthorizedz401 unauthorizedzre-authenticatereauthenticatezplease log inzplease loginzauth profilezno auth profileoauthc                 z    dj                  d | D              j                         }|syt        D ]	  }||v s	  y y)u  Return a user-friendly re-auth hint if any of the provided strings
    look like a codex OAuth/token-refresh failure; otherwise None.

    Used for both `turn/start` JSON-RPC errors and post-mortem stderr
    inspection when the subprocess exits unexpectedly. Conservative on
    purpose — we only redirect users to `codex login` when the signal
    is strong, so unrelated runtime failures still surface verbatim.
     c              3  &   K   | ]	  }|s|  y wr9   r-   r:   s     r/   r=   z*_classify_oauth_failure.<locals>.<genexpr>   s     .aA.r>   Nu   Codex authentication failed — your ChatGPT/Codex login looks expired or invalid. Run `codex login` to refresh, then retry. (Fall back to default runtime with `/codex-runtime auto` if the issue persists.))rD   lower_OAUTH_REFRESH_FAILURE_HINTS)rF   haystackneedles      r/   _classify_oauth_failurerY      sN     xx.5..446H. X@ r.   c                  .    e Zd ZU dZdZded<   dZded<   y)_ServerRequestRoutinga  Default policies for codex-side approval requests when no interactive
    callback is wired in. These are only used by tests + cron / non-interactive
    contexts; the live CLI path passes an approval_callback that defers to
    tools.approval.prompt_dangerous_approval().Fr   auto_approve_execauto_approve_apply_patchN)r'   r(   r)   r*   r\   r+   r]   r-   r.   r/   r[   r[      s    3
 $t#%*d*r.   r[   c            	         e Zd ZdZddddddddd	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZddZddZddZdd	Zdd
Z		 de
d	 	 	 	 	 	 	 d dZdddd	 	 	 	 	 	 	 	 	 d!dZddd	 	 	 	 	 d"dZd#dZd$dZd%dZd%dZd&dZd'dZy)(CodexAppServerSessionuz  One Codex thread per Hermes session, lifetime owned by AIAgent.

    Not thread-safe — one caller drives it at a time, matching how AIAgent's
    run_conversation() loop is structured today. The codex client itself can
    handle interleaved reads/writes via its own threads, but the adapter's
    state (projector, thread_id, turn counter) is owned by the caller thread.
    Ncodex)cwd	codex_bin
codex_homepermission_profileapproval_callbackon_eventrequest_routingclient_factoryc                  |xs t        j                         | _        || _        || _        |xs4 t
        j                  t         j                  j                  dd      d      | _        || _	        || _
        |xs
 t               | _        |xs t        | _        d | _        d | _        t#        j$                         | _        i | _        d| _        y )NHERMES_TERMINAL_SECURITY_MODEr   r   F)osgetcwd_cwd
_codex_bin_codex_home#_HERMES_TO_CODEX_PERMISSION_PROFILErC   environ_permission_profile_approval_callback	_on_eventr[   _routingr   _client_factory_client
_thread_id	threadingEvent_interrupt_event_pending_file_changes_closed)	selfra   rb   rc   rd   re   rf   rg   rh   s	            r/   __init__zCodexAppServerSession.__init__   s     &299;	#% "E"I"I

>G!# 	  #4!'B+@+B-E1E7;)- ) 1 68"r.   c                   | j                   | j                   S | j                  ,| j                  | j                  | j                        | _        | j                  j                  ddt                      d| j                  i}| j                  j                  d|d	      }|j                  d
      xs i }|j                  d      xs7 |j                  d      xs$ |j                  d      xs |j                  d      }|s(t        ddt        |j                                d      || _         t        j                  d| j                   dd | j                  | j                         | j                   S )u   Spawn the subprocess, do the initialize handshake, and start a
        thread. Returns the codex thread id. Idempotent — repeated calls
        return the same thread id.N)rb   rc   hermeszHermes Agent)client_nameclient_titleclient_versionra   zthread/start   timeoutthreadid	sessionIdthreadIdiz8codex thread/start returned no thread id (payload keys: )codemessagez8codex app-server thread started: id=%s profile=%s cwd=%s   )rx   rw   rv   rn   ro   
initialize_get_hermes_versionrm   requestrC   r   sortedkeysloggerinforr   )r~   paramsresult
thread_objr!   s        r/   ensure_startedz$CodexAppServerSession.ensure_started   sg    ??&??"<<////d6F6F 0 DL 	 '.0 	  	
( #(!3%%nfb%I
 ZZ)/R
NN4  &~~k*&zz+&& zz*%	 	 %&&,V[[]&;%<A?  $FOOBQ$$II		
 r.   c                    | j                   ry d| _         | j                  "	 | j                  j                          d | _        d | _        y # t        $ r Y w xY w)NT)r}   rw   close	Exceptionrx   r~   s    r/   r   zCodexAppServerSession.close,  sV    <<<<#""$  DL  s   A 	AAc                    | S r9   r-   r   s    r/   	__enter__zCodexAppServerSession.__enter__8  s    r.   c                $    | j                          y r9   )r   )r~   excs     r/   __exit__zCodexAppServerSession.__exit__;  s    

r.   c                8    | j                   j                          y)zIdempotent: signal the active turn loop to issue turn/interrupt
        and unwind. Called by AIAgent's _interrupt_requested path.N)r{   setr   s    r/   request_interruptz'CodexAppServerSession.request_interrupt@  s     	!!#r.   
tail_linesc               R   |dk7  r|t        |      nd}|r| d| n|}| j                  |S 	 | j                  j                  |      }|s|S dj	                  d |D              }|j                         s|S t        |d      }| dt        |       d| S # t        $ r |cY S w xY w)	a  Build a user-facing error string for codex failures.

        Appends the last few lines of codex's stderr buffer when available,
        passed through agent.redact with force=True so secrets in provider
        error responses (auth headers, query-string tokens, sk-* keys) never
        leak into chat output or trajectories. The codex CLI's own error
        text ('Internal error', 'turn/start failed: ...') is otherwise
        opaque and forces users to re-run with verbose flags to diagnose
        config / provider / auth-bridge problems.

        Use this for the generic / catch-all branches. Specific
        classifications (OAuth via _classify_oauth_failure, post-tool wedge
        watchdog) already produce a clean hint and should be used instead.
        r   : 
c              3  B   K   | ]  }|s|j                           y wr9   )rstrip)r;   lines     r/   r=   zBCodexAppServerSession._format_error_with_stderr.<locals>.<genexpr>f  s     BTT4;;=Bs   T)forcez
codex stderr (last z	 lines):
)r   rw   stderr_tailr   rD   r@   r
   len)	r~   prefixr   r   exc_strbasetailjoinedredacteds	            r/   _format_error_with_stderrz/CodexAppServerSession._format_error_with_stderrG  s    * "RiCO#c()0&G9%f<<K	<<++J7D KBTBB||~K(t<,SYKz(LL  	K	s   B B&%B&g     @g      ?g     V@)turn_timeoutnotification_poll_timeoutpost_tool_quiet_timeoutc                  t               }	 | j                          | j                  | j                  J | j                  |_	        | j                  j                          t               }t        |      }	 | j                  j                  d| j                  d|dgdd	      }	|	j'                  d      xs i j'                  d      |_        t+        j,                         |z   }d}d}t+        j,                         |k  r|s| j                  j/                         r$| j1                  |j(                         d|_        n| j                  j5                         s`d
j                  | j                  j!                  d            }
t#        |
      }|||_        n| j	                  dd      |_        d|_        n4|Qt+        j,                         |z
  |kD  r7| j1                  |j(                         d|_        d|dd|_        d|_        n| j                  j7                  d	      }|Yt9        d      D ]5  }| j                  j;                  d	      }| n| j<                  	 | j=                  |       tE        ||       tG        ||       | jI                  |       |jK                  |      }|jL                  r%|jN                  jQ                  |jL                         |jR                  r)|xjT                  dz  c_*        t+        j,                         }|jV                  |jV                  |_+        tY        |jV                        sd}d|_        |j
                  xs d|_        8 | j[                  |       d}| j                  j;                  |	      }||j'                  dd      }| j<                  	 | j=                  |       tE        ||       tG        ||       | jI                  |       |jK                  |      }|jL                  r%|jN                  jQ                  |jL                         |jR                  r*|xjT                  dz  c_*        t+        j,                         }n|jL                  s|jV                  d}|jV                  D|jV                  |_+        tY        |jV                        rd}d|_        |j
                  xs d|_        |d k(  rd}|j'                  d!      xs i j'                  d      xs i j'                  d"      }|r|d#vr|j'                  d!      xs i j'                  d      xs i j'                  d$      }|rvt]        |t_        |            }d
j                  | j                  j!                  d            }
t#        ||
      }|||_        d|_        n| j	                  d%| |      |_        t+        j,                         |k  r|s|s;|j2                  s/|jV                  r#|j
                  t@        ja                  d&       d}|s[|j2                  sO| j1                  |j(                         d|_        |j
                  s| j	                  d'| d(      |_        d|_        |S # t        t        f$ r*}| j	                  d|      |_        d|_        |cY d}~S d}~ww xY w# t        $ rt}d
j                  | j                  j!                  d            }
t#        |j$                  |
      }|||_        d|_        n| j	                  d|      |_        |cY d}~S d}~wt        $ rc}d
j                  | j                  j!                  d            }
t#        |
      }|xs | j	                  d|      |_        d|_        |cY d}~S d}~ww xY w# t>        $ r t@        jC                  dd       Y Yw xY w# t>        $ r t@        jC                  dd       Y %w xY w))a  Send a user message and block until turn/completed, while
        forwarding server-initiated approval requests and projecting items
        into Hermes' messages shape.

        post_tool_quiet_timeout: if codex emits a tool completion and then
        goes quiet for this many seconds without emitting another item or
        `turn/completed`, fast-fail and mark the session for retirement.
        Mirrors openclaw beta.8's post-tool completion watchdog (#81697)
        so a wedged codex doesn't burn the full turn deadline.
        codex app-server startup failedTNz
turn/startr2   )r1   r2   )r   input
   r   r   (   zturn/start failedzturn/start timed outturnr   F<   /codex app-server subprocess exited unexpectedly   r   zcodex went silent for z.0fz3s after a tool result; retiring app-server session.r   r   on_event callback raisedexc_info   codex reported turn_abortedmethodr   turn/completedr   status>   	completedr   r   zturn ended status=zcodex app-server turn reached deadline after a completed assistant message but before turn/completed; accepting the assistant text as the terminal responsezturn timed out after s)1r   r   r   TimeoutErrorr   r   r&   rw   rx   r!   r{   clearr   rI   r   rD   r   rY   r   rC   r    time	monotonicis_set_issue_interruptr   is_alivetake_server_requestrangetake_notificationrt   r   r   debug_apply_token_usage_notification_apply_compaction_notification_track_pending_file_changeprojectmessagesr   extendis_tool_iterationr   r   _has_turn_aborted_marker_handle_server_requestr	   r   warning)r~   rE   r   r   r   r   r   	projectoruser_input_texttsstderr_blobhintdeadlineturn_completelast_tool_completion_atsreq_pendingprojnoter   
projectionturn_statuserr_objerr_msgs                            r/   run_turnzCodexAppServerSession.run_turnn  s~   . 		! ||'DOO,GGG??##%')	1*="	%% $'-GH  & BF &&..B33D9>>#l2
 48nn)-$$++-%%fnn5%)" <<((*"ii(@(@(DE.{;##'FL#'#A#AI#% $B $FL (,$ (3^^%(??-. %%fnn5%)",.s3 434 
 (,$ <<33A3>D
 q #A"ll<<Q<GG ~~1 NN73
 4FGD267C33G<$,,W5D}}1188G--..!3.26..2B/2,0OO)3DOOD,0M15F. & !A#@ #LA#H ++D1 +/'<<111 2 D |XXh+F~~)LNN4( ,FD9*648 ++D1 #**40J""))001D1DE++&&!+& +/..*:'
 &&**?*?*K.2+$$0 %/$9$9!
 ,J,A,AB$(M)-F&E(E L )) $XXh'-2226:@b#h-  ;6R#R(+1r66v>D"c'l  "9'3{CS"T '+ii LL44R8'  7wL++/FL37F0+/+I+I"4[M BG,FLK nn)-T &&!!$NN>
 !MV%7%7
 !!&..1!%F<<#==+L>;  $(F E $\2 	9913FL
 $(F M	6 # 	 ))DLL$<$<R$@AK*3;;DD#
 (,$#=='  M 	))DLL$<$<R$@AK*;7D 4#A#A&$FL $(F M	d  ) "LL :T ) L ! LLL!;dLKLss   Z 7/[	 ,^3_ [[;[[		^0A)];^0^0A^+%^0+^03 __ _>=_>)r   r   c                  t               }	 | j                          | j                  | j                  J | j                  |_	        | j                  j                          t               }	 | j                  j                  dd| j                  id       t%        j&                         |z   }d}	t%        j&                         |k  r|	s| j                  j)                         r$| j+                  |j,                         d|_        nZ| j                  j1                         s`dj                  | j                  j                  d            }t!        |      }|||_        n| j	                  dd      |_        d|_        n| j                  j3                  d      }
|
| j5                  |
       | j                  j7                  |      }|$|j9                  dd      }| j:                  	 | j;                  |       tC        ||       tE        ||       | jG                  |       |jI                  |      }|jJ                  r%|jL                  jO                  |jJ                         |jP                  r|xjR                  dz  c_)        |jT                  D|jT                  |_*        tW        |jT                        rd}	d|_        |j
                  xs d|_        |dk(  rN|j9                  d      xs i j9                  d      xs i }|j9                  d      xs |j,                  |_        n|dk(  rd}	|j9                  d      xs i j9                  d      xs i }|j9                  d      xs |j,                  |_        |j9                  d      }|dk(  rd|_        |j
                  xs d|_        n|r|d k7  r|j9                  d!      }tY        |t[        |            }dj                  | j                  j                  d	            }t!        ||      }|||_        d|_        n| j	                  d"| |      |_        t%        j&                         |k  r|	s|	s[|j.                  sO| j+                  |j,                         d|_        |j
                  s| j	                  d#| d$      |_        d|_        |S # t        t        f$ r*}| j	                  d|      |_        d|_        |cY d}~S d}~ww xY w# t        $ rt}dj                  | j                  j                  d	            }t!        |j"                  |      }|||_        d|_        n| j	                  d
|      |_        |cY d}~S d}~wt        $ rc}dj                  | j                  j                  d	            }t!        |      }|xs | j	                  d|      |_        d|_        |cY d}~S d}~ww xY w# t<        $ r t>        jA                  dd       Y w xY w)%an  Trigger Codex-native history compaction for the current thread.

        `thread/compact/start` returns immediately; the actual compaction
        progress streams through the same turn/item notifications as a normal
        turn. We wait for the matching `turn/completed` so callers can treat a
        successful return as a completed compaction boundary.
        r   TNzthread/compact/startr   r   r   r   r   zthread/compact/start failedzthread/compact/start timed outFr   r   r   r   r   r   r   r   r   r   r   zturn/startedr   r   r   r   r   r   zcompact turn interruptedr   r   zcompact turn ended status=zcompact turn timed out after r   ).r   r   r   r   r   r   r&   rw   rx   r!   r{   r   r   r   rD   r   rY   r   r   r   r   r   r    r   r   r   r   r   rC   rt   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   turn_objr   r   r   s                     r/   compact_threadz$CodexAppServerSession.compact_thread  sq    	! ||'DOO,GGG??##%')		LL  &T__- ! 2 >>#l2nn)-$$++-%%fnn5%)"<<((*"ii(@(@(DE.{;##'FL#'#A#AI#% $B $FL (,$<<33A3>D++D1<<111 2 D |XXh+F~~)LNN4( ,FD9*648++D1"**40J""))001D1DE++&&!+&$$0$.$9$9!+J,A,AB$(M)-F&E(E L ' HHX.4"99&AGR!)d!3!Ev~~++ $ HHX.4"99&AGR!)d!3!Ev~~&ll84-/)-F&#)<<#M3MFL [K%?&ll73G5gs;?OPG"&))DLL,D,DR,H"IK27KHD''+/3,'+'E'E8F#(W nn)-` V%7%7!!&..1!%F<<#==3L>C  $(F E $\2 	9913FL $(F M	$ # 
	))DLL$<$<R$@AK*3;;DD#'+$#==13  M 	))DLL$<$<R$@AK*;7D 4#A#A0#$FL $(F M	\ ! LLL!;dLKLsa   S ,*S= 2W' S:S5/S:5S:=	W$A)U5/W$5W$AWW$W$' X
Xc                (   | j                   | j                  |y 	 | j                   j                  d| j                  |dd       y # t        $ r }t        j                  d|       Y d }~y d }~wt        $ r t        j                  d       Y y w xY w)Nzturn/interrupt)r   turnId   r   zturn/interrupt non-fatal: %szturn/interrupt timed out)rw   rx   r   r   r   r   r   r   )r~   r    r   s      r/   r   z&CodexAppServerSession._issue_interrupt"  s    <<4??#:go
	7LL   !__@ ! 
 # 	>LL7== 	7NN56	7s   +A	 		BA--!BBc                   | j                   y|j                  dd      }|j                  d      }|j                  d      xs i }|dk(  r0| j                  |      }| j                   j                  |d|i       y|dk(  r0| j	                  |      }| j                   j                  |d|i       y|d	k(  r| j                   j                  |dd
i       y|dk(  r\|j                  d      xs d}|dk(  r!| j                   j                  |dddd       y| j                   j                  |d
ddd       yt
        j                  d|       | j                   j                  |dd|        y)ui  Translate a codex server request (approval) into Hermes' approval
        flow, then send the response.

        Method names verified live against codex 0.130.0 (Apr 2026):
          item/commandExecution/requestApproval — exec approvals
          item/fileChange/requestApproval       — apply_patch approvals
          item/permissions/requestApproval      — permissions changes
                                                  (we decline; user controls
                                                  permission profile in
                                                  ~/.codex/config.toml).
        Nr   r   r   r   z%item/commandExecution/requestApprovaldecisionzitem/fileChange/requestApprovalz item/permissions/requestApprovaldeclinezmcpServer/elicitation/request
serverNamezhermes-toolsaccept)actionr4   _metaz Unknown codex server request: %sizUnsupported method: r   )rw   rC   _decide_exec_approvalrespond_decide_apply_patch_approvalr   r   respond_error)r~   reqr   ridr   r   server_names          r/   r   z,CodexAppServerSession._handle_server_request1  sf    <<2&ggdm"(b<<11&9HLL  z8&<=8888@HLL  z8&<=99
 LL  z9&=>66 !**\28bKn,$$'D4H
 $$(TDI NN=vFLL&&&,@*I ' r.   c                   | j                   j                  ry|j                  d      xs d}|j                  d      xs | j                  xs d}|j                  d      }d| }|r|d| z  }| j                   	 | j	                  ||d	
      }t        |      S y# t        $ r t        j                  d       Y yw xY w)Nr  commandr   ra   z	<unknown>reasonzCodex requests exec in u    — Fallow_permanentz(approval_callback raised on exec requestr   )	ru   r\   rC   rm   rs   "_approval_choice_to_codex_decisionr   r   	exception)r~   r   r  ra   r  descriptionchoices          r/   r  z+CodexAppServerSession._decide_exec_approvalk  s    ==****Y'-2 jj;499;H%/u5U6(++K"".!00[% 1  :&AA   !  !KL !s   =B B>=B>c                (   | j                   j                  ry| j                  |j                  d      }|j                  d      }|j                  d      xs d}| j	                  |      }g }|r|j                  |       |r|j                  |       |r|j                  d|        |rdj                  |      nd}|rd	| n|rd	| nd
}	 | j                  ||d      }	t        |	      S y# t        $ r t        j                  d       Y yw xY w)Nr  r  	grantRootitemIdr   zgrants write to z; zCodex requests to apply a patchzapply_patch: apply_patchFr  z'approval_callback raised on apply_patchr   )ru   r]   rs   rC   _lookup_pending_file_changerA   rD   r  r   r   r  )
r~   r   r  
grant_rootitem_idchange_summarydescription_partsr  command_labelr  s
             r/   r  z2CodexAppServerSession._decide_apply_patch_approval  s8   ==11"".
 ZZ)FK0Jjj*0bG!==gFN "!((0!((8!((+;J<)HI % 		+,6  5C-/017}VH-" 
	!00!$) 1 
 :&AA   !  !JK !s   C0 0DDc                |   |j                  dd      }|j                  d      xs i }|j                  d      xs i }|j                  d      dk7  ry|j                  d      xs d}|sy|d	k(  r+|j                  d
      xs g }|sd| j                  |<   yi }g }|D ]|  }	t        |	t              s|	j                  d      xs i j                  d      xs d}
|j                  |
d      dz   ||
<   |	j                  d      xs d}|sl|j	                  |       ~ dj                  d t        |j                               D              }dj                  |dd       }t        |      dkD  r|dt        |      dz
   dz  }|r| d| n|| j                  |<   y|dk(  r| j                  j                  |d       yy)u   Maintain self._pending_file_changes from item/started + item/completed
        notifications. Lets the apply_patch approval prompt show what's
        actually changing — codex's approval params don't carry the data.r   r   r   rG   r1   
fileChangeNr   item/startedchangesz1 change pendingkindupdater   r   pathz, c              3  0   K   | ]  \  }}| d |   yw)rS   Nr-   )r;   kns      r/   r=   zCCodexAppServerSession._track_pending_file_change.<locals>.<genexpr>  s     Lda!AaSzLs      z, +z morer   item/completed)
rC   r|   r?   rB   rA   rD   r   itemsr   pop)r~   r   r   r   rG   r  r!  kindspathschr"  r<   countspreviews                 r/   r   z0CodexAppServerSession._track_pending_file_change  s    (B'(#)rzz&!'R88F|+((4.&B^#hhy)/RG6H**73$&E!E $!"d+v,"11&9EX#iia014dFF6N(bLLO$ YYLfU[[]6KLLFiibq	*G5zA~SUa 066*16("WI&v &&w/ ''&&**7D9 (r.   c                H    |sy| j                   j                  |      }|sy|S )a   Look up an in-progress fileChange item by id and summarize its
        changes for the approval prompt. Returns None when we don't have
        the item cached (e.g. approval arrived before item/started, or
        fileChange item content not tracked yet).N)r|   rC   )r~   r  cacheds      r/   r  z1CodexAppServerSession._lookup_pending_file_change  s+    
 ++//8r.   )ra   r   rb   r   rc   r   rd   r   re   zOptional[Callable[..., str]]rf   z Optional[Callable[[dict], None]]rg   zOptional[_ServerRequestRouting]rh   z-Optional[Callable[..., CodexAppServerClient]]returnNoner3  r   )r3  r4  )r3  z'CodexAppServerSession')r   r   r3  r4  )r   )r   r   r   r   r   r   r3  r   )
rE   r   r   floatr   r6  r   r6  r3  r   )r   r6  r   r6  r3  r   )r    r   r3  r4  )r  rB   r3  r4  )r   rB   r3  r   )r   rB   r3  r4  )r  r   r3  r   )r'   r(   r)   r*   r   r   r   r   r   r   _STDERR_TAIL_LINESr   r   r   r   r   r  r  r   r  r-   r.   r/   r_   r_      su    " $(,0:>59;?HL# # 	#
 "# *# 8# 3# 9# F# 
#N:x

$ #M
 -#M#M #M
 #M 
#MV $+/)-\\ 	\
 $)\ "'\ 
\B	 $+/	R R $)	R
 
Rl78t.'R#:J
r.   r_   c                   t        |t              r|j                  d      dk7  ry|j                  d      xs i }|j                  d      xs i }t        |t              sy|j                  d      }|j                  d      }t        |t              rt        |      | _        t        |t              rt        |      | _        |j                  d      }t        |t
              r|d	kD  r|| _        yyy)
zCapture Codex app-server token usage updates for caller accounting.

    Codex does not put token usage on turn/completed. It emits a separate
    thread/tokenUsage/updated notification containing cumulative totals and
    the latest turn breakdown.
    r   zthread/tokenUsage/updatedNr   
tokenUsagelasttotalmodelContextWindowr   )r?   rB   rC   r"   r#   r   r$   )r   r   r   token_usager:  r;  windows          r/   r   r     s     dD!TXXh%7;V%VXXh%2F**\*0bKk4(??6"DOOG$E$"&t*%#'; __12F&#6A:&,# $.r.   c                b   t        |t              sy|j                  d      xs d}|j                  d      xs i }t        |t              sy|dk(  rPd| _        |j                  d      xs | j                  | _        |j                  d      xs | j
                  | _        y|d	vry|j                  d
      xs i }t        |t              r|j                  d      dk7  ryd| _        |j                  d      xs | j                  | _        |j                  d      xs | j
                  | _        y)a  Capture Codex-native context compaction boundaries.

    Recent app-server builds expose compaction as a ContextCompaction item.
    Older builds also emit the deprecated thread/compacted notification. Both
    mean the underlying Codex thread history has been compacted.
    Nr   r   r   zthread/compactedTr   r   >   r   r)  rG   r1   contextCompaction)r?   rB   rC   r%   r!   r    )r   r   r   r   rG   s        r/   r   r     s    dD!XXh%2FXXh%2Ffd###!::j1EV5E5EH-?77::f#DdD!TXXf%59L%LFzz*-A1A1AFZZ);V^^FNr.   c                    | dv ry| dv ryy)aa  Map Hermes approval choices onto codex's CommandExecutionApprovalDecision
    / FileChangeApprovalDecision wire values.

    Hermes returns 'once', 'session', 'always', or 'deny'.
    Codex expects 'accept', 'acceptForSession', 'decline', or 'cancel'
    (verified against codex-rs/app-server-protocol/src/protocol/v2/item.rs
    on codex 0.130.0).
    >   oncer  >   alwayssessionacceptForSessionr   r-   )r  s    r/   r  r    s     &&!r.   c                ,    | syt         D ]  }|| v s y y)a  Return True if `text` contains any of the raw markers codex uses
    to signal a turn was aborted without emitting `turn/completed`.

    Codex emits `<turn_aborted>` (and sometimes `<turn_aborted/>`) as raw
    text inside agentMessage items when an interrupt or upstream error
    tears the turn down before the normal completion path fires. Mirrors
    openclaw beta.8's terminal-marker fix so we don't burn the full turn
    deadline waiting for a turn/completed that never comes.
    FT)_TURN_ABORTED_MARKERS)r2   markers     r/   r   r   %  s(     ' T> r.   c                 >    	 ddl m}   | d      S # t        $ r Y yw xY w)z=Best-effort Hermes version string for codex's userAgent line.r   versionzhermes-agentz0.0.0)importlib.metadatarK  r   rJ  s    r/   r   r   7  s'    .~&& s    	)rE   r   r3  r   )rF   r   r3  r   )r   r   r   rB   r3  r4  )r  r   r3  r   )r2   r   r3  r   r5  )(r*   
__future__r   loggingrk   ry   r   dataclassesr   r   typingr   r   r   agent.codex_responses_adapterr	   agent.redactr
   !agent.transports.codex_app_serverr   r   &agent.transports.codex_event_projectorr   	getLoggerr'   r   r7  rp   r   rG  rI   rV   rY   r[   r_   r   r   r  r   r   r-   r.   r/   <module>rV     s   0 #  	   ( * * A . G			8	$   2!' #      6 > 9N  0. + + +Z Zz-0<@ $r.   