
    epjV                      U d Z ddlmZ ddlZddlZddlZddlZddlZddlZddl	m	Z	 ddl
mZ ddlmZmZ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 ddlmZmZ ddlmZ ddl m!Z! ddl"m#Z#m$Z$m%Z%m&Z&  ej'        e(          Z)dZ*dZ+dZ, e-d e+D                       Z. e-d e,D                       Z/ ej0        dej1        ej2        z            Z3 ej0        dd4                    e+           dej1        ej2        z            Z5 ej0        dd4                    e+           dej2                  Z6 ej0        dd4                    e,           dej2                  Z7d Z8 e9h d          Z:dd$Z;dd)Z<dddd*dd0Z=i Z>d1e?d2<    ej@                    ZAdd4ZBd5 ZCdd7ZDdd8ZEdd:ZFdd<ZGddd=ddFZHddKZIdLdMddOZJddPZK e9h dQ          ZLddSZMddTdd[ZNddddd\ddaZOddcZPdddZQdddgZRddhddlZSddddd\ddmZTddqZUddsZV	 	 	 	 	 ddd}ZWddZXdZYddZZddZ[ddZ\	 dddZ]ddZ^ddZ_ddZ`ddZaddZbddZcddZdddZeddZfddZgddZhg dZidS )u  Assorted AIAgent runtime helpers — moved out of run_agent.py for clarity.

Each function takes the parent ``AIAgent`` as its first argument
(``agent``) except for the static helpers (``sanitize_tool_call_arguments``,
``drop_thinking_only_and_merge_users``) which are stateless.  AIAgent
keeps thin forwarders for backward compatibility.

Methods covered:
* ``convert_to_trajectory_format`` — internal -> trajectory-file format
* ``sanitize_tool_call_arguments`` — repair corrupted JSON in tool_calls
* ``repair_message_sequence`` — enforce alternation invariants
* ``strip_think_blocks`` — remove inline reasoning from stored content
* ``recover_with_credential_pool`` — rotate pool entries on 429
* ``try_recover_primary_transport`` — re-create OpenAI client after rate-limit
* ``drop_thinking_only_and_merge_users`` — Anthropic-style cleanup
* ``restore_primary_runtime`` — un-do fallback activation
* ``extract_reasoning`` — pull reasoning fields out of API responses
* ``dump_api_request_debug`` — write request body for post-mortem
* ``anthropic_prompt_cache_policy`` — compute cache_control breakpoints
* ``create_openai_client`` — build the per-agent OpenAI SDK client
    )annotationsN)datetime)Path)AnyDictListOptionalTuple)get_provider_request_timeout)format_steer_marker)_trajectory_normalize_msgmake_tool_result_message)convert_scratchpad_to_think)STATUS_EXHAUSTED credential_pool_matches_provider)FailoverReason)drop_stale_api_content)base_url_host_matchesbase_url_hostnameenv_var_enabledatomic_json_write   )thinkthinking	reasoningREASONING_SCRATCHPADthought)	tool_call
