
    Jj2                   N   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mZ ddl	m
Z
 ddl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 ddlmZ ddlmZmZm Z m!Z!  ej"        e#          Z$dZ%d Z& e'h d          Z(dsdZ)dtdZ*ddddud!Z+dvd#Z,dvd$Z-dwd&Z.ddd'dxd0Z/dyd5Z0d6d7dzd9Z1d{d:Z2 e'h d;          Z3d|d=Z4dd>d}dEZ5dddddFd~dKZ6ddOZ7ddQZ8	 	 	 	 dddZZ9dd]Z:dd^Z;	 dddbZ<ddcZ=d{ddZ>ddhZ?ddjZ@ddlZAd{dmZBddnZCddpZDddqZEg drZFd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)get_provider_request_timeout)format_steer_marker)_trajectory_normalize_msgmake_tool_result_message)convert_scratchpad_to_think)STATUS_EXHAUSTED)FailoverReason)base_url_host_matchesbase_url_hostnameenv_var_enabledatomic_json_write   c                     ddl } | S )z4Lazy ``run_agent`` reference for test-patch routing.r   N	run_agentr   s    A/home/rurouni/.hermes/hermes-agent/agent/agent_runtime_helpers.py_rar   5   s        >   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getattrr(   r&   has_tool)r"   r#   memory_managers      r   !agent_runtime_owns_post_tool_hookr.   @   sm    :::tu2D99 muOo>o>otU$5t<<NI>#:#:=#I#IJJJr   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 r3|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         }
nH# t          j        $ r6 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   .0ms     r   
<listcomp>z0convert_to_trajectory_format.<locals>.<listcomp>Y   s!    ???)!,,???r   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	assistant
tool_calls 	reasoningz<think>
z

</think>
content
function	argumentsz2Unexpected invalid JSON in trajectory conversion: Nd   namerI   rG   z<tool_call>
Fensure_asciiz
</tool_call>
z<think>z<think>
</think>
gpttoolz<tool_response>
){[unknowntool_call_id)rR   rI   rD   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"   r/   r1   r2   
trajectory
system_msgimsgrD   	tool_callrG   tool_call_jsontool_responsesjtool_msgtool_responsetool_content
tool_index	tool_nameraw_contents                       r   convert_to_trajectory_formatrs   J   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		/ ' ' '   (G\efp\qr}\~  @D  AD  @D  ]E  (G  (G  H  H  H$&				' !** 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5;J1 1K
	K
)r^   
session_idlistrt   intc          
        |pt          j        t                    }t          | t                    sdS d}t                      j        j        dfd}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     |S )z<Repair corrupted assistant tool-call argument JSON in-place.r   rm   rZ   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 )NrD   rE   )rW   rY   r$   rb   r[   r`   	TypeError)rm   existingexisting_textmarkers      r   _prepend_markerz5sanitize_tool_call_arguments.<locals>._prepend_marker  s    <<	**h$$ 	 >&,##((00 >)/&=&=8&=&=#F"(HYF	* Jx00MM 	* 	* 	*MMMMM	*!'::=::s   A4 4BBr?   r@   r>   rA   rF   rG   NrB   {}rI   ?P   z~Corrupted tool_call arguments repaired before request (session=%s, message_index=%s, tool_call_id=%s, function=%s, preview=%r)-rN   rR   )rm   rZ   r%   rx   )logging	getLogger__name__rY   ru   r   AIAgent&_TOOL_CALL_ARGUMENTS_CORRUPTION_MARKERrV   rZ   rW   r$   rX   r[   r\   r]   _get_tool_call_id_staticr_   insertr   )r/   r^   rt   logrepairedr~   message_indexrh   rA   	insert_atri   rF   rG   rR   r#   previewexisting_tool_msg
scan_index	candidater}   s                      @r   sanitize_tool_call_argumentsr      s    
/G%h//Ch%% q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--
'
'` Os   F!!EK*)K*
List[Dict]c                   |sdS d}dd}g }|D ]}|rt          |t                    r|                    d          dk    rt          |d	         t                    r|d	                             d          dk    rd ||          sX ||d	                   sF|d	         }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    rw|d	         }|                    dd          }	|                    dd          }
t          |	t                    r.t          |
t                    r|	r
|
r|	dz   |
z   n|	p|
|d<   |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   r8   r   r%   r&   c                    t          |                     d          p-|                     d          p|                     d          dk              S )Ncodex_reasoning_itemscodex_message_itemsfinish_reason
incomplete)r&   rW   )r8   s    r   _is_codex_interimz2repair_message_sequence.<locals>._is_codex_interim  sR    EE)** 6uu*++6uu_%%5
 
 	
r   r?   r@   rA   rD   rE   c              3     K   | ]}||V  	d S Nr5   )r7   ps     r   	<genexpr>z*repair_message_sequence.<locals>.<genexpr>  s;       # #a## # # # # #r   Nreasoning_contentr>   )idcall_idrN   rR   rS   rB   

)r8   r   r%   r&   )rY   rZ   rW   ru   r$   rd   rX   rU   setadddiscard)r"   r/   repairsr   	collapsedrh   prev
prev_calls	new_callsprev_contentnew_contentjoinedknown_tool_idsfilteredr?   tckeytc_idmergeds                      r   repair_message_sequencer   i  s   T  qG 
 
 
 
 I & &$	3%%$	 ;..9R=$// /"!!&))[88%%c** 9%%im44 9 R=D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
 1c{{ Nr   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 r5   r   r6   s     r   	<setcomp>z6repair_message_sequence_with_cursor.<locals>.<setcomp>F  s    !I!I!IA"Q%%!I!I!Ir   c              3  >   K   | ]}t          |          v d V  dS )r>   Nr   )r7   r8   pre_repair_flushed_idss     r   r   z6repair_message_sequence_with_cursor.<locals>.<genexpr>L  s=       - -r!uu0F'F'F'F'F'F'F- -r   )	r+   rY   rv   r   hasattrsumr   minrV   )r"   r/   flush_cursorr   r   s       @r   #repair_message_sequence_with_cursorr   0  s    & "5"8$??L,$$ J)9)9!I!I-<-1H!I!I!I%eX66G{{wu&<=={!-), - - - -#- - - * *E&& *-*CMM* *E& Nr   rD   c                   |sdS t          j        dd|t           j        t           j        z            }t          j        dd|t           j        t           j        z            }t          j        dd|t           j        t           j        z            }t          j        dd|t           j        t           j        z            }t          j        dd|t           j        t           j        z            }dD ]8}t          j        d	| d
| dd|t           j        t           j        z            }9t          j        dd|t           j        t           j        z            }t          j        dd|t           j        t           j        z            }t          j        dd|t           j                  }t          j        dd|t           j                  }|S )uW  Remove reasoning/thinking blocks from content, returning only visible text.

    Handles four cases:
      1. Closed tag pairs (``<think>…</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.
    rB   z<think>.*?</think>flagsz<thinking>.*?</thinking>z<reasoning>.*?</reasoning>z0<REASONING_SCRATCHPAD>.*?</REASONING_SCRATCHPAD>z<thought>.*?</thought>)ri   rA   tool_resultfunction_callfunction_calls<z\b[^>]*>.*?</>zd(?:(?<=^)|(?<=[\n\r.!?:]))[ \t]*<function\b[^>]*\bname\s*=[^>]*>(?:(?:(?!</function>).)*)</function>zS(?:^|\n)[ \t]*<(?:think|thinking|reasoning|thought|REASONING_SCRATCHPAD)\b[^>]*>.*$z@</?(?:think|thinking|reasoning|thought|REASONING_SCRATCHPAD)>\s*zP</(?:tool_call|tool_calls|tool_result|function_call|function_calls|function)>\s*)resubDOTALL