tool_callstool_resultfunction_callfunction_callsc              #  |   K   | ]7}t          j        d | d| dt           j        t           j        z            V  8dS )<z>.*?</>NrecompileDOTALL
IGNORECASE.0names     A/home/thesage/.hermes/hermes-agent/agent/agent_runtime_helpers.py	<genexpr>r/   :   s_       " " J'D'''''R])BCC" " " " " "    c              #  |   K   | ]7}t          j        d | d| dt           j        t           j        z            V  8dS )r$   z\b[^>]*>.*?</r%   Nr&   r+   s     r.   r/   r/   ?   s_       " " J.D..t...	BM0IJJ" " " " " "r0   zd(?:(?<=^)|(?<=[\n\r.!?:]))[ \t]*<function\b[^>]*\bname\s*=[^>]*>(?:(?:(?!</function>).)*)</function>z(?:^|\n)[ \t]*<(?:|z)\b[^>]*>.*$z</?(?:z)>\s*z</(?:z|function)>\s*c                     ddl } | S )z4Lazy ``run_agent`` reference for test-patch routing.r   N	run_agentr4   s    r.   _rar6   ^   s    r0   >   todomemoryclarifydelegate_taskread_terminalsession_searchagentr   function_namestrreturnboolc                    |t           v rdS t          | dd          r|| j        v rdS t          | dd          }t          |o|                    |                    S )zBReturn True when an agent-level tool path emits its own post hook.T_context_engine_tool_namesN_memory_manager)"AGENT_RUNTIME_POST_HOOK_TOOL_NAMESgetattrrC   rA   has_tool)r=   r>   memory_managers      r.   !agent_runtime_owns_post_tool_hookrI   i   sm    :::tu2D99 muOo>o>otU$5t<<NI>#:#:=#I#IJJJr0   messagesList[Dict[str, Any]]
user_query	completedc                	   d |D             }g }d|                                   d}|                    d|d           |                    d|d           d}|t          |          k     r||         }|d         d	k    rd
|v r1|d
         r(d}|                    d          r&|d                                         rd|d          d}|                    d          r5|d                                         r|t          |d                   dz   z  }|d
         D ]}	|	rt          |	t                    s	 t          |	d         d         t                    r t          j
        |	d         d                   n|	d         d         }
nF# t          j        $ r4 t                              d|	d         d         dd                    i }
Y nw xY w|	d         d         |
d}|dt          j        |d           dz  }d|vrd|z   }|                    d|                                d           g }|dz   }|t          |          k     r8||         d         dk    r%||         }d }|d         }	 |                                                    d!          rt          j
        |          }n# t          j        t"          f$ r Y nw xY wt          |          }|t          |d
                   k     r|d
         |         d         d         nd"}|t          j        |                    d#d          ||d$d          z  }|d%z  }|                    |           |dz  }|t          |          k     r||         d         dk    %|r0|                    dd                    |          d           |dz
  }nd}|                    d          r&|d                                         rd|d          d}|d         pd}|t          |          z  }d|vrd|z   }|                    d|                                d           n*|d         d&k    r|                    d|d         d           |dz  }|t          |          k     |S )'aQ  
    Convert internal message format to trajectory format for saving.
    
    Args:
        messages (List[Dict]): Internal message history
        user_query (str): Original user query
        completed (bool): Whether the conversation completed successfully
        
    Returns:
        List[Dict]: Messages in trajectory format
    c                ,    g | ]}t          |          S  )r   r,   ms     r.   
<listcomp>z0convert_to_trajectory_format.<locals>.<listcomp>   s!    ???)!,,???r0   a  You are a function calling AI model. You are provided with function signatures within <tools> </tools> XML tags. You may call one or more functions to assist with the user query. If available tools are not relevant in assisting with user query, just respond in natural conversational language. Don't make assumptions about what values to plug into functions. After calling & executing the functions, you will be provided with function results within <tool_response> </tool_response> XML tags. Here are the available tools:
<tools>
a  
</tools>
For each function call return a JSON object, with the following pydantic model json schema for each:
{'title': 'FunctionCall', 'type': 'object', 'properties': {'name': {'title': 'Name', 'type': 'string'}, 'arguments': {'title': 'Arguments', 'type': 'object'}}, 'required': ['name', 'arguments']}
Each function call should be enclosed within <tool_call> </tool_call> XML tags.
Example:
<tool_call>
{'name': <function-name>,'arguments': <args-dict>}
</tool_call>system)fromvaluehuman   role	assistantr    r   z<think>
z

</think>
content
function	argumentsz4Unexpected invalid JSON in trajectory conversion: %sNd   r-   r-   r_   z<tool_call>
Fensure_asciiz
</tool_call>
z<think>z<think>
</think>
gpttoolz<tool_response>
){[unknowntool_call_id)ri   r-   r\   z
</tool_response>user) _format_tools_for_system_messageappendlengetstripr   
isinstancedictr?   jsonloadsJSONDecodeErrorloggerwarningdumpsrstrip
startswithAttributeErrorjoin)r=   rJ   rL   rM   
trajectory
system_msgimsgr\   r   r_   tool_call_jsontool_responsesjtool_msgtool_responsetool_content
tool_index	tool_nameraw_contents                       r.   convert_to_trajectory_formatr   s   s'    @?h???HJ
	b
 ::<<
	b 
	b 
	b            	
A
c(mm

qkv;+%%s""s<'8"  77;'' IC,<,B,B,D,D IH#k*:HHHG779%% R#i.*>*>*@*@ R :3y>JJTQQG "%\!2 p pI$QJy$,G,GQ'V`ajkuav  xC  bD  FI  WJ  WJ  %rDJy/D[/Q$R$R$R  PY  Zd  Pe  fq  Pr		/ ' ' ' ']_his_t  vA  `B  CG  DG  CG  `H  I  I  I$&				' !** 5f =%.& &N otz.W\/]/]/]ooooGG G++3g=G!!!$^^--# #    "$E#h--''HQK,?6,I,I'{H$7M $,I#6L'--//:::FF D+/:l+C+CL 0.A    "%^!4!4J &C,=(>(>>> L)*5jA&II& 
 "TZ(0^R(H(H )#/1 1 %*	&+ &+ &+ +M
 "%99M"))-888FA5 #h--''HQK,?6,I,I: " %% &!%>!:!:' '    AA
  77;'' IC,<,B,B,D,D IH#k*:HHHG ")n26{CCC G++3g=G!!!$]]__# #    
 [F""Y    
 	
Qi c(mm

l s&   :AF

A GG3;J/ /KK)ru   
session_idcursorlistr   r   Optional[dict]intc          
     @   |pt          j        t                    }t          | t                    sdS d}||                    d          }t          |t                    r[t          t          |          t          |                     }||k     r+| |         ||         u r|dz  }||k     r| |         ||         u d}t                      j	        j
        dfd	}	|}
|
t          |           k     r| |
         }t          |t                    r|                    d
          dk    r|
dz  }
P|                    d          }t          |t                    r|s|
dz  }
|
dz   }|D ]}t          |t                    s|                    d          }t          |t                    sD|                    d          }||dk    rd|d<   gt          |t                    r|                                sd|d<   t          |t                    s	 t          j        |           # t          j        $ r9 t                      j	                            |          pd}|                    dd          }|dd         }|                    d|pd|
|pd||           d|d<   d}|
dz   }|t          |           k     rk| |         }t          |t                    r|                    d
          dk    rn4|                    d          |k    r|}n|dz  }|t          |           k     k|3|                     |t)          |dk    r|nd|                     |dz  }n |	|           |dz  }Y 
w xY w|
dz  }
|
t          |           k     || dd         |d<   |S )uW  Repair corrupted assistant tool-call argument JSON in-place.

    ``cursor`` (optional) is a caller-owned dict used to skip re-validating
    messages already validated on a previous call.  It stores, under
    ``"prefix"``, the exact message *objects* (strong references) validated
    last time, in order.  On the next call, the longest contiguous prefix of
    ``messages`` whose objects are ``is``-identical to the stored prefix is
    skipped; scanning starts at the first divergence (conservative: any
    reordering, truncation, compression rewrite, or mid-list insertion breaks
    identity at that index and everything from there is re-scanned).

    Safety argument for skipping: a message in the matched prefix was fully
    scanned before — every tool_call argument was either already valid JSON
    or was rewritten to ``"{}"`` (valid).  The only code paths that mutate
    ``function["arguments"]`` on live history dicts between calls are the
    surrogate / non-ASCII sanitizers, which substitute characters *inside*
    JSON string values and cannot invalidate JSON syntax.  Compression,
    repair, undo, and steer paths replace or reorder message dicts, which
    breaks the identity match and forces a re-scan.  Holding strong
    references (the objects themselves, not ``id()``s) makes address reuse
    aliasing (#50372-style) impossible.
    r   NprefixrX   r   rq   r@   Nonec                >   |                      d          }t          |t                    r)|s| d<   n|                              s
 d| | d<   d S || d<   d S 	 t	          j        |          }n# t          $ r t          |          }Y nw xY w d| | d<   d S )Nr\   r]   )rn   rp   r?   ry   rr   rw   	TypeError)r   existingexisting_textmarkers      r.   _prepend_markerz5sanitize_tool_call_arguments.<locals>._prepend_markerJ  s    <<	**h$$ 	 >&,##((00 >)/&=&=8&=&=#F"(HYF	* Jx00MM 	* 	* 	*MMMMM	*!'::=::s   A4 4BBrY   rZ   r   r^   r_   r[   {}r-   ?P   z~Corrupted tool_call arguments repaired before request (session=%s, message_index=%s, tool_call_id=%s, function=%s, preview=%r)-re   ri   )r   rq   r@   r   )logging	getLogger__name__rp   r   rn   minrm   r6   AIAgent&_TOOL_CALL_ARGUMENTS_CORRUPTION_MARKERrq   r?   ro   rr   rs   rt   _get_tool_call_id_staticrv   insertr   )rJ   ru   r   r   logstart_indexprev_prefixlimitrepairedr   message_indexr   r   	insert_atr   r^   r_   ri   r>   previewexisting_tool_msg
scan_index	candidater   s                          @r.   sanitize_tool_call_argumentsr     sK   : 
/G%h//Ch%% qKjj**k4(( 	!K((#h--88E%%(;*?;{C[*[*[q  %%(;*?;{C[*[*[ HUU]AF; ; ; ; ; ;"  M
#h--
'
'}%#t$$ 	;(F(FQMWW\**
*d++ 	: 	QM!A%	# @	 @	Ii..  }}Z00Hh--  [11I IOO(,%)S)) )//2C2C (,%i-- /
9%%%%' - - -  #uu}EEiPPXTX (VS 9 9#CRC._%#! 'C!   )-%$(!*Q.
 3x==00 ( 4I%i66 )--:O:OSY:Y:Y }}^44DD,5)!OJ !3x==00 %,OO!0-:c-A-AMMr"(    NII#O$5666A[-^ 	] #h--
'
'`  $AAA;xOs   H**EM32M3zDict[str, Tuple[str, float]]_INFLIGHT_TURNS_BY_SESSIONturn_idc                   t          | dd          }t          | dd          }|| _        t          j                    | _        d}|rO||k    rIt                              d|||rt          j                    |z
  ndt          | dd          pd           |}t          | dd          }|rt          | d	d
          st          j                    }t          5  t                              |          }||ft          |<   ddd           n# 1 swxY w Y   || _	        |rM|d         ||fvrAt                              d||d         |d         r||d         z
  nd|           |p|d         }|S )u  Tripwire: detect a turn starting while a previous turn of the same
    agent — or of the same underlying *session* on a different agent object —
    has not completed its turn-end persist.

    Two turns interleaving on one session corrupt the durable transcript:
    their flushes race (user rows can persist out of arrival order), a row
    can be swallowed by the identity-marker dedup over shared history dicts,
    and the second turn runs on a history base that never saw the first
    turn's exchange. This helper does NOT prevent any of that — it names the
    occurrence, with both turn ids, so the dispatch route that let the
    second turn through the busy guard can be identified from logs.

    Returns the previous in-flight turn_id when an overlap is detected,
    else None. Takes ownership of the in-flight slot either way, so a turn
    that crashed before its persist produces at most one warning._inflight_turn_idN_inflight_turn_startedg        u   turn %s starting while turn %s (started %.0fs ago) has not completed its turn-end persist (session=%s) — concurrent turns on one session; transcript writes may interleaveg      r   r   _persist_disabledFr   u   turn %s starting while turn %s (started %.0fs ago) is still in flight on session %s under a different agent object — two routing keys are mapped to one session_id; concurrent turns on one session; transcript writes may interleaverX   )
rF   r   timer   ru   rv   _INFLIGHT_TURNS_LOCKr   rn   _inflight_turn_session_id)r=   r   prevprev_startedoverlapr   nowentrys           r.   note_turn_startr     s     5-t44D5":C@@L%E#'9;;E G 
? *6@DIKK,&&DE<..5#	
 	
 	
  d33J *'%)<eDD *ikk! 	D 	D.22:>>E6=s^&z2	D 	D 	D 	D 	D 	D 	D 	D 	D 	D 	D 	D 	D 	D 	D +5' 	*U1Xgt_44NNI a"'(4eAh	 	 	 )qGNs   'DD
D
c                    d| _         t          | dd          s^t          | dd          pt          | dd          }|r:t          5  t                              |d           ddd           n# 1 swxY w Y   d| _        dS )ud  Clear the in-flight marker at turn-end persist (see note_turn_start).

    Called from the single persist funnel; unconditional by design — when two
    turns genuinely overlap, the first persist clears the second turn's slot
    and the tripwire under-reports instead of double-reporting. A diagnostic
    must never be noisier than the defect it hunts.Nr   Fr   r   )r   rF   r   r   popr   )r=   r   s     r.   note_turn_persistedr     s     #E
 5-u55 AU$?FF 
'<K
 K

  	A% A A*..z4@@@A A A A A A A A A A A A A A A&*E###s   A,,A03A0
List[Dict]c                $	   |sdS d}dd}dd}g }|D ]}|rt          |t                    r|                    d          d	k    rt          |d
         t                    r|d
                             d          d	k    rz ||          sn ||d
                   s\|d
         } ||          r||d
<   |dz  }t          |                    d          pg           }t          |                    d          pg           }	|	r	||	z   |d<   n|r||d<   |                    d          }
|                    d          }t          |
t                    r`t          |t                    rKd                    d |
                                |                                fD                       }||d<   n	|
s|||d<   |                    d          s |                    d          r|d         |d<   |dz  }|                    |           t                      }g }|D ]K}t          |t                    s|                    |           .|                    d          }|d	k    rt                      }|                    d          pg D ]I}t          |t                    sdD ].}|                    |          }|r|	                    |           /J|                    |           |dk    rN|                    d          }|r0||v r,|                    |           |
                    |           |dz  }"|dk    rt                      }|                    |           Mg }|D ]}|rt          |t                    r|                    d          dk    rt          |d
         t                    r|d
                             d          dk    r|d
         }|                    dd          }
|                    dd          }t          |
t                    r=t          |t                    r(|
r
|r|
dz   |z   n|
p||d<   t          |           |dz  }|                    |           
|dk    r||dd<   |S )u  Collapse malformed role-alternation left in the live history.

    Providers (OpenAI, OpenRouter, Anthropic) expect strict alternation:
    after the system message, user/tool alternates with assistant, with
    no two consecutive user messages and no tool-result that doesn't
    follow an assistant-with-tool_calls. Violations cause silent empty
    responses on most providers, which triggers the empty-retry loop.

    This runs right before the API call as a defensive belt — by the
    time it fires, the scaffolding strip should already have prevented
    most shapes, but external callers (gateway multi-queue replay,
    session resume, cron, explicit conversation_history passed in by
    host code) can feed in already-broken histories.

    Repairs applied:
      0. Consecutive ``assistant`` messages with no intervening
         ``tool``/``user`` turn — merged into a single assistant turn
         (union of ``tool_calls``, concatenated ``content``). Strict
         OpenAI-compatible providers (DeepSeek v4, Moonshot/Kimi) reject
         a history where an ``assistant`` message carrying ``tool_calls``
         is immediately followed by another ``assistant`` message instead
         of its ``tool`` results — HTTP 400 "An assistant message with
         'tool_calls' must be followed by tool messages…". The split
         shape is produced by recovery/continuation paths that append an
         interim assistant turn (thinking-prefill, codex
         incomplete-continuation) or by host-fed / legacy-persisted /
         resumed histories. Refs #29148, #49147.
      1. Stray ``tool`` messages whose ``tool_call_id`` doesn't match
         any preceding assistant tool_call — dropped.
      2. Consecutive ``user`` messages — merged with newline separator
         so no user input is lost.

    Deliberately does NOT rewind orphan ``assistant(tool_calls)+tool``
    pairs that precede a user message — that pattern IS valid when the
    previous turn completed normally and the user jumped in to redirect
    before the model got a continuation turn (the ongoing dialog
    pattern). The empty-response scaffolding stripper handles the
    genuinely-broken variant via its flag-gated rewind.

    Returns the number of repairs made (for logging/telemetry).
    r   rR   r   r@   rA   c                    t          |                     d          p-|                     d          p|                     d          dk              S )Ncodex_reasoning_itemscodex_message_itemsfinish_reason
incomplete)rA   rn   rR   s    r.   _is_codex_interimz2repair_message_sequence.<locals>._is_codex_interimY  sR    EE)** 6uu*++6uu_%%5
 
 	
r0   c                0    |                      d          dv S )Nr   >   verify_hook_continueverification_required)rn   r   s    r.   _is_verification_candidatez;repair_message_sequence.<locals>._is_verification_candidate`  s     uu_%% *
 
 	
r0   rY   rZ   rX   r   r\   r]   c              3     K   | ]}||V  	d S NrP   )r,   ps     r.   r/   z*repair_message_sequence.<locals>.<genexpr>  s;       # #a## # # # # #r0   Nreasoning_content)idcall_idre   ri   rj   r[   

)rR   r   r@   rA   )rp   rq   rn   r   r?   r{   ro   rl   setadddiscardr   )r=   rJ   repairsr   r   	collapsedr   r   
prev_calls	new_callsprev_contentnew_contentjoinedknown_tool_idsfilteredrY   tckeytc_idmergeds                       r.   repair_message_sequencer     sc   T  qG 
 
 
 

 
 
 
 I 0 0.	3%%.	 ;..9R=$// /"!!&))[88%%c** 9%%im44 9 R=D *)$//  #	"1dhh|44:;;JSWW\228b99I 0%/)%;\"" 0%/\"  88I..L''),,K,,, .K1M1M . # # , 2 2 4 4k6G6G6I6IJ# # #   #)Y! .k&="-Y
 88/00 ESWW=P5Q5Q E,/0C,D()qLG( %%NH !! !!#t$$ 	OOC   wwv; UUNww|,,2 2 2!"d++ , 2 2CFF3KKE 2&**51112 OOC    V^^GGN++E 	.00$$$ &&u----1v~~ "%OOC     F  	3%%	 6))6":t,, *r
v&&&00":D88Ir22L'')R00K ,,, K1M1M  $7(37\F*[88&5+ Y 't,,,1c{{ Nr0   c                l   dt          | dd          }t          |t                    r|dk    rd |d|         D             t          | |          }|dk    rZt	          | d          rJ!t          fd|D                       | _        n't          | j        t          |                    | _        |S )u  Run :func:`repair_message_sequence` and keep the SessionDB flush
    cursor consistent with the compacted list (#44837).

    ``repair_message_sequence`` merges/drops messages in place, shrinking
    the list. ``_last_flushed_db_idx`` (the DB-write cursor) indexes into
    that list, so after compaction it can point past the new end — the
    turn-end flush would then skip the assistant/tool chain entirely — or
    past unflushed messages shifted to lower indexes.

    Repair preserves object identity for surviving messages, so counting
    the survivors from the previously-flushed prefix gives the exact new
    cursor even when messages are dropped/merged at indexes *before* the
    cursor — a plain ``min()`` clamp would silently skip that many
    unflushed rows. Falls back to the clamp when no prefix snapshot is
    available.

    Returns the number of repairs made (same as ``repair_message_sequence``).
    N_last_flushed_db_idxr   c                ,    h | ]}t          |          S rP   r   rQ   s     r.   	<setcomp>z6repair_message_sequence_with_cursor.<locals>.<setcomp>  s    !I!I!IA"Q%%!I!I!Ir0   c              3  >   K   | ]}t          |          v d V  dS )rX   Nr   )r,   rR   pre_repair_flushed_idss     r.   r/   z6repair_message_sequence_with_cursor.<locals>.<genexpr>  s=       - -r!uu0F'F'F'F'F'F'F- -r0   )	rF   rp   r   r   hasattrsumr   r   rm   )r=   rJ   flush_cursorr   r   s       @r.   #repair_message_sequence_with_cursorr     s    & "5"8$??L,$$ J)9)9!I!I-<-1H!I!I!I%eX66G{{wu&<=={!-), - - - -#- - - * *E&& *-*CMM* *E& Nr0   r\   c                V   |sdS t          |t                    sbt          |t                    rg }|D ]}t          |t                    r|                    |           -t          |t                    rt          |                    d          pd                                                                          }|dv r|                    d          }t          |t                    r|r|                    |           d                    |          }n^t          |t                    r:t          |                    d          p|                    d          pd          }nt          |          }|sdS t          D ]}|
                    d|          }t          D ]}|
                    d|          }t          
                    d|          }t          
                    d|          }t          
                    d|          }t          
                    d|          }|S )uS  Remove reasoning/thinking blocks from content, returning only visible text.

    Handles four cases:
      1. Closed tag pairs (`` <think>… ``) — the common path when
         the provider emits complete reasoning blocks.
      2. Unterminated open tag at a block boundary (start of text or
         after a newline) — e.g. MiniMax M2.7 / NIM endpoints where the
         closing tag is dropped.  Everything from the open tag to end
         of string is stripped.  The block-boundary check mirrors
         ``gateway/stream_consumer.py``'s filter so models that mention
         `` <think>`` in prose aren't over-stripped.
      3. Stray orphan open/close tags that slip through.
      4. Tag variants: `` <think>``, ``<thinking>``, ``<reasoning>``,
         ``<REASONING_SCRATCHPAD>``, ``<thought>`` (Gemma 4), all
         case-insensitive.

    Additionally strips standalone tool-call XML blocks that some open
    models (notably Gemma variants on OpenRouter) emit inside assistant
    content instead of via the structured ``tool_calls`` field:
      * ``<tool_call>…</tool_call>``
      * ``<tool_calls>…</tool_calls>``
      * ``<tool_result>…</tool_result>``
      * ``<function_call>…</function_call>``
      * ``<function_calls>…</function_calls>``
      * ``<function name="…">…</function>`` (Gemma style)
    Ported from openclaw/openclaw#67318. The ``<function>`` variant is
    boundary-gated (only strips when the tag sits at start-of-line or
    after punctuation and carries a ``name="..."`` attribute) so prose
    mentions like "Use <function> in JavaScript" are preserved.
    r[   type>   r   r   redacted_thinkingtextr\   )rp   r?   r   rl   rq   rn   ro   lowerr{   _REASONING_BLOCK_PATTERNSsub_TOOL_CALL_BLOCK_PATTERNS_NAMED_FUNCTION_BLOCK_PATTERN%_UNTERMINATED_REASONING_BLOCK_PATTERN_ORPHAN_REASONING_TAG_PATTERN_STRAY_TOOL_CALL_CLOSER_PATTERN)r=   r\   _parts_part_ptype_text_patterns          r.   strip_think_blocksr    s   >  r gs## gt$$ 	# "F  - -eS)) -MM%((((t,, 	- 6!2!2!8b99??AAGGIIF !OOO !IIf--E!%-- -% -e,,,ggfooGG&& 	#'++f--MY1G1GM2NNGG'llG 	2 . , ,,,r7++ . , ,,,r7++ ,//G<<G
 477GDDG+//G<<G
 .11"g>>GNr0   r   c                    t          | dd          }	 |$|                    t          | dd                    nd| _        dS # t          $ r d| _        Y dS w xY w)a  Rebind ``agent._credential_pool_entry_id`` from the current pool + key.

    OAuth refreshes can replace the runtime token before a failed request is
    recovered, so the mutable API-key value alone cannot reliably attribute
    the failure to its source entry.  This resolves the stable pool-entry ID
    for the agent's current ``api_key`` and clears it when no pool is bound.
    _credential_poolNapi_key)rF   entry_id_for_api_key_credential_pool_entry_id	Exception)r=   pools     r.   sync_credential_pool_entry_idr    s     5,d33D/  %%geY&E&EFFF 	'''
  / / /*.''''/s   -A AA)classified_reasonerror_contextstatus_codeOptional[int]has_retried_429r  Optional[FailoverReason]r  Optional[Dict[str, Any]]tuple[bool, bool]c          	        ! | j         !!d|fS t          | dd          pd                                                                }t          !dd          pd                                                                }|r||k    rd}|dk    r|                    d          r	 ddlm} t          | d	d          pd                                }	t          |	          o4 ||	          pd                                                                |k    }n# t          $ r d}Y nw xY w|s,t                      j
                            d
||           d|fS t          | dd          pdt          | dd          }
t          |
t                    r|
r|
nd sS!                                }|r=t          |dd           s*t          |dd          }t          |t                    r|r| d> !fd}|}|6|dk    rt          j        }n#|dk    rt          j        }n|dv rt          j        }|t          j        k    rorpi                     d          nd}|r(t                      j
                            d|           n&t                      j
                            d           d|fS |t          j        k    re||nd} ||          }|Nt                      j
                            d|t          |dd                     |                     |           dS d|fS |t          j        k    rd} r.t/           fd!                                D             d          }r0|p-t/          fd!                                D             d          }|!                                }|rt          |dd          nd}|t2          k    rt                      j
                            d|           ||nd} ||          }|Nt                      j
                            d|t          |dd                     |                     |           dS d S d}r|t                              d!          pd                                          }t                              d"          pd                                          }d#|v pd$|v pd%|v pd&|v }|s|sd S ||nd} ||          }|Nt                      j
                            d'|t          |dd                     |                     |           dS d S |t          j        k    r/|                     |          }d(                    fd)d*D                       }|s|d+k    rd,|v rd-}|s*|d+k    r$| j        pdd.k    rt          | d/d          d0k    rd-}|s|d+k    r| j        pdd1k    rd2|v pd3|v }|sd-}|r7t                      j
                            d4||nd5| j        pd           d|fS d6i} r |d7<    !j        d?i |}|t          |dd          }|t          | d8d          }|	i }|| _        | j        |f}|                    |d          d9z   ||<   ||         t>          k    r5t                      j
                            d:||         d9z
  |           d|fS t                      j
                            d;t          |dd                     |                     |           d-|fS ||nd<} ||          }|Nt                      j
                            d=|t          |dd                     |                     |           dS d|fS )@a  Attempt credential recovery via pool rotation.

    Returns (recovered, has_retried_429).
    On rate limits: first occurrence retries same credential (sets flag True).
                    second consecutive failure rotates to next credential.
    On billing exhaustion: immediately rotates.
    On auth failures: attempts token refresh before rotating.

    `classified_reason` lets the recovery path honor the structured error
    classifier instead of relying only on raw HTTP codes. This matters for
    providers that surface billing/rate-limit/auth conditions under a
    different status code, such as Anthropic returning HTTP 400 for
    "out of extra usage".
    NFproviderr[   customcustom:r   get_custom_provider_pool_keybase_urluu   Credential pool provider mismatch: pool=%s, agent=%s — skipping pool mutation to avoid cross-provider contaminationr  r	  runtime_api_keyr   rotate_statusr   c                8    | d}r|d<    j         di |S )N)r  r  api_key_hintcredential_idrP   )mark_exhausted_and_rotate)r  kwargs_api_key_hint_credential_idr  r  s     r.   _rotate_failed_credentialz?recover_with_credential_pool.<locals>._rotate_failed_credential  sE    (*)
 

  	5&4F?#-t-77777r0   i  i  >       upstream_providerun   Upstream provider %s rate-limited via aggregator — skipping credential rotation, deferring to fallback chainuh   Upstream aggregator 429 (provider unknown) — skipping credential rotation, deferring to fallback chainu4   Credential %s (billing) — rotated to pool entry %sr   TFc              3  2   K   | ]}|j         k    |V  d S r   r   )r,   er$  s     r.   r/   z/recover_with_credential_pool.<locals>.<genexpr>7  s/      EEqadn.D.D.D.D.D.DEEr0   c              3  2   K   | ]}|j         k    |V  d S r   )r  )r,   r+  r#  s     r.   r/   z/recover_with_credential_pool.<locals>.<genexpr><  s0      QQqa.?=.P.P.P.P.P.PQQr0   last_statusuZ   Credential already exhausted (last_status=%s) — rotating immediately instead of retryinguF   Credential %s (rate limit, pre-exhausted) — rotated to pool entry %s)FTreasonmessageusage_limit_reachedgousagelimitzusage limit reachedzusage limit has been reachedu7   Credential %s (rate limit) — rotated to pool entry %s c              3     K   | ]O}t          t                    t                              |          pd                                           V  PdS )r[   N)rp   rq   r?   rn   r   )r,   kr  s     r.   r/   z/recover_with_credential_pool.<locals>.<genexpr>  sn       "
 "
-.."
!!!$$*++1133"
 "
 "
 "
 "
 "
r0   )r/  r.  codeerrorr'  zCoauth authentication is currently not allowed for this organizationT	anthropicapi_modeanthropic_messagesz	xai-oauthz[wke=unauthenticated:z*oauth2 access token could not be validatedu   Credential %s — entitlement-shaped 403 from %s; skipping pool refresh (account lacks subscription, not a transient auth failure).authr  r   _auth_pool_refresh_countsrX   u   Credential auth failure persists after %s refreshes for pool entry %s — treating as unrecoverable and allowing fallback to activate.u3   Credential auth failure — refreshed pool entry %sr&  u@   Credential %s (auth refresh failed) — rotated to pool entry %s)r  r   rP   ) r  rF   ro   r   ry   agent.credential_poolr  rA   r
  r6   ru   rv   rp   r?   currentr   billing
rate_limitr:  upstream_rate_limitrn   info_swap_credentialnextentriesr   _is_entitlement_failurer{   r  try_refresh_matchingr;  _MAX_AUTH_REFRESH_ATTEMPTS)"r=   r  r  r  r  current_providerpool_provider_custom_matchr  _agent_base_raw_credential_id_cur_current_idr%  effective_reasonupstreamr  
next_entrycurrent_entrycurrent_last_statusr0  context_reasoncontext_messageis_entitlement_auth_haystack_is_xai_auth_failurerefresh_kwargs	refreshedrefreshed_idrefresh_countsrefresh_keyr#  r$  r  s"       `                          @@@r.   recover_with_credential_poolr^    s	   , !D|o%%  z266<"CCEEKKMMT:r228b??AAGGIIM  *)]:: x''M,D,DY,O,O'&NNNNNN&uj"==CJJLL $[ 1 1 !11+>>D"KKMMSSUU$%   & & & %& 	*EEL  O/  
 /)) E9d33;tM (CTJJ (#..	3E	 
  1||~~ 	1#D*;TBBM! 1%dD$77k3// 1K 1%0N8 8 8 8 8 8 8 8 8 )#-5C-8J&&-2>===
 FS\M'R,,-@AAAX\ 
	EELC    EELC   o%%>111'2'>C /.}==
!EELF
D#..  
 "":...;o%%>444  	 EEEEDLLNNEEE M  	) TQQQQDLLNNQQQ. .M   LLNNMMZdgm]DIII`d"222EELl#   ,7+BKKM22=AAJ%!!\!Jc22  
 &&z222"{;# 	 !2!28!<!<!BCCIIKKN!-"3"3I">">"D"EEKKMMO%7 E!^3E(O;E 2_D	    	': 	;'2'>C..}==
!EELI
D#..  
 "":...;{>..., 66}kRR "
 "
 "
 "
;"
 "
 "
 
 
 	"s""UYggg!N	"s""%2+55z2..2FFF!N 	&+"4"4%.:NBS^9^9^'>9 R?>Q ! ( &!% 	*EEL1  +6F,*   /))
 )-8 	=.<N?+-D-????	  #9dD99L'!(0KT!R!R!)%'N6DE3$~|<.<.@.@a.P.PST.T{+!+.1KKKEEL((0 '{3a7$   !/11EELSU\]fhlnqUrUrsss""9---(( (3'>C..}==
!EELR
D#..  
 "":...;/!!s   &A/D D%$D%	api_errorr
  retry_countmax_retriesc          
        | j         rdS t          |          j        }|t          vrdS |                                 rdS | j        pd                                                                }|dv rt          | dd          dk    rdS 	 t          | dd          .	 | 	                    | j
        d	           n# t          $ r Y nw xY w| j        }t          |d
                   | _        |d         | _        |d         | _        |                    d| j                  | _        |d         | _        |d         | _        t)          | d          r| j                                         |d         | _        | j        dk    rlddlm} |d         | _        |d         | _         ||d         |d         t9          | j        | j                            | _        |d         | _        d| _
        n~| j        pd                                                                dk    rddlm }  || | j                  | _
        n0| !                    t          |d
                   dd          | _
        tE          d|z   d          }	| #                    | j$         d| d| j         d|	 d d!           tK          j&        |	           dS # t          $ r&}
tN          (                    d"|
           Y d}
~
dS d}
~
ww xY w)#uj  Attempt one extra primary-provider recovery cycle for transient transport failures.

    After ``max_retries`` exhaust, rebuild the primary client (clearing
    stale connection pools) and give it one more attempt before falling
    back.  This is most useful for direct endpoints (custom, Z.AI,
    Anthropic, OpenAI, local models) where a TCP-level hiccup does not
    mean the provider is down.

    Skipped for proxy/aggregator providers (OpenRouter, Nous) which
    already manage connection pools and retries server-side — if our
    retries through them are exhausted, one more rebuilt client won't help.
    Fr[   >   nous-portalnousnousresearchr8  Nr9  clientprimary_recoveryr.  client_kwargsmodelr  requested_providerr  _transport_cacher  r   build_anthropic_clientanthropic_api_keyanthropic_base_urltimeoutis_anthropic_oauthmoabuild_moa_facadeTr.  shared      u   🔁 Transient z on u    — rebuilt client, waiting z"s before one last primary attempt.forcez%Primary transport recovery failed: %s))_fallback_activatedr   r   _TRANSIENT_TRANSPORT_ERRORS_is_openrouter_urlr  ro   r   rF   _retire_shared_openai_clientrf  r
  _primary_runtimerq   _client_kwargsrj  rn   rk  r  r8  r   rl  clearr  agent.anthropic_adapterrn  _anthropic_api_key_anthropic_base_urlr   _anthropic_client_is_anthropic_oauthagent.moa_looprv  _create_openai_clientr   _vprint
log_prefixr   sleepru   rv   )r=   r_  r`  ra  
error_typeprovider_lowerrtrn  rv  	wait_timer+  s              r.   try_recover_primary_transportr    s      u i)J444u !! un*113399;;N 	AAAE:t,,0DDDu? 5(D))522L); 3         ##B$788kJ#%66*>#O#O JJ5,-- 	+"((***9>111FFFFFF')*='>E$(*+?(@E%&<&<&',@)A4U^U[QQ' ' 'E# )++?(@E%ELLn"))++1133u<<
 877777++E5;??ELL 66R())) 7  EL K++	 U U
 U U U U'0U U U 	 	
 	
 	

 	
9t   >BBBuuuuus=   J= B7 6J= 7
CJ= CG7J= =
K-K((K-Tdrop_codex_reasoning_itemsr  c                  | s| S fd| D             }t          |           t          |          z
  }|dk    r| S g }d}|D ]}|r|d         nd}||                    d          dk    r|                    d          dk    r|                    dd          }|                    dd          }	t          |          }
t          |t                    r*t          |	t                    r|r|	rd	nd}||z   |	z   |
d<   nt          |t
                    r8t          |	t
                    r#t          |          t          |	          z   |
d<   nt          |t
                    rDt          |	t                    r/|	rt          |          d
|	dgz   |
d<   nt          |          |
d<   nxt          |t                    rLt          |	t
                    r7g }|r|                    d
|d           |                    |	           ||
d<   n|                    |           |
|d<   |dz  }|                    |           t                      j	        
                    d||           |S )ah  Drop thinking-only assistant turns; merge any adjacent user messages left behind.

    Runs on the per-call ``api_messages`` copy only. The stored
    conversation history (``agent.messages``) is never mutated, so the
    user still sees the thinking block in the CLI/gateway transcript and
    session persistence keeps the full trace. Only the wire copy sent to
    the provider is cleaned.

    Why drop-and-merge rather than inject stub text:
    - Fabricating ``"."`` / ``"(continued)"`` text lies in the history
      and makes future turns see model output the model didn't emit.
    - Dropping the turn preserves honesty; merging adjacent user messages
      preserves the provider's role-alternation invariant.
    - This is the pattern used by Claude Code's ``normalizeMessagesForAPI``
      (filterOrphanedThinkingOnlyMessages + mergeAdjacentUserMessages).
    c                d    g | ],}t                      j                            |           *|-S )r  )r6   r   _is_thinking_only_assistant)r,   rR   r  s     r.   rS   z6drop_thinking_only_and_merge_users.<locals>.<listcomp>]  sP       uu}88'A 9 
 
	  r0   r   r   NrY   rj   r\   r[   r   r   r   r   rX   zbPre-call sanitizer: dropped %d thinking-only assistant turn(s), merged %d adjacent user message(s))rm   rn   rq   rp   r?   r   rl   extendr6   ru   debug)rJ   r  keptdroppedr   mergesrR   r   r   cur_content	prev_copysep
new_blockss    `           r.   "drop_thinking_only_and_merge_usersr  D  s   *       D (mmc$ii'G!|| $&FF + +#-vbzz  F**f''88Ir22L%%	2..K
 T

I ,,, K1M1M  ,DDff"'3c'9K'G	)$$L$// J{D4Q4Q '+L'9'9D<M<M'M	)$$L$// J{C4P4P  >+/+=+=!'==A ,Ii(( ,0+=+=Ii((L#.. 
:k43P3P 
35
 N%%v|&L&LMMM!!+...'1	)$$ a   "F2JaKFFMM!EEL	-	   Mr0   c                   | j         s	d| _        dS t          | dd          t          j                    k    rdS d}	 t          | j        pi                     d          pd                                          	                                }t          | dd          }t          ||t          | j        pi                     d          pd          	          sdd
lm} |r ||          nd}|} t          |dd                       }||t          j                    k    rjt          | dd          sWd| _        t                              d|pdt!          j        |                              d          | j        | j                   dS n,# t*          $ r t                              dd           Y nw xY wd| _        | j        }	 |d         | _        |d         | _        |                    d| j                  | _        |d         | _        |d         | _        t5          | d          r| j                                         |d         | _        t=          |d                   | _        |d         | _         |                    d| j        dk    o
| j        dk              | _!        t          | dd          rd| _         d| _!        | j        d k    r$dd!l"m#}  || | j                  | _$        d| _%        n| j        dk    rldd"l&m'} |d#         | _(        |d$         | _)         ||d#         |d$         tU          | j        | j                  %          | _%        |d&         | _+        d| _$        n0| ,                    t=          |d                   d'd(          | _$        | j-        }	|	.                    |d)         |d*         |d+         |d,         |d-         |                    d.d          /           t          |                    d          pd                                          	                                }t          | dd          }t          t          |dd          pd                                          	                                }
|
|k    }|d0k    r|
/                    d1          r	 dd2lm0}  |t          |                    d          pd                    pd                                	                                }tc          |          o||
k    }n# t*          $ r d}Y nw xY w|g|
re|scd| _2        d| _3        	 ||| _2        ndd
lm}  ||          | _2        n3# t*          $ r&}t          4                    d3||           Y d}~nd}~ww xY wd| _3        t          | dd          }||5                                r|6                                }|t          t          |dd          pd                                          	                                }||k    }dd4lm7} |d0k    r|/                    |          rd}	 dd2lm0} t          |                    d          pd                                          } ||          pd                                	                                }tc          |          o||k    }n# t*          $ r d}Y nw xY wt          |d5d          pt          |d6d          }|rR|rP| 8                    |           t                              d7t          |d8d          t          |d9d                     nB|r@t                              d:t          |d8d          t          |d9d          |pd|pd           |                    d;          }|t=          |          | _9        d| _         d| _        d| _:        dd<l;m<}  ||            dd=l;m=}  || |d         |d                    t                              d>| j        | j                   dS # t*          $ r&}t          4                    d?|           Y d}~dS d}~ww xY w)@a  Restore the primary runtime at the start of a new turn.

    In long-lived CLI sessions a single AIAgent instance spans multiple
    turns.  Without restoration, one transient failure pins the session
    to the fallback provider for every subsequent turn.  Calling this at
    the top of ``run_conversation()`` makes fallback turn-scoped.

    The gateway caches agents across messages (``_agent_cache`` in
    ``gateway/run.py``), so this restoration IS needed there too.
    r   F_rate_limited_untilNr  r[   r  r  )r  	load_poolnext_available_atc                     d S r   rP   rP   r0   r.   <lambda>z)restore_primary_runtime.<locals>.<lambda>  s    T r0   _restore_wait_loggedTzSPrimary %s rate-limited until %s; staying on fallback %s/%s until the reset elapsesr   seconds)timespecz?Reset-aware restore gate failed; falling back to per-turn retryexc_inforj  rk  r8  rl  r  ri  use_prompt_cachinguse_native_cache_layoutr9  r7  _cache_disabledrt  ru  rm  ro  rp  rq  rs  restore_primaryrw  compressor_modelcompressor_context_lengthcompressor_base_urlcompressor_api_keycompressor_providercompressor_api_moderj  context_lengthr  r  r  r8  r  r  r  z;Restore could not reload primary credential pool for %s: %s)CUSTOM_POOL_PREFIXr  access_tokenz&Restore re-selected pool entry %s (%s)r   labelzRRestore skipped pool entry %s (%s): provider %s does not match primary provider %sreasoning_config_reset_stale_streak)rewrite_prompt_model_identityz.Primary runtime restored for new turn: %s (%s)z%Failed to restore primary runtime: %s)>r}  _fallback_indexrF   r   	monotonicr?   r  rn   ro   r   r   r<  r  r  ru   rA  r   fromtimestamp	isoformatr  rj  r
  r  rk  r  r8  r   rl  r  r  rq   r  _use_prompt_caching_use_native_cache_layoutr  rv  rf  r  r  rn  r  r  r   r  r  context_compressorupdate_modelry   r  rA   r  r	  rv   has_availableselectr  rB  r  _rate_limit_backoff_countagent.chat_completion_helpersr  r  )r=   prefetched_primary_poolprimary_providerr  r  next_atr  rv  rn  ccrI  pool_matches_primaryr  primary_keyexcr   entry_providerentry_matches_primaryr  primary_base_url	entry_keysaved_reasoningr  r  r+  s                            r.   restore_primary_runtimer    s	    $ 	 !"uu+Q//$.2B2BBBu. #!
#)r..z::@b
 

%''%%'' 	 u0$77/%06B;;JGGM2NN
 
 
 
	+
 877777 0@I		*+++T $ +DB'$ 3\\BBDD7TY[[#8#85"8%@@ 	-1*4$+*733==y=QQNK   5 
 
 
M 	 	
 	
 	
 	
 	


 "'E		BGkJ#%66*>#O#O JJ5,-- 	+"((***9#B$788$&';$<! *,%N22Tu~7T*
 *
& 5+U33 	3(-E%-2E* >U"" 877777++E5;??EL&*E##^333FFFFFF')*='>E$(*+?(@E%&<&<&',@)A4U^U[QQ' ' 'E# )++?(@E%ELL 66R())( 7  EL %
'(9:-.+,-.VV1266 	 	
 	
 	
 rvvj117R88>>@@FFHHu0$77GD*b99?R@@FFHHNNPP,0@@((((33 )-NNNNNN 10RVVJ5G5G5M21N1NOOUSU%''%%''  (,K'8'8'Y[M=Y$$ - - -',$$$-6J%)E".2E+*6 .EE**??????-6Y7G-H-HE*   Q$        +/'u0$77 2 2 4 4KKMME !$WUJ%C%C%Ir!J!J!P!P!R!R!X!X!Z!Z(6:J(J% EDDDDD$00&112DEE 1 -2)6VVVVVV+.rvvj/A/A/GR+H+H+N+N+P+P(889IJJPb%''%%'' $ 15[0A0A0ckUcFc--$ 6 6 605---6 E#4d;; :unb99   !6  **5111KK@tS11w44   
  KKltS11w44&-#(/C   &&!344&%)/%:%:E" %*! !*+'
 	FEEEEEE""" 	POOOOO%%eR["Z.III<K	
 	
 	
 t   >BBBuuuuus   EF &F54F5K'`3 /A.T `3 T-*`3 ,T--`3  U& %`3 &
V0V`3 VB7`3 B[ `3 [ `3 [  E`3 3
a#=aa#>   PoolTimeoutReadTimeoutConnectErrorConnectTimeoutAPITimeoutErrorAPIConnectionErrorRemoteProtocolErrorOptional[str]c                   g }t          |d          r!|j        r|                    |j                   t          |d          r*|j        r#|j        |vr|                    |j                   t          |d          r|j        r|j        D ]}t          |t                    ro|                    d          p>|                    d          p)|                    d          p|                    d          }|r||vr|                    |           t          |dd          }|st          |t                    r|D ]}t          |t                    rt|                    d	          dk    r[|                    d          p|                    d          pd
}|
                                }|r||vr|                    |           |st          |t                    rk|rid}|D ]d}	t          j        t          j        z  }
t          j        |	||
          D ]1}|
                                }|r||vr|                    |           2e|rd                    |          S dS )aE  
    Extract reasoning/thinking content from an assistant message.
    
    OpenRouter and various providers can return reasoning in multiple formats:
    1. message.reasoning - Direct reasoning field (DeepSeek, Qwen, etc.)
    2. message.reasoning_content - Alternative field (Moonshot AI, Novita, etc.)
    3. message.reasoning_details - Array of {type, summary, ...} objects (OpenRouter unified)
    
    Args:
        assistant_message: The assistant message object from the API response
        
    Returns:
        Combined reasoning text, or None if no reasoning found
    r   r   reasoning_detailssummaryr   r\   r   Nr   r[   )z<think>(.*?)</think>z<thinking>(.*?)</thinking>z<thought>(.*?)</thought>z<reasoning>(.*?)</reasoning>z2<REASONING_SCRATCHPAD>(.*?)</REASONING_SCRATCHPAD>)flagsr   )r   r   rl   r   r  rp   rq   rn   rF   r   ro   r?   r'   r)   r*   findallr{   )r=   assistant_messagereasoning_partsdetailr  r\   blockthinking_textinline_patternspatternr  cleaneds               r.   extract_reasoningr    s    O  +.. <3D3N <0:;;;  "566 H;L;^ H.oEE""#4#FGGG  "566 4;L;^ 4'9 
	4 
	4F&$'' 	4 JJy)) *zz*--*zz),,* zz&))	   4wo==#**7333
 'D99G :z'488 :  	: 	:E%&& :599V+<+<
+J+J %		* 5 5 P69J9J Pb - 3 3 5 5  :]/%I%I#**=999 4z'377 4G 4
 ' 	4 	4GI-EGWEBBB 4 4++-- 4wo==#**73334  ,{{?+++4r0   )r6  
api_kwargsDict[str, Any]r.  r6  Optional[Exception]Optional[Path]c          	     f   	 t          j        |          }|                    dd           d |                                D             }d}	 t	          | j        dd          }n># t          $ r1}t                      j        	                    d|           Y d}~nd}~ww xY wt          j                                                    | j        |d| j                            d           | j        dk    rd	nd
 d|                     |           dd|dd}|t%          |          j        t)          |          d}dD ]}	t	          ||	d          }
|
|
||	<   t	          |dd          }|||d<   t	          |dd          }|^	 t	          |dd          |d<   |j        |d<   n># t          $ r1}t                      j        	                    d|           Y d}~nd}~ww xY w||d<   t          j                                        d          }t                                          | j                  }| j        d| d| dz  }ddlm} t7          j        |dd t(          !          }t7          j         ||d"#                    }t=          ||t(          $           |                     | j          d%|            tC          d&          r*tE          t7          j        |dd t(          !                     |S # t          $ r-}| j#        rt          $                    d'|           Y d}~dS d}~ww xY w)(a  
    Dump a debug-friendly HTTP request record for the active inference API.

    Captures the request body from api_kwargs (excluding transport-only keys
    like timeout). Intended for debugging provider-side 4xx failures where
    retries are not useful.
    rr  Nc                    i | ]
\  }}|||S r   rP   r,   r4  vs      r.   
<dictcomp>z*dump_api_request_debug.<locals>.<dictcomp>,  s    ???A1r0   r  z,Could not extract API key for debug dump: %sPOST/codex_responsesz
/responsesz/chat/completionszBearer zapplication/json)AuthorizationzContent-Type)methodurlheadersbody)	timestampr   r.  request)r   r/  )r  
request_idr5  paramr   r  responser  response_statusresponse_textz,Could not extract error response details: %sr6  z%Y%m%d_%H%M%S_%frequest_dump__z.jsonr   )redact_sensitive_textFr   )rc   indentdefaultTr{  )r  u$   🧾 Request debug dump written to: HERMES_DUMP_REQUEST_STDOUTz,Failed to dump API request debug payload: %s)%copydeepcopyr   itemsrF   rf  r
  r6   ru   r  r   r   r  r   r  rx   r8  _mask_api_key_for_logsr   r   r?   r   strftime _safe_session_filename_componentlogs_diragent.redactr  rr   rw   rs   r   r  r  r   printverbose_loggingrv   )r=   r  r.  r6  r  r  r+  dump_payload
error_info	attr_name
attr_value	body_attrresponse_objr  safe_sid	dump_filer  _serialized_redacted_payload
dump_errors                       r.   dump_api_request_debugr$    s   N}Z((D!!!?????	RelIt<<GG 	R 	R 	REELMqQQQQQQQQ	R "1133* .//44  EenXiFiFill  pC  E  E%Vu/K/KG/T/T%V%V$6   	(
 (
 U,u::* *J T 7 7	$UIt<<
),6Jy)vt44I$%.
6""5*d;;L'Z4;L-Y]4^4^J012>2CJ//  Z Z ZEEL&&'UWXYYYYYYYYZ %/L!LNN++,>??	 5599%:JKKN%PX%P%P	%P%P%PP	 	766666jE!UXYYY J'<'<[PT'U'U'UVV)%6DDDD)ZZyZZ[[[788 	\$*.U1VYZZZ[[[     	WNNI:VVVtttttsn   A
K9 A$ #K9 $
B.'BK9 BC'K9 F& %K9 &
G!0'GK9 G!!DK9 9
L0"L++L0r  r  r8  rj  r  r  r8  rj  c               h    ||n| j         pd}||n| j        pd}|dk    ot          |          dk    S )zEReturn whether this resolved destination accepts native tool markers.Nr[   r9  api.anthropic.com)r  r8  r   )r=   r  r  r8  rj  eff_base_urleff_api_modes          r.   ._direct_native_anthropic_tool_cache_capabilityr*  {  sS      (388%.:NBL'388%.:NBL,, 	Cl++/BBr0   ttlc                d    | dv rdS | du s| dS t          |                                           dv S )u  Return True when a ``prompt_caching.cache_ttl`` value means caching off.

    Single source of truth for the disable-synonym detection shared by
    ``agent_init`` (live-agent ``_cache_disabled`` flag) and the stub policy
    paths below. Keeping one predicate prevents the two sites from drifting
    (a synonym added in only one place would recreate #76085).

    Unknown values (e.g. ``"2h"``, integers) are NOT a disable — callers keep
    caching enabled with the default TTL, matching ``agent_init``.
    )5m1hFNT)offfalsedisablednonone)r?   r   )r+  s    r.   cache_ttl_means_disabledr4    sB     lu
e||s{ts88>>IIIr0   c                     	 ddl m}   |                                 di           pi }|                    dd          }n# t          $ r Y dS w xY wt	          |          S )aF  Return True when ``prompt_caching.cache_ttl`` is configured as off.

    Same disable detection as ``agent_init`` (via ``cache_ttl_means_disabled``)
    so stub-based policy paths (MoA slot decoration, auxiliary fallback
    replan) honor the same config contract without holding a live
    ``AIAgent`` (#76085 / #33555).
    r   )load_config_readonlyprompt_caching	cache_ttlr-  F)hermes_cli.configr6  rn   r
  r4  )r6  pc_cfgr+  s      r.   #prompt_caching_disabled_from_configr;    s    ::::::%%''++,<bAAGRjjd++   uu#C(((s   <? 
AAcache_disabledOptional[bool]c           	     h    ddl m} | t                      }  |ddddt          |                     S )a  Build the destination-identity-blank stub for ``anthropic_prompt_cache_policy``.

    Single sanctioned constructor for that stub. Callers that resolve cache
    policy against a destination identified out-of-band (not a live
    ``AIAgent``) must go through here so ``_cache_disabled`` is never left
    off a hand-rolled ``SimpleNamespace`` (#76085).

    When ``cache_disabled`` is omitted, falls back to the global config so
    stub paths without an agent snapshot still honor an operator disable.
    r   )SimpleNamespaceNr[   )r  r  r8  rj  r  )typesr?  r;  rA   )r<  r?  s     r.   blank_cache_policy_stubrA    sW     &%%%%%<>>?^,,   r0   )r<  toolsOptional[list]Tuple[list, list]c                   ddl m}m}m}	 t	          |          }
t          |
||||          \  }}|s.t          j        | pg           } ||           | |	|          fS  || ||t          |
||||                    }|j	        |j
        fS )uP  Plan request-local cache sections for one resolved destination.

    Shared core of the synchronous acting-aggregator (MoA) and auxiliary
    fallback senders: resolve the cache policy for the destination's real
    provider/base_url/api_mode/model, then either return stripped canonical
    copies (non-caching route) or a :func:`build_prompt_cache_plan` layout
    (caching route, with the direct-native tool marker when the destination
    is api.anthropic.com on the Messages wire).

    Never mutates ``messages`` or ``tools`` — both return values are
    request-local copies.

    ``cache_disabled`` threads the operator's ``prompt_caching.cache_ttl``
    disable into the blank policy stub. When omitted, the live config is
    consulted so MoA/auxiliary paths cannot re-enable markers after the
    user turned caching off (#76085).
    r   )build_prompt_cache_planstrip_anthropic_cache_control"strip_anthropic_tool_cache_controlr%  )native_anthropicdirect_native_tool_cache)agent.prompt_cachingrF  rG  rH  rA  anthropic_prompt_cache_policyr  r  r*  rJ   rB  )rJ   rB  r  r  r8  rj  r<  rF  rG  rH  stubshould_cachenative_layoutcanonical_messagesplans                  r.   #plan_cache_sections_for_destinationrR    s   6          #>22D"?# # #L-  M!]8>r::%%&8999!#E#Ee#L#LLL""&!O"
 "
 "
	  D =$*$$r0   c               h   t          | dd          rdS ||n| j        pd}||n| j        pd}||n| j        pd}||n| j        pd}|                                                                dk    r_	 ddlm}	 dd	l	m
}
 dd
lm}  |
 |	                                d          pi |pd          }|                    d          pi }t          |                    d          pd                                          }t          |                    d          pd                                          }|rg|red}d}	  |||          }|                    d          pd}|                    d          pd}n# t          $ r Y nw xY wt!          | ||||          S n2# t          $ r%}t"                              d|           Y d}~nd}~ww xY wdS |                                }|                                }d|v }ddlm}  ||          pd|v }t+          |d          }d|                                v }|dk    }|o|dk    pt-          |          dk    }|rdS |s|r|s|r|sdS |rd|v rdS |r|rdS |r*|dv }t+          |d          pt+          |d           }|s|rdS d|v }|d!v } | r|rdS dS )"ut  Decide whether to apply Anthropic prompt caching and which layout to use.

    Returns ``(should_cache, use_native_layout)``:
      * ``should_cache`` — inject ``cache_control`` breakpoints for this
        request (applies to OpenRouter Claude, native Anthropic, and
        third-party gateways that speak the native Anthropic protocol).
      * ``use_native_layout`` — place markers on the *inner* content
        blocks (native Anthropic accepts and requires this layout);
        when False markers go on the message envelope (OpenRouter and
        OpenAI-wire proxies expect the looser layout).

    Third-party providers using the native Anthropic transport
    (``api_mode == 'anthropic_messages'`` + Claude-named model) get
    caching with the native layout so they benefit from the same
    cost reduction as direct Anthropic callers, provided their
    gateway implements the Anthropic cache_control contract
    (MiniMax, Zhipu GLM, LiteLLM's Anthropic proxy mode all do).

    Qwen / Alibaba-family models on OpenCode, OpenCode Go, and direct
    Alibaba (DashScope) also honour Anthropic-style ``cache_control``
    markers on OpenAI-wire chat completions. Upstream pi-mono #3392 /
    pi #3393 documented this for opencode-go Qwen. Without markers
    these providers serve zero cache hits, re-billing the full prompt
    on every turn.

    If the operator has set ``prompt_caching.cache_ttl`` to a falsy value
    (``false``, ``null``, ``"off"``, etc.) in config.yaml, prompt caching
    is fully disabled — this early return ensures the disable survives
    ``/model`` switches, fallback re-derivation, and runtime snapshot
    restoration (#33555). We check ``"_cache_disabled"`` (set by
    init_agent when the disable is detected) rather than ``_cache_ttl``
    directly, because ``_cache_ttl`` is not yet set when the policy runs
    during the initial ``init_agent`` call.
    r  F)FFNr[   rt  r   load_config)resolve_moa_preset)resolve_runtime_provider
aggregatorr  rj  )	requestedtarget_modelr  r8  r%  z1MoA aggregator cache-policy resolution failed: %sclaude)_model_name_is_kimi_familymoonshotzopenrouter.aire  r9  r7  r'  )TTr)  qwen>   
minimax-cnminimaxzapi.minimax.iozapi.minimaxi.com>   alibabaopencodeopencode-goopencode-zen)rF   r  r  r8  rj  ro   r   r9  rU  hermes_cli.moa_configrV  hermes_cli.runtime_providerrW  rn   r?   r
  rL  ru   r  r  r\  r   r   )!r=   r  r  r8  rj  eff_providerr(  r)  	eff_model_load_moa_cfgrV  rW  _preset_agg_agg_provider
_agg_model_agg_base_url_agg_api_mode_rt_moa_excmodel_lowerr  	is_clauder\  is_kimiis_openrouteris_nous_portalis_anthropic_wireis_native_anthropicis_minimax_provideris_minimax_hostmodel_is_qwenprovider_is_alibaba_familys!                                    r.   rL  rL    s*   T u'// ~ ( 4HH%.ORL'388%.:NBL'388%.:NBL+CI !!##u,,	XFFFFFF@@@@@@LLLLLL((##E**0b)2Ct G ;;|,,2D 4 4 :;;AACCMTXXg..4"55;;==J   " "22"/j  C %(GGJ$7$7$=2M$'GGJ$7$7$=2MM    D4***$     	X 	X 	XLLLhWWWWWWWW	X|//##K!''))NK'I CBBBBB""9--J{1J  *,HHM $|'9'9';';;N$(<< 	d[(b,=l,K,KOb,b 
  z 
(! "
 {  &K//{ Y z  ,0II!,0@AA G$\3EFF 	  	/ 	: k)M!/ 4 " " m  {<s=   4CF =;E9 8F 9
FF FF 
G(GGri  rq   rx  c                  ddl m}m} ddlm} t          |          }|                    dd           }|                    dd           } |||          }	 |              ||                    d                     | j        dk    s6t          |                    dd	                    
                    d
          rKddlm}
  |
di |}t                      j                            d|||                                            |S | j        dk    rddlm}m} t          |                    dd	          pd	          } ||          rd |                                D             }d|vr|                     ||	          }|||d<    |di |}t                      j                            d|||                                            |S d|vr2|                     |                    dd	          |	          }|||d<   |                    dd           	 t/          t          |                    dd	                    d          rxddlm} t          |                    d          pi           }d |D             } |                                            D ] \  }}|                                |vr|||<   !||d<   n8# t6          $ r+ t                      j                            dd           Y nw xY w t                      j        di |}t                      j                            d|||                                            |S )Nr   )_validate_base_url_validate_proxy_env_urls)resolve_httpx_verifyssl_ca_cert
ssl_verify)	ca_bundler  r  zcopilot-acpr[   zacp://copilot)CopilotACPClientz-Copilot ACP client created (%s, shared=%s) %sgemini)GeminiNativeClientis_native_gemini_base_urlc                "    i | ]\  }}|d v 	||S )>   r  rr  r  http_clientdefault_headersrP   r  s      r.   r  z(create_openai_client.<locals>.<dictcomp>  s4       A\\\ 1\\\r0   r  )verifyz/Gemini native client created (%s, shared=%s) %sra  zgithubcopilot.com)copilot_default_headersr  c                6    h | ]}|                                 S rP   )r   )r,   r4  s     r.   r   z'create_openai_client.<locals>.<setcomp>	  s     :::Aaggii:::r0   z$Copilot default-header guard skippedTr  z(OpenAI client created (%s, shared=%s) %srP   )agent.auxiliary_clientr~  r  agent.ssl_verifyr  rq   r   rn   r  r?   ry   agent.copilot_acp_clientr  r6   ru   rA  _client_log_contextagent.gemini_native_adapterr  r  r  _build_keepalive_http_client
setdefaultr   hermes_cli.modelsr  r   r
  r  OpenAI)r=   ri  r.  rx  r~  r  r  r  ssl_verify_cfghttpx_verifyr  rf  r  r  r  safe_kwargskeepalive_httpr  r   existing_lowerhkhvs                         r.   create_openai_clientr    s)   SSSSSSSS555555 ''M##M488K"&&|T::N''+.YYYL}((44555~&&#m.?.?
B.O.O*P*P*[*[\k*l*l&======!!22M22;%%''		
 	
 	
 ~!!]]]]]]]]}((R88>B??$$X.. 	 !.!4!4!6!6  K K//!&!C!C\ "D " " "-1?K.''66+66FEELA))++	   M$ M));;j"--l < 
 
 %+9M-( ]A...
R ]%6%6z2%F%F!G!GI\]] 	8AAAAAAM--.?@@FBGGH:::::N113399;; & &B88::^33#%HRL/7M+, R R RADQQQQQR SUU\**M**FEEL2!!##	   Ms    B)K
 
2K?>K?r[   c           
        /012 ddl m} |s ||||          }|dk    r1|dv r-t          |t                    r|rt	          j        dd|          } j        } j        }t                      // fdd	D             0t          t           d
i           pi           0d
<   t           d/          0d<   t           d/          0d<   dS/0 fd}		 d _        | _        | _        | _        |pd                                                                }
|pd                                                                }|r| _        n|
|k    rt!          d| d| d          | _        t%           d          r j                                         |r| _        |pd                                                                2|pd                                                                121k    st           dd          Yd _        d _        	 ddlm}  ||           _        n3# t4          $ r&}t6                              d||           Y d}~nd}~ww xY w|pd                                                                dk    r<ddlm} d _        |pd _        d _        i  _         |  j                   _         n/|dk    r8ddl!m"}m#}m$} |dk    }|r|p j        p |            pdn
|p j        pd}|dk    rrt          |t                    r]|r[	 ddl%m&}  |            }nI# t4          $ r<}ddl'}|(                    tR                                        d |           Y d}~nd}~ww xY w| _        | _*        |pt           d!d           _+         || j+        tY           j         j                  "           _-        |r t          |t                    r ||          nd# _.        d _         i  _        n|p j        }|p j        }||d$ _        	 dd%l/m0}m1}m2}  | j        t          |pd           | |                                 n,# t4          $ r t6          3                    d&d'(           Y nw xY wtY           j         j                  }|
| j        d)<    4                    |            5                    t           j                  d*d'+           _         tm                      n# t4          $ r  |	              w xY wd}	 dd,l/m1}m7}m8}  |            } ||          } | j         j        |-          } n# t4          $ r d} Y nw xY w|  _         9                    |           }! :                    |!          rt6                              d.            ;                    | |!          }" <                    | j        ||/          \   _=         _>        t%           d0          r j?        rdd1l@mA}# |/	 dd2l/m1}m8}  | |                      }n# t4          $ r d}Y nw xY wt           j        t                    r j        nd}$ |# j         j        |$ j        |"|3          }% j?        B                     j        |% j         j         j         j        4           	 dd5lCmD}& dd6l/m8}'  |'            pi }( |&|( j                   _E        t6          F                    d7 j         jE                   n2# t4          $ r%})t6          3                    d8|)           Y d})~)nd})~)ww xY wd _G        dd9lHmI}*  |*            t%           d0          r j?        r j?        nd}+i d: j        d; j        d< j        d= j        d> j        d?t           d?d          d@t           j                  dA j=        dB j>        dCt           dCd          rt           jE                  nddD|+rt          |+d: j                  n j        dE|+rt          |+d= j                  n j        dF|+rt          |+d?d          nddG|+rt          |+d; j                  n j        dH|+r|+jJ        nddI|+rt          |+d> j                  n j        dJ|+r|+jK        nd _L        |dk    r- jL        M                     j*         j+         j.        dK           d# _N        d _O        |pd                                                                2|pd                                                                1t          t           dLg           pg           },2r1r21k    r12fdM|,D             },|, _Q        |,r|,d         nd _R        t6          F                    dN||||           t           dOd          }-t           dPd          }.|-d|.rd	 |-S                    |. j         j        t           d>d          Q           dS # t4          $ r  t6                              dRd'(           Y dS w xY wdS dS )Ta5  Switch the model/provider in-place for a live agent.

    Called by the /model command handlers (CLI and gateway) after
    ``model_switch.switch_model()`` has resolved credentials and
    validated the model.  This method performs the actual runtime
    swap: rebuilding clients, updating caching flags, and refreshing
    the context compressor.

    The implementation mirrors ``_try_activate_fallback()`` for the
    client-swap logic but also updates ``_primary_runtime`` so the
    change persists across turns (unlike fallback which is
    turn-scoped).
    r   )determine_api_mode)rj  r9  >   rc  rd  z/v1/?$r[   c                4    i | ]}|t          |          S rP   rF   )r,   r-   _MISSINGr=   s     r.   r  z switch_model.<locals>.<dictcomp>e	  s7        	geT8,,  r0   )rj  r  rk  r  r8  r  rf  r  r  r  r  _config_context_lengthr  r  r	  r@   r   c                                                      D ],\  } }|u r
	 t          | |           # t          $ r Y )w xY wd S r   )r  setattrr
  )_name_valuer  	_snapshotr=   s     r.   _restore_snapshotz'switch_model.<locals>._restore_snapshot	  sr    &__.. 	 	ME6!!uf----   	 	s   3
A A Nz1switch_model: no base_url resolved for provider 'z' (switching from 'z5'); refusing to keep the previous provider's endpointrl  r  zcswitch_model: credential pool reload failed for %s (%s); continuing without pool rotation this turnrt  ru  chat_completionszmoa-virtual-providerzmoa://local)rn  resolve_anthropic_token_is_oauth_tokenr7  zminimax-oauth)"build_minimax_oauth_token_providerz`MiniMax OAuth: failed to install per-request token provider on switch (%s); using static bearer.r  rq  F)r  r  )*apply_custom_provider_tls_to_client_kwargsget_compatible_custom_providersr6  z6custom-provider TLS resolution skipped on switch_modelTr  rr  switch_modelrw  )r  "get_custom_provider_context_lengthrU  )rj  r  custom_providerszLM Studio model activation was rejected or completed without a verifiable active context length during model switch; continuing with configured contextr%  r  )get_model_context_length)r  rU  )r  r  r  config_context_lengthr  r  )resolve_reasoning_configrT  z2switch_model: reasoning_config resolved for %s: %sz7switch_model: could not re-resolve reasoning_config: %sr  rj  r  rk  r  r8  r  ri  r  r  r  r  r  r  r  r  r  compressor_threshold_tokens)ro  rp  rs  _fallback_chainc                    g | ]C}|                     d           pd                                                                hvA|DS )r  r[   )rn   ro   r   )r,   r   new_normold_norms     r.   rS   z switch_model.<locals>.<listcomp>
  s\     
 
 
		*%%+2244::<<XxDXXX XXXr0   z+Model switched in-place: %s (%s) -> %s (%s)_session_dbr   )r  r  billing_modez2Failed to persist billing route after model switchr@   r   )Thermes_cli.providersr  rp   r?   r'   r   rj  r  objectrq   rF   r  rk  ro   r   r  
ValueErrorr8  r   rl  r  r  r  r	  r<  r  r
  ru   rv   r  rv  r  rf  r  rn  r  r  hermes_cli.authr  r   r   r   r  r  r   r  r  r9  r  r  r6  r  "_apply_client_headers_for_base_urlr  r  r  rU  _ensure_lmstudio_runtime_loaded_lmstudio_load_was_unverified"_effective_lmstudio_context_length_anthropic_prompt_cache_policyr  r  r  agent.model_metadatar  r  hermes_constantsr  r  rA  _cached_system_promptr  r  r  threshold_tokensr  updater}  r  r   r  _fallback_modelupdate_session_billing_route)3r=   	new_modelnew_providerr  r  r8  r  	old_modelold_providerr  old_norm_providernew_norm_providerr  	_pool_excrv  rn  r  r  _is_native_anthropiceffective_keyr  _mm_exc_loggingeffective_baser  r  r6  _sm_timeout_sm_custom_providersr  rU  _sm_cfg_destination_context_intent_runtime_context_length_effective_context_lengthr  _ctx_api_keynew_context_lengthr  _sm_load_config_reasoning_cfg_reasoning_errr  _ccfallback_chainr  _session_idr  r  r  r  s3   `                                              @@@@r.   r  r  2	  s    877777  O%%lHINNN 	(((;;;x%% < < 6)R22I>L xxH    
  I& #'wu6F'K'K'Qr"R"RI %,E3Ex$P$PI !-4*H. .I)*       i (,$  %#/  */R6688>>@@)/R6688>>@@ 	%ENN"333D D D5AD D D  
 "5,-- 	+"((*** 	$#EM !&B--//5577 &B--//5577x752Dd#K#K#S &*E".2E+;;;;;;)2<)@)@&&   A )        B%%''--//588777777 0EN#='=EM*EN#%E ++E5;??ELL---          $0;#> ]q  XWXX9P9P9R9RXVXx  yW  DI  DQ  yW  UWM
 ..:mS3Q3Q.Vc.	RRRRRR$F$F$H$HMM    ....&&x0088?        *EM'4E$(0(_GECXZ^4_4_E%&<&<u84U^U[QQ' ' 'E# L`  )Odno|  B  eC  eC  )O(F(F(F  JOE%EL#%E  #4u}M%7N(*$ $E f          ;:(,"--334H4H4J4JKK   
  f f fU`deeeeef6u~u{SSK&2=$Y/
 44^DDD 66U)**% 7  EL 	&e,,,,    	  +	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 +-->>wGG&H&H+^1'
 '
 '
##
  + + +&*###+#>E #CC#  **+BCC 
&	
 	
 	

 !& H H#! ! 	,,!^	 	- 	
 	
 >Eu= u*++ 
0H 
AAAAAA',ZZZZZZZZ'F'F{{}}'U'U$$ , , ,'+$$$, )35=#(F(FNu}}B55K^ ^";1
 
 
 	 --+-^M^^ 	. 	
 	
 	
`======DDDDDD(**0b!9!9.%+!V!V@K/	
 	
 	
 	
  ` ` `NP^________` #'E BAAAAA '.e5I&J&J
quOg
q%
"
"mqCEN 	e6 	EN	
 	EN 	75)R00 	e233 	e7 	"5#A 	GEK]_c<d<dnD!7888jn 	#VGC%+>>>5; 	3bwsJGGGTYTb 	SHgc9b999b 	3bwsJGGGTYTb 	$3%ES%7%7A  	3bwsJGGGTYTb!" 	&s'Is';';#E& '''%%!&!9"'";"'";'
 '
 	 	 	 !&EE "))++1133H"))++1133H'%):B??E2FFN 
H 
X!5!5
 
 
 
 
-
 
 
 +E1?IN1--TE
KK5<L   %55K%t44K;	44$UJ==	 5       	 	 	NND       	 s   E T H* )T *
I4IT ICT L- ,T -
M372M.)T .M33B-T !<Q T &RT RA<T T 7U U'&U'X2 2Y YA\ 
] ]  ]#2h &i iFfunction_argseffective_task_idri   pre_tool_block_checkedskip_tool_request_middlewaretool_request_middleware_traceOptional[List[Dict[str, Any]]]skip_tool_execution_middlewarec
                   	 t          t                    si t          |pg           	 ddlm}
 |sW |
pdt           dd          pdpdt           dd          pdt           dd          pd          }|j        |j        n2# t          $ r%}t          
                    d|           Y d	}~nd	}~ww xY wd	}|sq	 dd
lm}  |pdt           dd          pdpdt           dd          pdt           dd          pdt                              }n# t          $ r d	}Y nw xY w|t          j        d|id          }	 ddlm}  ||pdt           dd          pdpdt           dd          pdt           dd          pddd|t                               n# t          $ r Y nw xY w|S t#          j                    d,d- fddk    rd. fdn}dk    rd. fdnodk    r
d. fd n_ j        r# j                                      r	d. fd!n5d"k    rd. fd#n'd$k    rd. fd%nd&k    rd. fd'nd. 	fd(	r           S dd)lm}  |fd*pdt           dd          pdpdt           dd          pdt           dd          pd+	  	        S )/a0  Invoke a single tool and return the result string. No display logic.

    Handles both agent-level tools (todo, memory, etc.) and registry-dispatched
    tools. Used by the concurrent execution path; the sequential path retains
    its own inline invocation for backward-compatible display handling.
    r   )apply_tool_request_middlewarer[   r   _current_turn_id_current_api_request_id)task_idr   ri   r   api_request_idz!tool_request middleware error: %sN)resolve_pre_tool_block)r  r   ri   r   r  middleware_tracer6  Frb   _emit_post_tool_call_hookblockedplugin_block)r>   r  resultr  r   ri   r   r  statusr  error_messager  r  r   observed_argsr   r@   c                d   t          |t                    r|n}	 ddlm}  ||| pdt	          dd          pd	pdt	          dd          pdt	          dd          pdt          t          j                    