IGNORECASE)r"   rD   _tc_names      r   strip_think_blocksr   X  s   >  r f*Bry2=?XYYYGf0"gRYQSQ^E^___Gf2BrySUS`G`aaaGfH"g]_]fikiv]vwwwGf.G29r}C\]]]G9 
 
&333333)bm+	
 
 
 f	0 	i"-'  G f^
i"-'	  G fK
m	  G f[
m	  G Nr   )classified_reasonerror_contextstatus_codeOptional[int]has_retried_429r   Optional[FailoverReason]r   Optional[Dict[str, Any]]tuple[bool, bool]c          	     Z   | j         }|d|fS t          | dd          pd                                                                }t          |dd          pd                                                                }|r|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 |}|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    rq||nd}|                    |          }|Nt                      j
                            d|t          |dd                     |                     |           dS d|fS |t          j        k    r|                                }|rt          |dd          nd}|t,          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    r9|                     |          }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          d)k    rd&}|s|d$k    r| j        pdd*k    rd+|v pd,|v }|sd&}|r7t                      j
                            d-||nd.| j        pd           d|fS |                                }|t          |dd          }|t          | d/d          }|	i }|| _        | j        |f}|                    |d          d0z   ||<   ||         t:          k    r5t                      j
                            d1||         d0z
  |           d|fS t                      j
                            d2t          |dd                      |                     |           d&|fS ||nd3}|                    |          }|Nt                      j
                            d4|t          |dd                     |                     |           dS d|fS )5a  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providerrB   customzcustom:r   get_custom_provider_pool_keybase_urluu   Credential pool provider mismatch: pool=%s, agent=%s — skipping pool mutation to avoid cross-provider contaminationi  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 chain)r   r   u4   Credential %s (billing) — rotated to pool entry %sr   r   TF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 )rB   N)rY   rZ   r$   rW   lower)r7   kr   s     r   r   z/recover_with_credential_pool.<locals>.<genexpr>i  sn       "
 "
-.."
!!!$$*++1133"
 "
 "
 "
 "
 "
r   )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).auth_auth_pool_refresh_countsr>   u   Credential auth failure persists after %s refreshes for pool entry %s — treating as unrecoverable and allowing fallback to activate.u1   Credential auth failure — refreshed pool entry r   u@   Credential %s (auth refresh failed) — rotated to pool entry %s)_credential_poolr+   rX   r   rb   agent.credential_poolr   r&   	Exceptionr   r^   r_   r   billing
rate_limitr   upstream_rate_limitrW   infomark_exhausted_and_rotate_swap_credentialcurrentr   r$   _is_entitlement_failurerd   r   try_refresh_currentr   _MAX_AUTH_REFRESH_ATTEMPTS)r"   r   r   r   r   poolcurrent_providerpool_provider_custom_matchr   _agent_baseeffective_reasonupstreamrotate_status
next_entrycurrent_entrycurrent_last_statusr   context_reasoncontext_messageis_entitlement_auth_haystack_is_xai_auth_failure	refreshedrefreshed_idrefresh_countsrefresh_keys       `                      r   recover_with_credential_poolr    sV   , !D|o%%  z266<"CCEEKKMMT:r228b??AAGGIIM *M *.>-.O.O 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/  
 /))(#-5C-8J&&-2>===
 FS\M'R,,-@AAAX\ 
	EELC    EELC   o%%>111'2'>C33]j3kk
!EELF
D#..  
 "":...;o%%>444
 MZdgm]DIII`d"222EELl#   ,7+BKKM77Man7ooJ%!!\!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33]j3kk