z
  dz            t                    
  
         n# t          $ r Y nw xY w| S )	Nr   r  r[   r   r  r  i  )
r>   r  r  r  r   ri   r   r  duration_msr  )
rp   rq   model_toolsr  rF   r   r   r  r   r
  )r  r  	hook_argsr  _tool_middleware_tracer=   r  r  r>   ri   tool_start_times       r.   _finish_agent_toolz'invoke_tool.<locals>._finish_agent_tool7  s    %/t%D%DWMM-		======%%+')/R"5,;;Ar)/R'92>>D"&u.GLLRPR!1!1O!Ct KLL!%&<!=!=      	 	 	D	s   BB   
B-,B-r7   	next_argsrq   c           	         ddl m}   ||                     d          |                     dd          j                  |           S )Nr   )	todo_tooltodosmergeF)r
  r  store)tools.todo_toolr	  rn   _todo_store)r  
_todo_toolr  r=   s     r.   _executezinvoke_tool.<locals>._executeL  si    ??????%%
#--00#--77+  
   r0   r<   c                                                    }|s/ddlm}  t          j        d |            d          |           S ddlm}   ||                     dd          |                     d          |                     d	d
          |                     d          |                     d          |                     dd          |                     d          |j        	  	        |           S )Nr   )format_session_db_unavailableF)successr6  )r<   queryr[   role_filterr   ry  r   around_message_idwindow   sort)	r  r  r   r   r  r  r  dbcurrent_session_id)	_get_session_db_for_recallhermes_stater  rr   rw   tools.session_search_toolr<   rn   r   )r  
session_dbr  _session_searchr  r=   s       r.   r  zinvoke_tool.<locals>._executeW  s   99;;J FFFFFF))$*QnQnQpQp5q5q*r*rt}~~~SSSSSS%%#--44 )m < <#--33(}}\::&/mm4G&H&H$==155"v..!','7
 
 
   r0   r8   c                n   |                      dd          }|                      d          }ddlm}  ||                      d          ||                      d          |                      d          |j        	          }j        r"j                            || fd
            ||           S )Ntargetr8   
operationsr   )memory_toolactionr\   old_text)r%  r"  r\   r&  r#  r  c                 2                                    S )N)r  ri   )_build_memory_write_metadata)r=   r  ri   s   r.   r  z/invoke_tool.<locals>._execute.<locals>.<lambda>  s#    5+M+M 1%1 ,N , , r0   )build_metadata)rn   tools.memory_toolr$  _memory_storerD   notify_memory_tool_write)	r  r"  r#  _memory_toolr  r  r=   r  ri   s	        r.   r  zinvoke_tool.<locals>._executel  s    ]]8X66F"|44JEEEEEE!\ }}X..!i00"z22%)  F $ %>>$ $ $ $ $ $ ?    &%fi888r0   c                N     j                             |           |           S r   )rD   handle_tool_call)r  r  r=   r>   s    r.   r  zinvoke_tool.<locals>._execute  s+    %%e&;&L&L]\e&f&fhqrrrr0   r9   c           
         ddl m}   ||                     dd          |                     d          |                     dd          j                  |           S )	Nr   )clarify_toolquestionr[   choicesmulti_selectF)r2  r3  r4  callback)tools.clarify_toolr1  rn   clarify_callback)r  _clarify_toolr  r=   s     r.   r  zinvoke_tool.<locals>._execute  sz    HHHHHH%%&]]:r::%MM)44!*~u!E!E"3	     r0   r;   c                    ddl m}   ||                     d          |                     d          t          dd                     |           S )Nr   )read_terminal_tool