!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,*   /)),,..	  #9dD99L'!(0KT!R!R!)%'N6DE3$~|<.<.@.@a.P.PST.T{+!+.1KKKEEL((0 '{3a7$   !/11EELqRYZceiknRoRoqqrrr""9---(( (3'>C33]j3kk
!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dS 	 t          | dd          /	 | 	                    | j
        dd           n# t          $ r Y nw xY w| j        }t          |d	                   | _        |d
         | _        |d         | _        |d         | _        |d         | _        t%          | d          r| j                                         |d         | _        | j        dk    rlddlm} |d         | _        |d         | _         ||d         |d         t5          | j        | j                            | _        |d         | _        d| _
        n0|                     t          |d	                   dd          | _
        t=          d|z   d          }|                     | j          d| d| j         d| dd           tC          j"        |           dS # t          $ r&}	tF          $                    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.
    FrB   >   nous-researchnousclientNprimary_recoveryTr   sharedclient_kwargsmodelr   r   r   _transport_cacheapi_keyr   r   build_anthropic_clientanthropic_api_keyanthropic_base_urltimeoutis_anthropic_oauth      u   🔁 Transient z on u    — rebuilt client, waiting z"s before one last primary attempt.forcez%Primary transport recovery failed: %s)%_fallback_activatedtyper   _TRANSIENT_TRANSPORT_ERRORS_is_openrouter_urlr   rX   r   r+   _close_openai_clientr  r   _primary_runtimerZ   _client_kwargsr  r   r   r   r   clearr!  agent.anthropic_adapterr#  _anthropic_api_key_anthropic_base_urlr
   _anthropic_client_is_anthropic_oauth_create_openai_clientr   _vprint
log_prefixtimesleepr^   r_   )
r"   r  r  r  
error_typeprovider_lowerrtr#  	wait_timees
             r   try_recover_primary_transportrD    s       u i)J444u !! un*113399;;N222u05(D))5**L);D +         ##B$788kJJJ5,-- 	+"((***9>111FFFFFF')*='>E$(*+?(@E%&<&<&',@)A4U^U[QQ' ' 'E# )++?(@E%ELL 66R())) 7  EL K++	 U U
 U U U U'0U U U 	 	
 	
 	

 	
9t   >BBBuuuuus=   3H; B# "H; #
B0-H; /B00F	H; ;
I+I&&I+Tdrop_codex_reasoning_itemsrF  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 )rE  )r   r   _is_thinking_only_assistant)r7   r8   rF  s     r   r9   z6drop_thinking_only_and_merge_users.<locals>.<listcomp>&  sP       uu}88'A 9 
 
	  r   r   r   Nr?   rS   rD   rB   r   textr.  rJ  r>   zbPre-call sanitizer: dropped %d thinking-only assistant turn(s), merged %d adjacent user message(s))rV   rW   rZ   rY   r$   ru   rU   extendr   r^   debug)r/   rF  keptdroppedr   mergesr8   r   r   cur_content	prev_copysep
new_blockss    `           r   "drop_thinking_only_and_merge_usersrU    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	-	   Mr   c                	   | j         s	d| _        dS t          | dd          t          j                    k    rdS | j        }	 |d         | _        |d         | _        |d         | _        |d         | _	        t          | d          r| j                                         |d	         | _        t          |d
                   | _        |d         | _        |                    d| j	        dk    o
| j        dk              | _        | j	        dk    rlddlm} |d         | _        |d         | _         ||d         |d         t/          | j        | j                            | _        |d         | _        d| _        n0|                     t          |d
                   dd          | _        | j        }|                    |d         |d         |d         |d         |d         |                    dd                     t          | d d          }|/|                                r|                                }|tA          t          |dd          pd          !                                "                                }tA          |                    d          pd          !                                "                                }||k    }dd!l#m$}	 |d"k    r|%                    |	          rd}	 dd#l#m&}
 tA          |                    d          pd          !                                } |
|          pd!                                "                                }tO          |          o||k    }n# tP          $ r d}Y nw xY wt          |d$d          pt          |d%d          }|rR|rP| )                    |           tT          +                    d&t          |d'd(          t          |d)d(                     nB|r@tT          +                    d*t          |d'd(          t          |d)d(          |pd(|pd(           d| _         d| _        dd+l,m-}  || |d         |d                    tT          +                    d,| j        | j                   dS # tP          $ r&}tT          .                    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_untilr  r   r   r   r   r!  r  use_prompt_cachinguse_native_cache_layoutr   r   r"  r$  r%  r&  r(  Nrestore_primaryTr  compressor_modelcompressor_context_lengthcompressor_base_urlcompressor_api_keycompressor_providercompressor_api_moderB   r  context_lengthr   r!  r   r   r   )CUSTOM_POOL_PREFIXr   r   runtime_api_keyaccess_tokenz&Restore re-selected pool entry %s (%s)r   r   labelzRRestore skipped pool entry %s (%s): provider %s does not match primary provider %s)rewrite_prompt_model_identityz.Primary runtime restored for new turn: %s (%s)z%Failed to restore primary runtime: %s)/r-  _fallback_indexr+   r=  	monotonicr2  r  r   r   r   r   r   r4  r!  rZ   r3  _use_prompt_cachingrW   _use_native_cache_layoutr5  r#  r6  r7  r
   r8  r9  r  r:  context_compressorupdate_modelhas_availableselectr$   rX   r   r   rc  rb   r   r&   r   r   r^   r   agent.chat_completion_helpersrg  r_   )r"   rA  r#  ccr   entryentry_providerprimary_providerentry_matches_primaryrc  r   primary_base_urlprimary_key	entry_keyrg  rC  s                   r   restore_primary_runtimery  k  sc    $ 	 !"uu+Q//$.2B2BBBu		B~kJJJ5,-- 	+"((***9#B$788$&';$<! *,%N22Tu~7T*
 *
& >111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 	 	
 	
 	
$ u0$77 2 2 4 4KKMME !$WUJ%C%C%Ir!J!J!P!P!R!R!X!X!Z!Z#&rvvj'9'9'?R#@#@#F#F#H#H#N#N#P#P (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   %*! ! 	POOOOO%%eR["Z.III<K	
 	
 	
 t   >BBBuuuuus?   J0R 2BM5 4R 5NR NDR 
SR??S>   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
    rC   r   reasoning_detailssummarythinkingrD   rJ  Nr.  rB   )z<think>(.*?)</think>z<thinking>(.*?)</thinking>z<thought>(.*?)</thought>z<reasoning>(.*?)</reasoning>z2<REASONING_SCRATCHPAD>(.*?)</REASONING_SCRATCHPAD>r   r   )r   rC   rU   r   r  rY   rZ   rW   r+   ru   rX   r$   r   r   r   findallrd   )r"   assistant_messagereasoning_partsdetailr  rD   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  ,{{?+++4r   )r   
api_kwargsDict[str, Any]r   r   Optional[Exception]Optional[Path]c          	     j   	 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.
    r'  Nc                    i | ]
\  }}|||S r   r5   r7   r   vs      r   
<dictcomp>z*dump_api_request_debug.<locals>.<dictcomp>r  s    ???A1r   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)	timestamprt   r   request)r.  r   )r   
request_idr   paramr.  r  responser   response_statusresponse_textz,Could not extract error response details: %sr   z%Y%m%d_%H%M%S_%frequest_dump__z.jsonr   )redact_sensitive_textFr   )rL   indentdefaultTr+  )r  u$   🧾 Request debug dump written to: HERMES_DUMP_REQUEST_STDOUTz*Failed to dump API request debug payload: )%copydeepcopypopitemsr+   r  r   r   r^   rM  r   now	isoformatrt   r   ra   r   _mask_api_key_for_logsr.  r   r$   rJ  strftime _safe_session_filename_componentlogs_diragent.redactr  r[   r`   r\   r   r;  r<  r   printverbose_loggingr_   )r"   r  r   r   r  r!  rC  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  a  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[[[     	VNNT
TTUUUtttttsn   A
K9 A$ #K9 $
B.'BK9 BC'K9 F& %K9 &
G!0'GK9 G!!DK9 9
L2$L--L2r   r   r   r  r   r   r   r  c                  ||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 }t%          |d          }d|                                v }|dk    }|o|dk    pt'          |          dk    }|rdS |s|r|r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 )uR  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.
    NrB   moar   )load_config)resolve_moa_preset)resolve_runtime_provider