start_linecountread_terminal_callback)r;  r<  r5  )tools.read_terminal_toolr:  rn   rF   )r  _read_terminal_toolr  r=   s     r.   r  zinvoke_tool.<locals>._execute  sr    ZZZZZZ%%##(}}\::#--00$U,DdKK  
   r0   r:   c                B                          |           |           S r   )_dispatch_delegate_task)r  r  r=   s    r.   r  zinvoke_tool.<locals>._execute  s%    %%e&C&CI&N&NPYZZZr0   c                Z   t          j        pdt          dd          pdt          dd          pdj        rt	          j                  nd ddt          dd           t          dd           t	                    
  
        }rd|d<    t                      j        | fi |S )	Nr[   r  r  Tenabled_toolsetsdisabled_toolsets)
ri   r   r   r  enabled_toolsskip_pre_tool_call_hookr  rC  rD  r  r  )rq   r   rF   valid_tool_namesr   r6   handle_function_call)r  dispatch_kwargsr  r=   r  r>   r  ri   s     r.   r  zinvoke_tool.<locals>._execute  s    ") +1r'92>>D"&u.GLLRPR>C>T^d5#9:::Z^(,-1!(0BD!I!I")%1Dd"K"K.23I.J.J  O . IDH @A-355-!  "	  r0   )run_tool_execution_middlewarec                H     t          | t                    r| n          S r   )rp   rq   )r  r  r  s    r.   r  zinvoke_tool.<locals>.<lambda>  s%    ((
9d0K0K#^99Q^__ r0   )original_argsr  r   ri   r   r  r   )r  r   r  r   r@   r   )r  rq   r@   r   )rp   rq   r   hermes_cli.middlewarer  rF   payloadtracer
  ru   r  hermes_cli.pluginsr  rr   rw   r  r  r   r  rD   rG   rJ  )r=   r>   r  r  ri   rJ   r  r  r  r  r  _tool_request_mw_mw_errblock_messager  r  r  rJ  r  r  r  r  s   `````    `        @@@@r.   invoke_toolrT  
  s    mT** !"?"E2FFCGGGGGG+ 	<<<)/R"5,;;Ar)/R'92>>D"&u.GLLRPR      -4M%5%;" C C C8'BBBBBBBBC $(M! !	!AAAAAA22)/R"5,;;Ar)/R'92>>D"&u.GLLRPR!%&<!=!=	 	 	MM  	! 	! 	! MMM	! Wm45III	======%%++)/R"5,;;Ar)/R'92>>D"&u.GLLRPR )+!%&<!=!=      	 	 	D	n&&O            ( 		 		 		 		 		 		 		 		 
*	*	*	 	 	 	 	 	 	 	( 
(	"	"	9 	9 	9 	9 	9 	9 	9 	9 	9 	92 
	 25#8#A#A-#P#P 2	s 	s 	s 	s 	s 	s 	s 	s 	s	)	#	#
	 
	 
	 
	 
	 
	 
	 
	 
/	)	)		 		 		 		 		 		 		 		 
/	)	)	[ 	[ 	[ 	[ 	[ 	[ 	[ 	[	 	 	 	 	 	 	 	 	 	 	, & 'x&&&CCCCCC((_____#!'R5,339r!'R1266<"u&?DDJ
 
 
 
s>   AB 
CB>>CAD) )D87D8A!F8 8
GGr   
str | Nonec                   ddl ddlm} |sdS dD ]'}|                    |          }|dk    r
|d|         }(|sdS dd}dfd	}dd}|                                }|| j        v r|S  ||          }	|	| j        v r|	S |||	 ||          h}
t          d          D ]x}t                      }|
D ]`} ||          }|rQ|                    |           |                     ||                     |                     ||                     a|
|z  }
y|
D ]}|r|| j        v r|c S  ||| j        dd          }|r|d         S dS )at  Attempt to repair a mismatched tool name before aborting.

    Models sometimes emit variants of a tool name that differ only
    in casing, separators, or class-like suffixes. Normalize
    aggressively before falling back to fuzzy match:

    1. Lowercase direct match.
    2. Lowercase + hyphens/spaces -> underscores.
    3. CamelCase -> snake_case (TodoTool -> todo_tool).
    4. Strip trailing ``_tool`` / ``-tool`` / ``tool`` suffix that
       Claude-style models sometimes tack on (TodoTool_tool ->
       TodoTool -> Todo -> todo). Applied twice so double-tacked
       suffixes like ``TodoTool_tool`` reduce all the way.
    5. Fuzzy match (difflib, cutoff=0.7).

    See #14784 for the original reports (TodoTool_tool, Patch_tool,
    BrowserClick_tool were all returning "Unknown tool" before).

    Returns the repaired name if found in valid_tool_names, else None.
    r   N)get_close_matches)"'r$   r%   sr?   r@   c                z    |                                                      dd                              dd          S )Nr   r
  r2  )r   replace)rZ  s    r.   _normzrepair_tool_call.<locals>._norm  s0    wwyy  c**223<<<r0   c                L     j         dd|                                           S )Nz(?<!^)(?=[A-Z])r
  )r   r   )rZ  r'   s    r.   _camel_snakez&repair_tool_call.<locals>._camel_snake  s&    rv(#q1177999r0   rU  c                    |                                  }dD ]D}|                    |          r-| d t          |                                        d          c S Ed S )N)_toolz-toolre   z_-)r   endswithrm   rx   )rZ  lcsuffixs      r.   _strip_tool_suffixz,repair_tool_call.<locals>._strip_tool_suffix  sg    WWYY0 	6 	6F{{6"" 6CKK<(//555556tr0   r   rX   gffffff?)ncutoff)rZ  r?   r@   r?   )rZ  r?   r@   rU  )	r'   difflibrW  findr   rG  ranger   r   )r=   r   rW  _xml_sep_idxr]  r_  re  lowered
normalizedcandsr
  extracstrippedmatchesr'   s                   @r.   repair_tool_callrt    s   * III)))))) t  ) ) )~~h''!88!%4%(I t= = = =: : : : : :    ooG%(((y!!JU+++ !':||I7N7NOE1XX  %% 	2 	2A))!,,H 2		(###		%%//***		,,x00111   	e,,,HHH  )?1SQQQG qz4r0   z[response interrupted]r   c                (   |                      d          }t          |t                    r|                                rdS nt          |t                    r|D ]~}t          |t
                    rb|                     d          dk    rFt          |                     d          t                    r|d                                         r dS v dS |r dS n|dvrdS |                      d          rdS t          |                      d          t                    r| d                                         rdS |                      d          s|                      d	          rdS |                      d
          s|                      d          rdS dS )at  True if ``msg`` carries anything the API treats as non-empty content.

    Covers string content, non-empty multimodal content lists, tool_calls,
    tool_call_id linkage (tool results), and reasoning payloads. Mirrors the
    emptiness checks used by ``AIAgent._is_thinking_only_assistant`` but is
    role-agnostic so it can vet user/assistant/tool turns uniformly.
    r\   Tr   r   )Nr[   r   r   r   r  r   r   F)rn   rp   r?   ro   r   rq   )r   r\   r  s      r.   _msg_has_payloadrv  2  s    ggi  G'3 ==?? 	4		GT	"	"  
	 
	E%&& 	 99V$$..!%))F"3"3S99 $eFm>Q>Q>S>S $#tttt tt
	 

	"	"t
ww| t#''-..44 =P9Q9W9W9Y9Y t
ww{ sww':;; t ww$%% 1H)I)I t5r0   c                   | rt          |           dk     r| S g }d}t          |           dz
  }t          |           D ]\  }}||k    rot          |t                    rZ|                    d          dv rCt          |          s4t          |          }t          |d<   |                    |           |dz  }z|                    |           |r)t                      j	        
                    d|           |S | S )uk  Heal empty-content non-final messages before they reach the provider.

    Root-cause context: a stream that dies with 0 recovered characters (peer
    reset, stall-kill) could persist an assistant turn with ``content=None``
    and no tool_calls. The Anthropic message schema — and the litellm/Bedrock
    proxies in front of it — reject ANY request whose transcript contains an
    empty non-final message:

        "all messages must have non-empty content except for the optional
         final assistant message"  (HTTP 400 INVALID_REQUEST_BODY)

    Once such a message lands mid-transcript it poisons EVERY subsequent turn
    of that session until it scrolls out of context. The write-time guard in
    ``chat_completion_helpers`` stops NEW stubs, but sessions already carrying
    one (persisted before the guard, or fed in from a host history) stay stuck
    and previously needed a manual DB edit + gateway restart to recover.

    This pass is the self-healing counterpart: it runs unconditionally on the
    per-call ``api_messages`` copy, so a poisoned transcript repairs itself
    IN MEMORY on the very next send — no restart, no DB surgery. The final
    message is left untouched (an empty final assistant turn is legal). The
    stored conversation history is never mutated; only the wire copy is
    repaired, so the UI/session trace stays faithful.

    Repair strategy is substitution, not deletion: dropping a mid-transcript
    turn can break role alternation and tool-call pairing, whereas an honest
    minimal placeholder keeps the sequence intact and reads correctly as an
    interrupted turn on replay.
    r   r   rX   rY   )rZ   rj   r\   u-  Pre-call sanitizer: healed %d empty non-final message(s) by substituting placeholder content — an empty-content turn was in the transcript and would 400 the request ('messages must have non-empty content' / INVALID_REQUEST_BODY). Self-recovering the poisoned transcript in memory; no restart needed.)rm   	enumeraterp   rq   rn   rv  _INTERRUPTED_PLACEHOLDERrl   r6   ru   rv   )rJ   r   healedlast_idxidxr   fixeds          r.   repair_empty_non_final_messagesr~  b  s    @  s8}}q((%'HF8}}q Hh'' ! !S8OO3%%  #888$S)) 9 IIE7E)OOE"""aKFFOOC     	@
 	
 	
 	
 Or0   c           	        g }| D ]n}|                     d          }|t                      j        j        vr(t                      j                            d|           Y|                    |           o|} t          |           } g }d}| D ]}t          |t                    rc|                     d          dk    rJd|v rFt          |d         t                    r|d         s#d |                                D             }|dz  }|                    |           |r)|} t                      j                            d|           d	}| D ]}|                     d          dk    r|                     d          pg }|s7|D ]k}t          |t                    rQ|                     d
          }	t          |	t                    r|	                     d          nt          |	dd          }
n&t          |d
d          }	|	rt          |	dd          nd}
t          |
t                    r|
                                rt                      j                            d|t                      j                            |                     t          |	t                    r||	d<   !|	+t#          |	d          r	 ||	_        =# t&          $ r Y Jw xY wt          |t                    r|dd|d
<   mt)                      }| D ]r}|                     d          dk    rW|                     d          pg D ]?}t                      j                            |          }|r|                    |           @st)                      }| D ][}|                     d          dk    r@|                     d          pd                                }|r|                    |           \||z
  rBfd| D             } t                      j                            dt-                               ||z
  }|rg }| D ]}|                    |           |                     d          dk    r|                     d          pg D ]j}t                      j                            |          }||v r>|                    dt                      j                            |          d|d           k|} t                      j                            dt-          |                     t)                      }t)                      }g }d}| D ]}|                     d          }|dk    r|                     d          rg }|                     d          pg D ]`}t                      j                            |          }|r
||v r|dz  }4|r|                    |           |                    |           at-          |          t-          |                     d          pg           k    ri |d|i}|                    |           |dk    rd|                     d          pd                                }|r||v r|dz  }=|r|                    |           |                    |           k|                    |           |r)|} t                      j                            d|           | S )u   Fix orphaned tool_call / tool_result pairs before every LLM call.

    Runs unconditionally — not gated on whether the context compressor
    is present — so orphans from session loading or manual message
    manipulation are always caught.
    rY   z9Pre-call sanitizer: dropping message with invalid role %rr   rZ   r   c                &    i | ]\  }}|d k    ||S )r   rP   r  s      r.   r  z)sanitize_api_messages.<locals>.<dictcomp>  s(    EEEDAq13D3D1a3D3D3Dr0   rX   zOPre-call sanitizer: dropped empty/invalid tool_calls on %d assistant message(s)invalid_tool_callr^   r-   NzNPre-call sanitizer: repairing tool_call with empty function.name -> %r (id=%s)r   ra   re   ri   r[   c                    g | ]H}|                     d           dk    r+|                     d          pd                                v F|IS )rY   re   ri   r[   )rn   ro   )r,   rR   orphaned_resultss     r.   rS   z)sanitize_api_messages.<locals>.<listcomp>(  s`     
 
 
EE&MMV++~1F1F1L"0S0S0U0UYi0i0i 0i0i0ir0   z6Pre-call sanitizer: removed %d orphaned tool result(s)u2   [Result unavailable — see context summary above])rY   r-   r\   ri   z0Pre-call sanitizer: added %d stub tool result(s)zBPre-call sanitizer: removed %d duplicate tool_call_id reference(s))rn   r6   r   _VALID_API_ROLESru   r  rl   r~  rp   rq   r   r  rF   r?   ro   rv   r   r   r-   r
  r   r   rm   _get_tool_call_name_static)rJ   r   r   rY   rn  dropped_empty_tool_calls_EMPTY_NAME_SENTINELtcsr   fnr-   surviving_call_idscidresult_call_idsmissing_resultspatchedseen_assistant_call_idsseen_result_call_idsdedupedremoved_dupeskept_tcsr  s                        @r.   sanitize_api_messagesr    s    H  wwvsuu}555EELK   H /x88H" (*J  	 	sD!!	*;..##L 1488 $=@=N $ FECIIKKEEEC$)$# 
#$	
 	
 	
, / S S776??k))ggl##)r 	 	S 	SB"d## AVVJ'')3B)=)=\rvvf~~~72vW[C\C\RT2246@wr64000D$$$  EEL  .$66r::	   "d## S16

GB$7$72BGG    DB%% S*>T!R!R:/	S2 "ee 0 0776??k))ggl++1r 0 0eem<<R@@ 0&**3///55O ) )776??f$$77>**0b7799C )##C((( ');; 

 
 
 

 
 
 	D !!	
 	
 	
 )?:O 
(* 	 	CNN3wwv+--'',//52  B%%-@@DDCo--$*$'EEM$L$LR$P$P'[,/	( (    >  	
 	
 	
 $'55 #$&GM    wwv;377<#8#8Hggl++1r $ $eem<<R@@ 3"999!Q&M 5+//444####8}}CGGL$9$9$?R @ @@@55lH55NN3V^^77>**0b7799C s222" .$((---NN3NN3 
P	
 	
 	
 Os   8K
KKuser_messageassistant_contentrequire_workspacec                l   t          d |D                       rdS |                     |pd                                                                          sdS t	                    dk    rdS t          t          j        d                    }|sdS d}d}t          fd|D                       }|sdS |sd	S d
dlm	}	  |	|                                                                          t          fd|D                       pdv pdv }
t          fd|D                       }|
p|S )u@  Detect a planning/ack message that should continue instead of ending the turn.

    ``require_workspace`` (default True) keeps the original codex-coding scope:
    the ack must reference a filesystem/repo workspace. The conversation loop
    passes ``require_workspace=False`` when the user has explicitly opted into
    intent-ack continuation for all api_modes (``agent.intent_ack_continuation``
    is ``true`` or a model-list), so general autonomous workflows ("I'll run a
    health check on the server", "I'll start the deployment") — which carry a
    future-ack and an action verb but no filesystem reference — are caught too.
    The future-ack + short-content + no-prior-tools + action-verb requirements
    always apply, which is what keeps conversational "I'll help you brainstorm"
    replies from tripping it.
    c              3  r   K   | ]2}t          |t                    o|                    d           dk    V  3dS )rY   re   N)rp   rq   rn   )r,   r   s     r.   r/   z4looks_like_codex_intermediate_ack.<locals>.<genexpr>  s@      
S
S3:c4  >SWWV__%>
S
S
S
S
S
Sr0   Fr[   i  u@   \b(i['’]ll|i will|let me|i can do that|i can help with that)\b)z	look intozlook atinspectscancheckanalyzreviewexplorereadopenruntestfixr  searchri  walkthroughzreport back	summarize)	directoryzcurrent directoryzcurrent dircwdrepo
repositorycodebaseprojectfolder
filesystemz	file treefilespathc              3      K   | ]}|v V  	d S r   rP   r,   r   assistant_texts     r.   r/   z4looks_like_codex_intermediate_ack.<locals>.<genexpr>  s(      #Z#ZFn$<#Z#Z#Z#Z#Z#Zr0   Tr   )_summarize_user_message_for_logc              3      K   | ]}|v V  	d S r   rP   )r,   r   	user_texts     r.   r/   z4looks_like_codex_intermediate_ack.<locals>.<genexpr>  s(      @@FFi@@@@@@r0   z~/r  c              3      K   | ]}|v V  	d S r   rP   r  s     r.   r/   z4looks_like_codex_intermediate_ack.<locals>.<genexpr>  s9       & &%+. & & & & & &r0   )
any_strip_think_blocksro   r   rm   rA   r'   r  agent.codex_responses_adapterr  )r=   r  r  rJ   r  has_future_ackaction_markersworkspace_markersassistant_mentions_actionr  user_targets_workspaceassistant_targets_workspacer  r  s               @@r.   !looks_like_codex_intermediate_ackr  v  s   ( 
S
S(
S
S
SSS u../@/FBGGMMOOUUWWN u
>T!!u
	UWeff N  uN*  !$#Z#Z#Z#Z>#Z#Z#Z Z Z$ u
  t NMMMMM//==CCEEKKMMI@@@@.?@@@@@ 	9	) 
 #& & & & &/@& & & # # "@%@@r0   c                   t          | dd          }|du s+t          |t                    r|                                dv rdS |du s+t          |t                    r|                                dv rdS t          |t                    r:| j        pd	                                t          fd
|D                       rdndS | j        dk    rdndS )u  Classify the resolved intent-ack continuation mode for this turn.

    Returns one of:
      * ``"off"``        — never continue.
      * ``"codex_only"`` — historical scope: continue only on the
        ``codex_responses`` api_mode, and only for codebase/workspace acks
        (``require_workspace=True``).
      * ``"all"``        — user opted in for every api_mode; continue on any
        future-ack + action verb (``require_workspace=False``).

    Mirrors the four-mode shape of ``agent.tool_use_enforcement``: ``"auto"``
    (default) → codex_only; ``True``/"true"/"always"/"yes"/"on" → all;
    ``False``/"false"/"never"/"no"/"off" → off; ``list`` → all when a substring
    matches the active model name, else off.
    _intent_ack_continuationautoT>   onyestruealwaysallF>   r2  r/  r0  neverr/  r[   c              3  n   K   | ]/}t          |t                    |                                v V  0d S r   )rp   r?   r   )r,   r   rr  s     r.   r/   z/intent_ack_continuation_mode.<locals>.<genexpr>  sA      YYjQRTWFXFXYAGGII4YYYYYYr0   r  
codex_only)rF   rp   r?   r   r   rj  r  r8  )r=   moderr  s     @r.   intent_ack_continuation_moder    s      54f==Dt||
4--|$**,,Ba2a2auu}}D#..}4::<<Cb3b3bu$ e{(b//11YYYYdYYYYYduu_dd >->>><<EIr0   c                (    t          |           dk    S )u  Whether intent-ack continuation should fire at all for this turn.

    The ``codex_ack_continuations < 2`` per-turn cap and the
    ``looks_like_codex_intermediate_ack`` detector are applied by the caller;
    this only decides the on/off gate. Callers that also need to know whether
    the workspace requirement applies should use ``intent_ack_continuation_mode``
    directly (``"codex_only"`` ⇒ require_workspace=True, ``"all"`` ⇒ False).
    r/  )r  )r=   s    r.   intent_ack_continuation_enabledr    s     (..%77r0   
source_msgapi_msgc                P    ddl m}  ||||                                            dS )u  Copy provider-facing reasoning fields onto an API replay message.

    Forwarder — the strip-vs-repad POLICY is owned by
    ``agent.message_sanitization.apply_reasoning_content_policy`` (audit F4);
    this only supplies the agent's cached provider-direction flag.
    r   )apply_reasoning_content_policyN)agent.message_sanitizationr  _needs_thinking_reasoning_pad)r=   r  r  r  s       r.   copy_reasoning_content_for_apir    sI     JIIIII""GU@@BB    r0   api_messagesc                J    ddl m}  |||                                           S )u0  Re-pad (or strip) assistant turns' reasoning_content for the active provider.

    ``api_messages`` is built once, before the retry loop, while the *primary*
    provider is active.  A mid-conversation fallback can then switch providers,
    so the reasoning fields baked into ``api_messages`` are shaped for the
    *prior* provider and must be reconciled against the *current* one:

    * Switching TO a require-side provider (DeepSeek / Kimi / MiMo thinking
      mode): assistant turns built when the prior provider did NOT need the
      echo-back go out without ``reasoning_content`` and the new provider
      rejects them with HTTP 400 ("The reasoning_content in the thinking mode
      must be passed back").  Re-apply the pad.

    * Switching TO a strict provider that rejects the field (Mistral,
      Cerebras, Groq, SambaNova, …): assistant turns built under a reasoning
      primary carry a ``reasoning_content`` pad (often a single space ``" "``),
      and the strict provider rejects it with HTTP 400/422 ("Extra inputs are
      not permitted").  Strip the field.  This is the exact cross-provider
      fallback bug from #45655 — a DeepSeek primary pads history with ``" "``,
      the request falls back to Mistral, and Mistral 422s on the stale pad.

    Calling this immediately before building the request kwargs reconciles the
    fields against the *current* provider.  It is idempotent and safe to call
    every iteration; it covers every fallback path.

    Returns the number of assistant turns whose reasoning_content was added or
    removed.
    r   )reapply_reasoning_echo)r  r  r  )r=   r  r  s      r.   #reapply_reasoning_echo_for_providerr    s=    : BAAAAA!!e99;;  r0   r  c              #  <  K   t                      d	fdd
fd}	  |t          | dd                    E d{V  t          | dd          pi }t          |                                          D ]\  }} ||          E d{V  dS # t          $ r Y dS w xY w)u  Yield httpcore pool objects reachable from an httpx client.

    Hermes' keepalive client (#10324 / ``_build_keepalive_http_client``) and
    any ``HTTP(S)_PROXY`` configuration put live connections on *mounted*
    transports (``client._mounts``), not only on the default
    ``client._transport``. Walking the default transport alone makes
    ``force_close_tcp_sockets`` return 0 while a stream is still mid-recv —
    the interrupt logs success and the provider keeps burning the slot
    (#72975).
    r  r   c              3  p   K   | d S t          |           }|v rd S                     |           | V  d S r   )r   r   )r  r   
seen_poolss     r.   _emitz'_iter_httpx_pool_objects.<locals>._emitE  sK      <FDZFv




r0   	transportc              3     K   | d S t          | dd           }| |          E d {V  d S t          | dd            |           E d {V  d S d S )N_pool_connectionsr  )r  r  r  s     r.   _pools_for_transportz6_iter_httpx_pool_objects.<locals>._pools_for_transportN  s      F
 y'400uT{{"""""""F9nd33?uY''''''''''' @?r0   
_transportN_mounts)r  r   )r  r   )r   rF   r   r  r
  )r  r  mountsr  mountedr  r  s        @@r.   _iter_httpx_pool_objectsr  8  s      55J     ( ( ( ( ( (''\4(P(PQQQQQQQQQi66<"!%fllnn!5!5 	5 	5Hg++G4444444444	5 	5   s   A+B 
BBconnc              #  2  K   t                      }| g}|r|                                }|t          |          }||v r-|                    |           |V  t	          |dd          }|&t          |          |vr|                    |           |dS dS )uA   Walk nested ``_connection`` wrappers (proxy tunnel → HTTP11/2).N_connection)r   r   r   r   rF   rl   )r  seenstackr   r   inners         r.   _connection_candidatesr  e  s      UUDFE
  IIKK	IT>>	=$77E$!6!6LL           r0   rf  c              #    K   	 t          | dd          }|| }t          t          |                    }n# t          $ r Y dS w xY w|sdS t	                      }|D ]{}t          |dd          pt          |dd          pg }t          |          D ]C}t          |          D ]/}t          |dd          pt          |dd          }|(t          |dd          }	|	?t          |dd          }
t          |
          r	  |
d	          }	n# t          $ r d}	Y nw xY w|	$t          |d
d          }|t          |dd          }	|	[t          |dd          }t          |dd          }t          |          r*	 ddlm}  ||j	                  }	n# t          $ r d}	Y nw xY w|	t          |	          }||v r|                    |           |	V  1E}dS )u  Yield raw sockets reachable from an OpenAI/httpx client pool.

    httpcore 1.x stores the concrete HTTP11/HTTP2 connection under
    ``conn._connection``; older versions exposed stream attributes directly
    on the pool entry. Proxy tunnels wrap another layer
    (``TunnelHTTPConnection`` / ``ForwardHTTPConnection``). Keep the
    traversal defensive because these are private transport internals and
    vary across httpx/httpcore releases.

    Also walks ``httpx`` mount transports — see ``_iter_httpx_pool_objects``.
    _clientNr  r  _network_stream_stream_sockget_extra_infosocketstreamrp  r   )SocketAttribute)rF   r   r  r
  r   r  callable	anyio.abcr  
raw_socketr   r   )rf  r  poolsr  r  connectionsr  r   r  sockr  wrappedrp  r  r   s                  r.   _iter_pool_socketsr  w  s     fi66 !K-k::;;     UUD + +D.$// tWd++ 	
 %% %	 %	D3D99 $ $	I'8$?? ;y)T::  >vw55<%,V5Et%L%LN// ((#1>(#;#;DD( ( ( (#'DDD(<%fh==G*&w>>< &fi>>G#GWd;;E ((AAAAAA#(5)C#D#DDD( ( ( (#'DDD(<DT>>   



I$%	+ +s3   16 
AA?DD	D	7FF	F	c                   t          | dd          }|dS 	 d}t          |          D ]}ddl}	 |                    d           |                    d|j        |j        z            }|dk    r|dz  }n # t          $ r Y nt          $ r |dz  }Y nw xY w	 |                    d           # t          $ r Y w xY w# 	 |                    d           w # t          $ r Y w w xY wxY w|dk    r?t                      j
                            d|           |                     d	
           dS n># t          $ r1}t                      j
                            d|           Y d}~nd}~ww xY wdS )aE  Detect and clean up dead TCP connections on the primary client.

    Inspects the httpx connection pool for sockets in unhealthy states
    (CLOSE-WAIT, errors).  If any are found, force-closes all sockets
    and rebuilds the primary client from scratch.

    Returns True if dead connections were found and cleaned up.
    rf  NFr   rX   r0   Tu@   Found %d dead connection(s) in client pool — rebuilding clientdead_connection_cleanuprh  zDead connection check error: %s)rF   r  r  setblockingrecvMSG_PEEKMSG_DONTWAITBlockingIOErrorOSErrorr6   ru   rv   _replace_primary_openai_clientr
  r  )r=   rf  
dead_countr   _socketdatar  s          r.   cleanup_dead_connectionsr    s    UHd++F~uC
&v.. 	 	D$$$$  '''yyG$4w7K$KLL3;;!OJ"         a


 $$T****   D$$T****   D>>EEL  R   008Q0RRR4   C C C<cBBBBBBBBC5s   D+ AA32B:3
B=B:?BB:BB:B*)D+ *
B74D+ 6B77D+ :C"<CC"
C	C"C	C""AD+ +
E&5'E!!E&c                

   i }t          | dd          }d}t          |t                    r?t          |                    d          t                    r|                    d          n|}t          |t                    r|                    d          p)|                    d          p|                    d          }t          |t                    r+|                                r|                                |d<   |                    d          p|                    d          }|s=t          |                    d          t                    r|                    d          }t          |t                    r+|                                r|                                |d<   d	D ]"}|                    |          }|d
vr||d<    n#|                    d          }|d
vrCd|vr?	 t          j                    t          |          z   |d<   n# t          t          f$ r Y nw xY wt          | dd          }	t          |	dd          }
|
r|
                    d          p|
                    d          }|rCd|vr?	 t          j                    t          |          z   |d<   n# t          t          f$ r Y nw xY w|
                    d          }|r	d|vr||d<   d|vr0t	          |                                           }|r|dd         |d<   d|vr|                    d          pd}t          |t                    rt          j        d|t          j                  }|rot          |                    d                    }|                    d                                          dk    r|dz  n|}t          j                    |z   |d<   n0t          j        d|t          j                  }|rt          |                                          rt          |                    d          pd          }t          |                    d          pd          }t          |                    d          pd          }t          j                    |dz  z   |dz  z   |z   |d<   n[t          j        d|t          j                  }|r9t          j                    t          |                    d                    z   |d<   |S )z;Extract structured rate-limit details from provider errors.r  Nr6  r5  r   r.  r/  error_description)	resets_atreset_at>   Nr[   r  retry_afterr  r   zretry-afterzRetry-Afterzx-ratelimit-reseti  r[   z,quotaResetDelay[:\s\"]+(\d+(?:\.\d+)?)(ms|s)rX   r   msg     @@zresets?\s+in\s+(?:(\d+(?:\.\d+)?)\s*(?:h|hr|hrs|hour|hours)\b\s*)?(?:(\d+(?:\.\d+)?)\s*(?:m|min|mins|minute|minutes)\b\s*)?(?:(\d+(?:\.\d+)?)\s*(?:s|sec|secs|second|seconds)\b)?r   ry  i  <   z?retry\s+(?:after\s+)?(\d+(?:\.\d+)?)\s*(?:sec|secs|seconds|s\b))rF   rp   rq   rn   r?   ro   r   floatr   r  r'   r  r*   groupr   r  groups)r6  contextr  rN  r.  r/  r   rV   r  r  r   ratelimit_resetraw_messagedelay_matchr  resets_in_matchhoursminutes	sec_matchs                      r.   extract_api_error_contextr"    s    G5&$''DG$ U'1$((72C2CT'J'JT$((7###PT'4   V$$SF(;(;Sw{{7?S?Sfc"" 	/v||~~ 	/ &GH++i((LGKK8K,L,L 	+:gkk'&:&:C@@ 	+ kk'**Ggs## 	1 	1!(GI, 	 	CKK$$EJ&&&+
# ' kk-00j((Zw-F-F&*ikkE+4F4F&F
##z*    uj$//Hh	400G 	2kk-00NGKK4N4N 	:W44&*ikkE+4F4F&F
##z*   !++&9:: 	2z88"1GJ%jj&&(( 	3!,TcT!2GI  ++i((.Bgs## 	V)$SU\^`^kllK Vk//2233,7,=,=a,@,@,F,F,H,HD,P,P%&..V[&*ikkG&;
##"$)N M# # # Vs?+A+A+C+C'D'D V!/"7"7":":"?a@@E#O$9$9!$<$<$ABBG#O$9$9!$<$<$ABBG*.)++*F'TV,*WZa*aGJ'' "	Z! !I
 ! V.2ikkE)//RSBTBT<U<U.U