aggregatorr   r  )	requestedtarget_modelr   r   r  z1MoA aggregator cache-policy resolution failed: %s)FFclaudezopenrouter.ainousresearchr   r   zapi.anthropic.com)TTr   qwen>   
minimax-cnminimaxzapi.minimax.iozapi.minimaxi.com>   alibabaopencodeopencode-goopencode-zen)r   r   r   r  rX   r   hermes_cli.configr  hermes_cli.moa_configr  hermes_cli.runtime_providerr  rW   r$   r   anthropic_prompt_cache_policyr^   rM  r   r   )r"   r   r   r   r  eff_providereff_base_urleff_api_mode	eff_model_load_moa_cfgr  r  _preset_agg_agg_provider
_agg_model_agg_base_url_agg_api_mode_rt_moa_excmodel_lowerr@  	is_claude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   r  r    s   B !) 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),HHM $|'9'9';';;N$(<< 	d[(b,=l,K,KOb,b 
  z  Y {  &K//{ Y z  ,0II!,0@AA G$\3EFF 	  	/ 	: k)M!/ 4 " " m  {<s=   !CF *;E& %F &
E30F 2E33F 
F:F55F:r  rZ   r  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                      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-acprB   zacp://copilot)CopilotACPClientz-Copilot ACP client created (%s, shared=%s) %sgemini)GeminiNativeClientis_native_gemini_base_urlc                "    i | ]\  }}|d v 	||S )>   r!  r'  r   http_clientdefault_headersr5   r  s      r   r  z(create_openai_client.<locals>.<dictcomp>t  s4       A\\\ 1\\\r   r   )verifyz/Gemini native client created (%s, shared=%s) %sr  z(OpenAI client created (%s, shared=%s) %sr5   )agent.auxiliary_clientr  r  agent.ssl_verifyr  rZ   r  rW   r   r$   rb   agent.copilot_acp_clientr  r   r^   r   _client_log_contextagent.gemini_native_adapterr  r  r  _build_keepalive_http_client
setdefaultOpenAI)r"   r  r   r  r  r  r  r  ssl_verify_cfghttpx_verifyr  r  r  r  r   safe_kwargskeepalive_https                    r   create_openai_clientr  S  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... SUU\**M**FEEL2!!##	   Mr   rB   c                   &'( 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             }	t          t           d	i           pi           |	d	<   t           d
&          |	d
<   	 d _        | _        | _        |r| _        | _        t           d          r j                                         |r| _        |pd                                                                (|pd                                                                '('k    st           d
d          K	 ddlm}
  |
|           _        n3# t.          $ r&}t0                              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        pd           _        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# t.          $ r<}ddl$}|%                    tL                                        d|           Y d}~nd}~ww xY w| _        | _'        |pt           dd           _(         || j(        tS           j         j                             _*        |r t          |t                    r ||          nd _+        d _        i  _        n|p j        }|p j        }||d _        	 ddl,m-}m.}m/}  | j        t          |pd           | |                                 n,# t.          $ r t0          0                    dd !           Y nw xY wtS           j         j                  }|
| j        d"<    1                    t           j                  d#d $           _        nP# t.          $ rC |	2                                D ],\  }}|&u r
	 tg           ||           # t.          $ r Y )w xY w w xY w 4                    | j        ||%          \   _5         _6         7                                 t           d&          rˉ j8        rdd'l9m:} d}	 dd(l,m;}m.}  |            } ||          }n# t.          $ r d}Y nw xY wt           j        t                    r j        nd}  | j         j        |  j        t           d)d          |*          }! j8        <                     j        |! j         j         j         j        +           d _=        t           d&          r j8        r j8        nd}" j         j         j         j        t           d,d          t           j                   j5         j6        |"rt          |"d- j                  n j        |"rt          |"d. j                  n j        |"rt          |"d,d          nd|"rt          |"d/ j                  n j        |"r|"j>        nd|"rt          |"d0 j                  n j        |"r|"j?        ndd1 _@        |dk    r- j@        A                     j'         j(         j+        d2           d _B        d _C        |pd                                                                (|pd                                                                't          t           d3g           pg           }#(r'r('k    r'(fd4|#D             }#|# _E        |#r|#d         nd _F        t0          G                    d5||||           t           d6d          }$t           d7d          }%|$d|%rd	 |$H                    |% j         j        t           d0d          8           dS # t.          $ r  t0                              d9d !           Y dS w xY wdS dS ):a5  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_moder   >   r  r  z/v1/?$rB   c                4    i | ]}|t          |          S r5   )r+   )r7   rI   _MISSINGr"   s     r   r  z switch_model.<locals>.<dictcomp>  s7        	geT8,,  r   )r  r   r   r   r!  r  r8  r6  r7  r9  _config_context_lengthr3  r   Nr   )	load_poolzcswitch_model: credential pool reload failed for %s (%s); continuing without pool rotation this turnr  )	MoAClientchat_completionszmoa-virtual-providerzmoa://localr  )r#  resolve_anthropic_token_is_oauth_tokenr   zminimax-oauth)"build_minimax_oauth_token_providerz`MiniMax OAuth: failed to install per-request token provider on switch (%s); using static bearer.r7  r&  F)r!  r   )*apply_custom_provider_tls_to_client_kwargsget_compatible_custom_providersload_config_readonlyz6custom-provider TLS resolution skipped on switch_modelT)exc_infor'  switch_modelr  r  rl  )get_model_context_length)r  r  r  )r   r!  r   config_context_lengthcustom_providersra  r!  r  r   r   r   )r  r   r   r   r!  r  rX  rY  r[  r]  r^  r_  r\  r`  compressor_threshold_tokens)r$  r%  r(  _fallback_chainc                    g | ]C}|                     d           pd                                                                hvA|DS )r   rB   )rW   rX   r   )r7   rr  new_normold_norms     r   r9   z switch_model.<locals>.<listcomp>  s\     
 
 
		*%%+2244::<<XxDXXX XXXr   z+Model switched in-place: %s (%s) -> %s (%s)_session_dbrt   )r   r   billing_modez2Failed to persist billing route after model switch)Ihermes_cli.providersr  rY   r$   r   r   r  r   objectrZ   r+   r  r   r   r   r   r4  r!  rX   r   r   r  r   r   r^   r_   agent.moa_loopr  r3  r  r5  r#  r  r  hermes_cli.authr  r   r   r   r6  r7  r
   r8  r9  r  r  r  r  rM  r:  r  setattr_anthropic_prompt_cache_policyrj  rk  _ensure_lmstudio_runtime_loadedrl  agent.model_metadatar   r  rm  _cached_system_promptrb  threshold_tokensr2  updater-  rh  ru   r$  _fallback_modelr   update_session_billing_route))r"   	new_modelnew_providerr!  r   r   r  	old_modelold_provider	_snapshotr  	_pool_excr  r#  r  r  _is_native_anthropiceffective_keyr  _mm_exc_loggingeffective_baser  r  r  _sm_timeout_name_valuer   _sm_custom_providersr  _sm_cfg_ctx_api_keynew_context_length_ccfallback_chainr(  _session_idr  r&  r'  s)   `                                     @@@r   r  r    s~    877777  >%%lH== 	(((;;;x%% < < 6)R22I>L xxH    
  I$ #'wu6F'K'K'Qr"R"RI %,E3Ex$P$PI !T (,$  %  	&%EN!5,-- 	+"((*** 	$#EM !&B--//5577 &B--//5577x752Dd#K#K#S;;;;;;)2<)@)@&&   A )        B%%''--//588000000 0EN#='=EM*EN#%E $9U[%=I>>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/ 66U)**% 7  EL
     '__.. 	 	ME6!!uf----   $ 	,,!^	 	- 	
 	
 >Eu= 
))+++ u*++ !
0H !
AAAAAA  $	(VVVVVVVV!kmmG#B#B7#K#K   	( 	( 	(#'   	( )35=#(F(FNu}}B55K^ ^")%1I4"P"P1
 
 
 	 --+-^M^^ 	. 	
 	
 	
 #'E '.e5I&J&J
quOg
q%
"
"mqCNNN5)R00e233#7#(#ABEVGC%+>>>5;KNbwsJGGGTYTb=@Hgc9b999bKNbwsJGGGTYTb;>%ES%7%7AKNbwsJGGGTYTb?B'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   3B>Q  2F	 Q  	
F9F4/Q  4F99CQ  <J Q  
K2K	Q  KB-Q  <N> =Q  >&O'$Q  &O''AQ   (R)Q;:R;
RRRR/T TT'2` &aaFfunction_argseffective_task_idrR   pre_tool_block_checkedskip_tool_request_middlewaretool_request_middleware_traceOptional[List[Dict[str, Any]]]c	                    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	}|so	 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 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nndk    r
d. fd n^ j        r# j                                      r	d. fd!n4d"k    rd. fd#n&d$k    rd. fd%nd&k    rd. fd'n
d. fd(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_middlewarerB   rt   _current_turn_id_current_api_request_id)task_idrt   rR   turn_idapi_request_idz!tool_request middleware error: %sN)get_pre_tool_call_block_message)rV  rt   rR   rW  rX  middleware_tracer   FrK   _emit_post_tool_call_hookblockedplugin_block)r#   rL  resultrV  rt   rR   rW  rX  statusr?  error_messagerZ  r_  r   observed_argsOptional[dict]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[  rB   rt   rT  rU  i  )
r#   rL  r_  rV  rt   rR   rW  rX  duration_msrZ  )
rY   rZ   model_toolsr\  r+   rv   r=  ri  ru   r   )r_  rb  	hook_argsr\  _tool_middleware_tracer"   rM  rL  r#   rR   tool_start_times       r   _finish_agent_toolz'invoke_tool.<locals>._finish_agent_toolZ  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-r   	next_argsrZ   c           	         ddl m}   ||                     d          |                     dd          j                  |           S )Nr   )	todo_tooltodosmergeF)rn  ro  store)tools.todo_toolrm  rW   _todo_store)rk  
_todo_toolrj  r"   s     r   _executezinvoke_tool.<locals>._executeo  si    ??????%%
#--00#--77+  
   r   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)successr   )r!   queryrB   role_filterlimitr)  rt   around_message_idwindow   sort)	rx  ry  rz  rt   r{  r|  r~  dbcurrent_session_id)	_get_session_db_for_recallhermes_staterv  r[   r`   tools.session_search_toolr!   rW   rt   )rk  
session_dbrv  _session_searchrj  r"   s       r   rt  zinvoke_tool.<locals>._executez  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
 
 
   r   r   c                n   |                      dd          }|                      d          }ddlm}  ||                      d          ||                      d          |                      d          |j        	          }j        r"j                            || fd
            ||           S )Ntargetr   
operationsr   )memory_toolactionrD   old_text)r  r  rD   r  r  rp  c                 2                                    S )N)rV  rR   )_build_memory_write_metadata)r"   rM  rR   s   r   <lambda>z/invoke_tool.<locals>._execute.<locals>.<lambda>  s#    5+M+M 1%1 ,N , , r   )build_metadata)rW   tools.memory_toolr  _memory_storer)   notify_memory_tool_write)	rk  r  r  _memory_toolr_  rj  r"   rM  rR   s	        r   rt  zinvoke_tool.<locals>._execute  s    ]]8X66F"|44JEEEEEE!\ }}X..!i00"z22%)  F $ %>>$ $ $ $ $ $ ?    &%fi888r   c                N     j                             |           |           S r   )r)   handle_tool_call)rk  rj  r"   r#   s    r   rt  zinvoke_tool.<locals>._execute  s+    %%e&;&L&L]\e&f&fhqrrrr   r   c                    ddl m}   ||                     dd          |                     d          j                  |           S )Nr   )clarify_toolquestionrB   choices)r  r  callback)tools.clarify_toolr  rW   clarify_callback)rk  _clarify_toolrj  r"   s     r   rt  zinvoke_tool.<locals>._execute  si    HHHHHH%%&]]:r::%MM)44"3  
   r   r    c                    ddl m}   ||                     d          |                     d          t          dd                     |           S )Nr   )read_terminal_tool
start_linecountread_terminal_callback)r  r  r  )tools.read_terminal_toolr  rW   r+   )rk  _read_terminal_toolrj  r"   s     r   rt  zinvoke_tool.<locals>._execute  sr    ZZZZZZ%%##(}}\::#--00$U,DdKK  
   r   r   c                B                          |           |           S r   )_dispatch_delegate_task)rk  rj  r"   s    r   rt  zinvoke_tool.<locals>._execute  s%    %%e&C&CI&N&NPYZZZr   c                >   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                              S )NrB   rT  rU  Tenabled_toolsetsdisabled_toolsets)
rR   rt   rW  rX  enabled_toolsskip_pre_tool_call_hookrO  r  r  rP  )r   handle_function_callrt   r+   valid_tool_namesru   )rk  rh  r"   rM  r#   rR   s    r   rt  zinvoke_tool.<locals>._execute  s    55--y*;) +1r'92>>D"&u.GLLRPR>C>T^d5#9:::Z^(,-1!(0BD!I!I")%1Dd"K"K.23I.J.J .   r   )run_tool_execution_middlewarec                H     t          | t                    r| n          S r   )rY   rZ   )rk  rt  rL  s    r   r  zinvoke_tool.<locals>.<lambda>  s%    ((
9d0K0K#^99Q^__ r   )original_argsrV  rt   rR   rW  rX  r   )r_  r   rb  rc  r%   r   )rk  rZ   r%   r   )rY   rZ   ru   hermes_cli.middlewarerS  r+   payloadtracer   r^   rM  hermes_cli.pluginsrY  r[   r`   rf  r\  r=  ri  r)   r,   r  )r"   r#   rL  rM  rR   r/   rN  rO  rP  rS  _tool_request_mw_mw_errblock_messagerY  r_  r\  r  rt  rj  rh  ri  s   `````            @@@@r   invoke_toolr    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! 	JJJJJJ;;)/R"5,;;Ar)/R'92>>D"&u.GLLRPR!%&<!=!=	 	 	MM  	 	 	D	 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 
	 *5#8#A#A-#P#P *	s 	s 	s 	s 	s 	s 	s 	s 	s	)	#	#		 		 		 		 		 		 		 		 