+Ns$   +&H H&%H&>&J% %J98J9num_tool_msgsc                N   |dk    s|sdS |                                  }|sdS d}t          t          |          dz
  t          t          |          |z
  dz
  d          d          D ]<}||         }t	          |t
                    r|                    d          dk    r|} n=|st          | dd          }|<|5  | j        r| j        dz   |z   | _        n|| _        ddd           n# 1 swxY w Y   n"t          | d	d          }|r|dz   |z   n|| _        dS t          |          }	||                             d
d          }
t	          |
t                    si	 |
rt          |
          ng }|                    d|	                                d           |||         d
<   n-# t          $ r |
 |	 ||         d
<   Y nw xY w|
|	z   ||         d
<   t                      j                            dt          |          |dd         t          |          dk    rdndz              dS )uQ  Append any pending /steer text to the last tool result in this turn.

    Called at the end of a tool-call batch, before the next API call.
    The steer is appended to the last ``role:"tool"`` message's content
    with a clear marker so the model understands it came from the user
    and NOT from the tool itself. Role alternation is preserved —
    nothing new is inserted, we only modify existing content.

    Args:
        messages: The running messages list.
        num_tool_msgs: Number of tool results appended in this batch;
            used to locate the tail slice safely.
    r   NrX   r   rY   re   _pending_steer_lockr]   _pending_steerr\   r[   r   r  z9Delivered /steer to agent after tool batch (%d chars): %sx   z...)_drain_pending_steerrj  rm   maxrp   rq   rn   rF   r&  r   r?   r   rl   lstripr
  r6   ru   rA  )r=   rJ   r#  
steer_text
target_idxr   r   _lockr   r   existing_contentblockss               r.   #apply_pending_steer_to_tool_resultsr0  ?  s    ++--J  J3x==1$c#h---*G!*KR&P&PRTUU  qkc4   	SWWV__%>%>JE 4d;; 6 6' 6+0+?$+F+SE((+5E(	6 6 6 6 6 6 6 6 6 6 6 6 6 6 6 u&6==HEM#]HtOj$@$@S]E  ,,F
+//	2>>&,, D	L/?GT*+++RFMM66==??CCDDD.4HZ ++ 	L 	L 	L1A.K6.K.KHZ +++	L +;V*CY'EELCJ4C4S__s%:%:EEC    s%   ;"C))C-0C-AF# #F?>F?c                   ddl }d}	 t          |           D ]3}	 |                    |j                   n# t          $ r Y nw xY w|dz  }4n># t
          $ r1}t                      j                            d|           Y d}~nd}~ww xY w|S )u&  Abort in-flight TCP I/O by shutting down sockets WITHOUT closing FDs.

    When a provider drops a connection mid-stream — or the user issues an
    interrupt — we want to unblock httpx's reader/writer immediately rather
    than waiting for the kernel's per-connection timeout. ``shutdown(SHUT_RDWR)``
    achieves that: it sends FIN, breaks any pending ``recv``/``send`` with EOF
    or ``EPIPE``, but does NOT release the file descriptor.

    Historically this helper also called ``socket.close()`` so the FD got
    released immediately, but that's unsafe when (as is the case for both the
    interrupt-abort path and stale-call kill path) the helper runs on a
    different thread than the one driving the request:

      * The Python ``socket.socket`` we close here is the SAME object held by
        httpx's pool, so closing it via Python sets its ``_fd`` to -1 and
        future operations on that Python object fail safely.
      * BUT the SSL wrapper (``ssl.SSLSocket``'s underlying OpenSSL ``BIO``)
        caches the raw integer FD. Once ``os.close(fd)`` runs, the kernel may
        immediately recycle that integer to the next ``open()`` call — e.g.
        the kanban dispatcher opening ``kanban.db``.
      * The owning worker thread then unwinds httpx, the SSL layer flushes a
        pending TLS record, and the encrypted bytes get written into the
        wrong file (issue #29507: 24-byte TLS application-data record
        clobbering SQLite header bytes 5..28).

    The fix is to let the owning thread own the close. ``shutdown()`` from any
    thread is FD-safe; ``close()`` is not. The httpx connection's own close
    path — which runs from the worker thread when it unwinds — will release
    the FD via the same ``socket.socket`` object, and because Python's socket
    close atomically swaps ``_fd`` to -1 *before* issuing ``os.close``, there
    is no FD-aliasing window when only one thread closes.

    Returns the number of sockets shut down. (Field kept as
    ``tcp_force_closed=N`` in the log line for backwards-compatible parsing.)
    r   NrX   z'Force-close TCP sockets sweep error: %s)	r  r  shutdown	SHUT_RDWRr
  r
  r6   ru   r  )rf  r  shutdown_countr   r  s        r.   force_close_tcp_socketsr5    s    H N
K&v.. 	  	 Dg/0000    aNN	   K K KDcJJJJJJJJKs6   A 5A 
AA A	A 
B'BB)r   r   r   r  r^  r  r  r  r  r$  r;  rA  rR  rL  r  r  rT  rt  r  r  r  r  r"  r0  r  r5  )r=   r   r>   r?   r@   rA   )rJ   rK   rL   r?   rM   rA   r@   rK   )rJ   r   r   r?   r   r   r@   r   )r   r?   )rJ   r   r@   r   )r\   r?   r@   r?   r  )
r  r  r  rA   r  r  r  r  r@   r  )r_  r
  r`  r   ra  r   r@   rA   )rJ   rK   r  rA   r@   rK   )r@   rA   )r@   r  )r  r  r.  r?   r6  r  r@   r  )
r  r  r  r  r8  r  rj  r  r@   rA   )r+  r   r@   rA   r   )r<  r=  )rJ   r   rB  rC  r  r?   r  r?   r8  r?   rj  r?   r<  r=  r@   rD  )
r  r  r  r  r8  r  rj  r  r@   r  )ri  rq   r.  r?   rx  rA   r@   r   )r[   r[   r[   )NNFFNF)r>   r?   r  rq   r  r?   ri   r  rJ   r   r  rA   r  rA   r  r  r  rA   r@   r?   )r   r?   r@   rU  )r   r  r@   rA   )rJ   rK   r@   rK   )T)
r  r   r  r?   rJ   rK   r  rA   r@   rA   )r@   r?   )r  rq   r  rq   r@   r   )r  r   r@   r   )r  r   )r  r   )rf  r   )r6  r
  r@   r  )rJ   r   r#  r   r@   r   )rf  r   r@   r   )j__doc__
__future__r   r  rr   r   r'   	threadingr   r   pathlibr   typingr   r   r   r	   r
   hermes_cli.timeoutsr   agent.prompt_builderr   agent.tool_dispatch_helpersr   r   agent.trajectoryr   r<  r   r   agent.error_classifierr   agent.turn_contextr   utilsr   r   r   r   r   r   ru   rG  _REASONING_TAG_NAMES_TOOL_CALL_TAG_NAMEStupler   r   r(   r)   r*   r   r{   r   r   r   r6   	frozensetrE   rI   r   r   r   __annotations__Lockr   r   r   r   r   r  r  r^  r  r  r  r~  r  r$  r*  r4  r;  rA  rR  rL  r  r  rT  rt  ry  rv  r~  r  r  r  r  r  r  r  r  r  r  r"  r0  r5  __all__rP   r0   r.   <module>rI     s    , # " " " " "    				                  3 3 3 3 3 3 3 3 3 3 3 3 3 3 < < < < < < 4 4 4 4 4 4 [ [ [ [ [ [ [ [ 8 8 8 8 8 8 T T T T T T T T 1 1 1 1 1 1 5 5 5 5 5 5 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^		8	$	$   ] d !E " "$" " "   
 "E " "$" " "    !+
, I	! !  )3
F#((#788FFFI) ) %
 !+
3chh+,,333M! ! 
 #-"*;SXX*++;;;M# #    &/YUUU& & "
K K K Kg g g g\ !T T T T T TD <>  = = = =%y~'' ? ? ? ?D+ + +,X X X Xv$ $ $ $P^ ^ ^ ^D/ / / /0 37.2A" A" A" A" A" A"J
e e e e\ (,Z Z Z Z Z Z|[ [ [ [~ (i ) ) )   N N N Nn "&\ \ \ \ \ \F #""     "J J J J$) ) ) )$    @ &*9% 9% 9% 9% 9% 9%~ #""r r r r r rlu u u upw w w wv GK056;QU8=\ \ \ \ \@[ [ [ [F 4 - - - -`A A A AHL L L Lj #bA bA bA bA bAJJ J J J:	8 	8 	8 	8   ! ! ! !H* * * *Z       $E E E EP( ( ( (XP P P Ph= = = =B2 2 2 2l  r0   