/	)	)		 		 		 		 		 		 		 		 
/	)	)	[ 	[ 	[ 	[ 	[ 	[ 	[ 	[	 	 	 	 	 	 	 	 	 	 D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( (
D54D5A!F5 5
GGrq   
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  r   )r   replace)r  s    r   _normzrepair_tool_call.<locals>._norm	  s0    wwyy  c**223<<<r   c                L     j         dd|                                           S )Nz(?<!^)(?=[A-Z])r  )r   r   )r  r   s    r   _camel_snakez&repair_tool_call.<locals>._camel_snake	  s&    rv(#q1177999r   r  c                    |                                  }dD ]D}|                    |          r-| d t          |                                        d          c S Ed S )N)_toolz-toolrN   z_-)r   endswithrV   ra   )r  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tr   r   r>   gffffff?)ncutoff)r  r$   r%   r$   )r  r$   r%   r  )	r   difflibr  findr   r  ranger   r   )r"   rq   r  _xml_sep_idxr  r  r  lowered
normalizedcandsr  extracstrippedmatchesr   s                   @r   repair_tool_callr    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4r   c           	     @   g }| D ]n}|                     d          }|t                      j        j        vr(t                      j                            d|           Y|                    |           o|} 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.
    r?   z9Pre-call sanitizer: dropping message with invalid role %rinvalid_tool_callr@   rA   rF   rI   NzNPre-call sanitizer: repairing tool_call with empty function.name -> %r (id=%s)r   rJ   rN   rR   rB   c                    g | ]H}|                     d           dk    r+|                     d          pd                                v F|IS )r?   rN   rR   rB   )rW   rX   )r7   r8   orphaned_resultss     r   r9   z)sanitize_api_messages.<locals>.<listcomp>	  s`     
 
 
EE&MMV++~1F1F1L"0S0S0U0UYi0i0i 0i0i0ir   z6Pre-call sanitizer: removed %d orphaned tool result(s)u2   [Result unavailable — see context summary above])r?   rI   rD   rR   z0Pre-call sanitizer: added %d stub tool result(s)r   r>   zBPre-call sanitizer: removed %d duplicate tool_call_id reference(s))rW   r   r   _VALID_API_ROLESr^   rM  rU   rY   rZ   r+   r$   rX   r_   r   r   rI   r   r   r   rV   _get_tool_call_name_static)r/   r   rh   r?   _EMPTY_NAME_SENTINELtcsr   fnrI   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  C	  s    H  wwvsuu}555EELK   H$ / 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   (G11
G?>G?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 )r?   rN   N)rY   rZ   rW   )r7   rh   s     r   r   z4looks_like_codex_intermediate_ack.<locals>.<genexpr>	  s@      
S
S3:c4  >SWWV__%>
S
S
S
S
S
Sr   FrB   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fixrM  searchr  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   r5   r7   r}   assistant_texts     r   r   z4looks_like_codex_intermediate_ack.<locals>.<genexpr>/
  s(      #Z#ZFn$<#Z#Z#Z#Z#Z#Zr   Tr   )_summarize_user_message_for_logc              3      K   | ]}|v V  	d S r   r5   )r7   r}   	user_texts     r   r   z4looks_like_codex_intermediate_ack.<locals>.<genexpr>B
  s(      @@FFi@@@@@@r   z~/r  c              3      K   | ]}|v V  	d S r   r5   r  s     r   r   z4looks_like_codex_intermediate_ack.<locals>.<genexpr>F
  s9       & &%+. & & & & & &r   )
any_strip_think_blocksrX   r   rV   r&   r   r  agent.codex_responses_adapterr  )r"   r  r  r/   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  	  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	) 
 #& & & & &/@& & & # # "@%@@r   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>   noofffalseneverr$  rB   c              3  n   K   | ]/}t          |t                    |                                v V  0d S r   )rY   r$   r   )r7   r   r  s     r   r   z/intent_ack_continuation_mode.<locals>.<genexpr>d
  sA      YYjQRTWFXFXYAGGII4YYYYYYr   r  
codex_only)r+   rY   r$   r   ru   r  r  r   )r"   moder  s     @r   intent_ack_continuation_moder*  L
  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Ir   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,  i
  s     (..%77r   
source_msgapi_msgrx   c                P   |                     d          dk    rdS |                                 }|                     d          }t          |t                    r,|s|                    dd           n|dk    rd|d<   n||d<   dS |                     d          }|r3|                     d          rt          |t                    r	|rd|d<   dS t          |t                    r"|r |r||d<   n|                    dd           dS |rd|d<   dS |                    dd           dS )	zACopy provider-facing reasoning fields onto an API replay message.r?   r@   Nr   rB   r   rC   rA   )rW   _needs_thinking_reasoning_padrY   r$   r  )r"   r-  r.  needs_thinking_padr{   normalized_reasonings         r   copy_reasoning_content_for_apir3  w
  s   ~~f,,<<>>( ~~122H(C   ! 	4KK+T2222^^+.G'((+3G'( &>>+66NN<(( +S11 !	 (+#$ &,, 1E  	3+?G'((KK+T222  '*#$ KK#T*****r   api_messagesc                6   |                                  }d}|D ]}|                    d          dk    r|rB|                    d          r4t          | ||           |                    d          r|dz  }`d|v r|                    dd           |dz  }|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   r?   r@   r   r>   N)r0  rW   r3  r  )r"   r4  	needs_padchangedr.  s        r   #reapply_reasoning_echo_for_providerr8  
  s    : 3355IG  ;;v+-- 	{{.// *5'7CCC{{.// 1
 #g--/6661Nr   r  c              #    K   	 t          | dd          }|dS t          |dd          }|dS t          |dd          }|dS t          |dd          pt          |dd          pg }n# t          $ r Y dS w xY wt                      }t          |          D ]a}|g}t          |dd          }||                    |           |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cdS )aa  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. Keep the traversal defensive because these are private
    transport internals and vary across httpx/httpcore releases.
    _clientN
_transport_pool_connections_connection_network_stream_stream_sockget_extra_infosocketstreamr  r   )SocketAttribute)r+   r   r   ru   rU   callable	anyio.abcrE  
raw_socketr   r   )r  r   	transportr   connectionsseenconn
candidatesinnerr   rD  sockrB  wrappedr  rE  r}   s                    r   _iter_pool_socketsrQ  
  s     fi66FKt<<	Fy'400<FD.$// tWd++ 	
     UUD[!! ) )V
mT22e$$$# $	 $	I	#4d;; 79i66  ~67D11D|!(1A4!H!HN++ $$-~h77$ $ $ $#$|!&(D99&"7GT::D| "&)T::$77E?? $$======$u_%?@@$ $ $ $#$|XXF~~HHVJJJJI$	) )sE   A( A( A( $A( (
A65A6"D..D=<D=F11G ?G 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.
    r  NFr   r>   r   Tu@   Found %d dead connection(s) in client pool — rebuilding clientdead_connection_cleanup)r   zDead connection check error: %s)r+   rQ  rC  setblockingrecvMSG_PEEKMSG_DONTWAITBlockingIOErrorOSErrorr   r^   r_   _replace_primary_openai_clientr   rM  )r"   r  
dead_countrO  _socketdataexcs          r   cleanup_dead_connectionsr_  F  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                    rj|                    d          p)|                    d          p|                    d          }t          |t                    r+|                                r|                                |d<   |                    d          p|                    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  Nr   r   r.  r   r   error_description)	resets_atreset_at>   NrB   rc  retry_afterr  r  zretry-afterzRetry-Afterzx-ratelimit-reseti  rB   z,quotaResetDelay[:\s\"]+(\d+(?:\.\d+)?)(ms|s)r>   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   r)  i  <   z?retry\s+(?:after\s+)?(\d+(?:\.\d+)?)\s*(?:sec|secs|seconds|s\b))r+   rY   rZ   rW   r$   rX   r=  floatrz   
ValueErrorr   r  r   groupr   r  groups)r   contextr  r  r   r   r   r<   rd  r  r  ratelimit_resetraw_messagedelay_matchsecondsresets_in_matchhoursminutes	sec_matchs                      r   extract_api_error_contextrt  r  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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$   ,&G G'&G'?&I& &I:9I: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   Nr>   r   r?   rN   _pending_steer_lockrE   _pending_steerrD   rB   rJ  rK  z9Delivered /steer to agent after tool batch (%d chars): %sx   z...)_drain_pending_steerr  rV   maxrY   rZ   rW   r+   rx  r   r$   ru   rU   lstripr   r   r^   r   )r"   r/   ru  
steer_text
target_idxrl   rh   _lockr{   r}   existing_contentblockss               r   #apply_pending_steer_to_tool_resultsr    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   Nr>   z'Force-close TCP sockets sweep error: %s)	rC  rQ  shutdown	SHUT_RDWRrY  r   r   r^   rM  )r  r\  shutdown_countrO  r^  s        r   force_close_tcp_socketsr    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)rs   r   r   r   r  rD  rU  ry  r  r  r  r  r  r  r  r  r  r3  r_  rt  r  rQ  r  )r"   r   r#   r$   r%   r&   )r/   r0   r1   r$   r2   r&   r%   r0   )r/   ru   rt   r$   r%   rv   )r/   r   r%   rv   )rD   r$   r%   r$   )
r   r   r   r&   r   r   r   r   r%   r   )r  r   r  rv   r  rv   r%   r&   )r/   r0   rF  r&   r%   r0   )r%   r&   )r%   r  )r  r  r   r$   r   r  r%   r  )
r   r  r   r  r   r  r  r  r%   r   )r  rZ   r   r$   r  r&   r%   r   )rB   rB   rB   )NNFFN)r#   r$   rL  rZ   rM  r$   rR   r  r/   ru   rN  r&   rO  r&   rP  rQ  r%   r$   )rq   r$   r%   r  )r/   r0   r%   r0   )T)
r  r   r  r$   r/   r0   r  r&   r%   r&   )r%   r$   )r-  rZ   r.  rZ   r%   rx   )r4  ru   r%   rv   )r  r   )r   r   r%   r  )r/   ru   ru  rv   r%   rx   )r  r   r%   rv   )G__doc__
__future__r   r  r[   r   r   r=  r   pathlibr   typingr   r   r   r	   hermes_cli.timeoutsr
   agent.prompt_builderr   agent.tool_dispatch_helpersr   r   agent.trajectoryr   r   r   agent.error_classifierr   utilsr   r   r   r   r   r   r^   r   r   	frozensetr*   r.   rs   r   r   r   r   r  rD  rU  ry  r/  r  r  r  r  r  r  r  r  r  r*  r,  r3  r8  rQ  r_  rt  r  r  __all__r5   r   r   <module>r     s   , # " " " " "    				              , , , , , , , , , , , , < < < < < < 4 4 4 4 4 4 [ [ [ [ [ [ [ [ 8 8 8 8 8 8 2 2 2 2 2 2 1 1 1 1 1 1 ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^		8	$	$     &/YUUU& & "
K K K Kg g g g\ 	p p p p p phD D D DN$ $ $ $P\ \ \ \J 37.2}" }" }" }" }" }"BN N N Nn (,Z Z Z Z Z Z|X X X Xx (i ) ) )   N N N Nn "&\ \ \ \ \ \F #""N N N N N Nd\ \ \ \~[ [ [ [~
 GK056;QU	P P P P Ph[ [ [ [~` ` ` `R #bA bA bA bA bAJJ J J J:	8 	8 	8 	8S+ S+ S+ S+l/ / / /dD D D DN( ( ( (XL L L L`= = = =B2 2 2 2l  r   