§
    ÷žyjLa  ã                  ó  — d Z ddlmZ ddlZddlZddlmZmZ ddlm	Z	m
Z
 ddlmZ ddlmZ  eh d£¦  «        Z eh d	£¦  «        Z ed
¬¦  «         G d„ d¦  «        ¦   «         ZdZdZ ed
¬¦  «         G d„ d¦  «        ¦   «         Z ed
¬¦  «         G d„ d¦  «        ¦   «         Z ed
¬¦  «         G d„ d¦  «        ¦   «         Zd3d„Zd4d„Z G d„ d ¦  «        Zd5d"„Zd6d#„Zd7d&„Zd8d(„Zd9d)„Zd:d.„Z d;d/„Z!d;d0„Z"d<d1„Z#d=d2„Z$dS )>a"  Pure tool-call loop guardrail primitives.

The controller in this module is intentionally side-effect free: it tracks
per-turn tool-call observations and returns decisions. Runtime code owns whether
those decisions become warning guidance, synthetic tool results, or controlled
turn halts.
é    )ÚannotationsN)Ú	dataclassÚfield)ÚAnyÚMapping)Úsafe_json_loads)Úfile_mutation_result_landed>   Ú	read_fileÚ
web_searchÚweb_extractÚsearch_filesÚsession_searchÚbrowser_consoleÚbrowser_snapshotÚbrowser_get_imagesÚmcp_filesystem_read_fileÚmcp_filesystem_search_filesÚmcp_filesystem_get_file_infoÚmcp_filesystem_directory_treeÚmcp_filesystem_list_directoryÚmcp_filesystem_read_text_fileÚ"mcp_filesystem_read_multiple_filesÚ(mcp_filesystem_list_directory_with_sizes>   ÚtodoÚpatchÚmemoryÚcronjobÚprocessÚterminalÚ
write_fileÚbrowser_typeÚexecute_codeÚsend_messageÚskill_manageÚbrowser_clickÚbrowser_pressÚdelegate_taskÚbrowser_scrollÚbrowser_navigateT)Úfrozenc                  ó  — e Zd ZU dZdZded<   dZded<   dZded	<   d
Zded<   dZ	ded<   dZ
ded<   dZded<   d
Zded<    ed„ ¬¦  «        Zded<    ed„ ¬¦  «        Zded<    ed„ ¬¦  «        Zded<   ed!d„¦   «         Zd S )"ÚToolCallGuardrailConfiga  Thresholds for per-turn tool-call loop detection.

    Warnings are enabled by default and never prevent tool execution. Hard stops
    are explicit opt-in so interactive CLI/TUI sessions get a gentle nudge unless
    the user enables circuit-breaker behavior in config.yaml.
    TÚboolÚwarnings_enabledFÚhard_stop_enabledé   ÚintÚexact_failure_warn_afteré   Úexact_failure_block_afteré   Úsame_tool_failure_warn_afteré   Úsame_tool_failure_halt_afterÚno_progress_warn_afterÚno_progress_block_afterc                 ó   — t           S ©N)ÚIDEMPOTENT_TOOL_NAMES© ó    ú;/home/ragecks/.hermes/hermes-agent/agent/tool_guardrails.pyú<lambda>z ToolCallGuardrailConfig.<lambda>P   s   € ÕEZ€ r?   )Údefault_factoryzfrozenset[str]Úidempotent_toolsc                 ó   — t           S r<   )ÚMUTATING_TOOL_NAMESr>   r?   r@   rA   z ToolCallGuardrailConfig.<lambda>Q   s   € ÕCV€ r?   Úmutating_toolsc                 ó   — t          ¦   «         S r<   )ÚLoopCapConfigr>   r?   r@   rA   z ToolCallGuardrailConfig.<lambda>R   s
   € ½}¹¼€ r?   ú'LoopCapConfig'Ú	loop_capsÚdataúMapping[str, Any] | NoneÚreturnú'ToolCallGuardrailConfig'c                óÒ  — t          |t          ¦  «        s
 | ¦   «         S |                     d¦  «        }t          |t          ¦  «        si }|                     d¦  «        }t          |t          ¦  «        si } | ¦   «         } | t          |                     d¦  «        |j        ¦  «        t          |                     d¦  «        |j        ¦  «        t          |                     d|                     d¦  «        ¦  «        |j        ¦  «        t          |                     d|                     d¦  «        ¦  «        |j        ¦  «        t          |                     d	|                     d
¦  «        ¦  «        |j	        ¦  «        t          |                     d|                     d¦  «        ¦  «        |j
        ¦  «        t          |                     d|                     d¦  «        ¦  «        |j        ¦  «        t          |                     d	|                     d¦  «        ¦  «        |j        ¦  «        t                               |                     d¦  «        ¦  «        ¬¦	  «	        S )zABuild config from the `tool_loop_guardrails` config.yaml section.Ú
warn_afterÚhard_stop_afterr.   r/   Úexact_failurer2   Úsame_tool_failurer6   Úidempotent_no_progressr9   r4   r8   r:   rJ   )	r.   r/   r2   r6   r9   r4   r8   r:   rJ   )Ú
isinstancer   ÚgetÚ_as_boolr.   r/   Ú_positive_intr2   r6   r9   r4   r8   r:   rH   Úfrom_mapping)ÚclsrK   rP   rQ   Údefaultss        r@   rY   z$ToolCallGuardrailConfig.from_mappingT   s)  € õ ˜$¥Ñ(Ô(ð 	Ø�3‘5”5ˆLà—X’X˜lÑ+Ô+ˆ
Ý˜*¥gÑ.Ô.ð 	ØˆJØŸ(š(Ð#4Ñ5Ô5ˆÝ˜/­7Ñ3Ô3ð 	!Ø ˆOà�3‘5”5ˆØˆsÝ% d§h¢hÐ/AÑ&BÔ&BÀHÔD]Ñ^Ô^Ý& t§x¢xÐ0CÑ'DÔ'DÀhÔF`ÑaÔaÝ%2Ø—’˜°·²Ð9SÑ0TÔ0TÑUÔUØÔ1ñ&ô &õ *7Ø—’Ð2°D·H²HÐ=[Ñ4\Ô4\Ñ]Ô]ØÔ5ñ*ô *õ $1Ø—’Ð7¸¿ºÐBZÑ9[Ô9[Ñ\Ô\ØÔ/ñ$ô $õ '4Ø×#Ò# O°T·X²XÐ>YÑ5ZÔ5ZÑ[Ô[ØÔ2ñ'ô 'õ *7Ø×#Ò#Ð$7¸¿ºÐB`Ñ9aÔ9aÑbÔbØÔ5ñ*ô *õ %2Ø×#Ò#Ð$<¸d¿hºhÐG`Ñ>aÔ>aÑbÔbØÔ0ñ%ô %õ $×0Ò0°·²¸+Ñ1FÔ1FÑGÔGð7
ñ 
ô 
ð 	
r?   N)rK   rL   rM   rN   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__r.   Ú__annotations__r/   r2   r4   r6   r8   r9   r:   r   rC   rF   rJ   ÚclassmethodrY   r>   r?   r@   r,   r,   ?   s7  € € € € € € ðð ð "ÐÐ!Ð!Ð!Ñ!Ø#ÐÐ#Ð#Ð#Ñ#Ø$%ÐÐ%Ð%Ð%Ñ%Ø%&ÐÐ&Ð&Ð&Ñ&Ø()Ð Ð)Ð)Ð)Ñ)Ø()Ð Ð)Ð)Ð)Ñ)Ø"#ÐÐ#Ð#Ð#Ñ#Ø#$ÐÐ$Ð$Ð$Ñ$Ø', uÐ=ZÐ=ZÐ'[Ñ'[Ô'[ÐÐ[Ð[Ð[Ñ[Ø%* UÐ;VÐ;VÐ%WÑ%WÔ%W€NÐWÐWÐWÑWØ!& Ð7NÐ7NÐ!OÑ!OÔ!O€IÐOÐOÐOÑOàð)
ð )
ð )
ñ „[ð)
ð )
ð )
r?   r,   é2   c                  óH   — e Zd ZU dZeZded<   eZded<   e	dd	„¦   «         Z
d
S )rH   a'  Per-turn caps on runaway-prone tool calls.

    Inspired by Claude Code v2.1.212 (Week 29, July 2026), which added caps on
    WebSearch calls and subagent spawns to stop runaway search / delegation
    loops. Here the caps count *within a single agent loop* (one turn): the
    counters reset in ``reset_for_turn`` at the start of every
    ``run_conversation``, so a legitimate multi-turn session is never starved,
    but a single turn that spirals into an unbounded search / delegation loop
    is stopped.

    Semantics differ from the per-turn loop *detector* above (which keys on
    repeated identical/failing calls): these caps are a hard ceiling on the
    total count of a tool within the turn and fire regardless of
    ``hard_stop_enabled``. A value of ``0`` disables the cap (unlimited).
    r1   Úmax_web_searchesÚmax_subagentsrK   rL   rM   rI   c                ó  — t          |t          ¦  «        s
 | ¦   «         S  | ¦   «         } | t          |                     d¦  «        |j        ¦  «        t          |                     d¦  «        |j        ¦  «        ¬¦  «        S )zABuild config from the ``tool_loop_guardrails.loop_caps`` section.rd   re   )rd   re   )rU   r   Ú_non_negative_intrV   rd   re   )rZ   rK   r[   s      r@   rY   zLoopCapConfig.from_mapping    sŠ   € õ ˜$¥Ñ(Ô(ð 	Ø�3‘5”5ˆLØ�3‘5”5ˆØˆsÝ.Ø—’Ð+Ñ,Ô,¨hÔ.Gñô õ ,Ø—’˜Ñ)Ô)¨8Ô+Añô ð	
ñ 
ô 
ð 	
r?   N)rK   rL   rM   rI   )r\   r]   r^   r_   Ú"_DEFAULT_MAX_WEB_SEARCHES_PER_TURNrd   r`   Ú_DEFAULT_MAX_SUBAGENTS_PER_TURNre   ra   rY   r>   r?   r@   rH   rH   ‹   sc   € € € € € € ðð ð  ?ÐÐ>Ð>Ð>Ñ>Ø8€MÐ8Ð8Ð8Ñ8àð
ð 
ð 
ñ „[ð
ð 
ð 
r?   rH   c                  óH   — e Zd ZU dZded<   ded<   edd	„¦   «         Zdd„ZdS )ÚToolCallSignaturezDStable, non-reversible identity for a tool name plus canonical args.ÚstrÚ	tool_nameÚ	args_hashÚargsrL   rM   ú'ToolCallSignature'c                óX   — t          |pi ¦  «        } | |t          |¦  «        ¬¦  «        S )N©rm   rn   )Úcanonical_tool_argsÚ_sha256)rZ   rm   ro   Ú	canonicals       r@   Ú	from_callzToolCallSignature.from_call·   s0   € å'¨¨
°Ñ3Ô3ˆ	Øˆs˜Yµ'¸)Ñ2DÔ2DÐEÑEÔEÐEr?   údict[str, str]c                ó    — | j         | j        dœS )z3Return public metadata without raw argument values.rr   rr   ©Úselfs    r@   Úto_metadatazToolCallSignature.to_metadata¼   s   € à!œ^¸$¼.ÐIÐIÐIr?   N)rm   rl   ro   rL   rM   rp   )rM   rw   )r\   r]   r^   r_   r`   ra   rv   r{   r>   r?   r@   rk   rk   °   sj   € € € € € € àNÐNà€N€N�NØ€N€N�NàðFð Fð Fñ „[ðFðJð Jð Jð Jð Jð Jr?   rk   c                  ó    — e Zd ZU dZdZded<   dZded<   dZded<   dZded<   d	Z	d
ed<   dZ
ded<   edd„¦   «         Zedd„¦   «         Zdd„ZdS )ÚToolGuardrailDecisionz8Decision returned by the tool-call guardrail controller.Úallowrl   ÚactionÚcodeÚ Úmessagerm   r   r1   ÚcountNzToolCallSignature | NoneÚ	signaturerM   r-   c                ó   — | j         dv S )N>   Úwarnr~   ©r   ry   s    r@   Úallows_executionz&ToolGuardrailDecision.allows_executionÌ   ó   € àŒ{Ð/Ð/Ð/r?   c                ó   — | j         dv S )N>   ÚhaltÚblockr‡   ry   s    r@   Úshould_haltz!ToolGuardrailDecision.should_haltÐ   r‰   r?   údict[str, Any]c                óŽ   — | j         | j        | j        | j        | j        dœ}| j        �| j                             ¦   «         |d<   |S )N)r   r€   r‚   rm   rƒ   r„   )r   r€   r‚   rm   rƒ   r„   r{   )rz   rK   s     r@   r{   z!ToolGuardrailDecision.to_metadataÔ   sN   € à”kØ”IØ”|ØœØ”Zð 
ð  
ˆð Œ>Ð%Ø $¤× :Ò :Ñ <Ô <ˆD�ÑØˆr?   )rM   r-   )rM   rŽ   )r\   r]   r^   r_   r   r`   r€   r‚   rm   rƒ   r„   Úpropertyrˆ   r�   r{   r>   r?   r@   r}   r}   Á   sÐ   € € € € € € àBÐBà€FÐÐÐÑØ€DÐÐÐÑØ€GÐÐÐÑØ€IÐÐÐÑØ€E€N€N€N�NØ*.€IÐ.Ð.Ð.Ñ.àð0ð 0ð 0ñ „Xð0ð ð0ð 0ð 0ñ „Xð0ð
ð 
ð 
ð 
ð 
ð 
r?   r}   ro   úMapping[str, Any]rM   rl   c                ó°   — t          | t          ¦  «        s$t          dt          | ¦  «        j        › �¦  «        ‚t          j        | dddt          ¬¦  «        S )z5Return sorted compact JSON for parsed tool arguments.z!tool args must be a mapping, got FT©Ú,ú:©Úensure_asciiÚ	sort_keysÚ
separatorsÚdefault)rU   r   Ú	TypeErrorÚtyper\   ÚjsonÚdumpsrl   ©ro   s    r@   rs   rs   á   s]   € å�d�GÑ$Ô$ð SÝÐQ½DÀ¹J¼JÔ<OÐQÐQÑRÔRÐRÝŒ:ØØØØÝðñ ô ð r?   rm   Úresultú
str | Noneútuple[bool, str]c                ó   — |€dS t          | |¦  «        rdS | dk    rKt          |¦  «        }t          |t          ¦  «        r%|                     d¦  «        }|�|dk    rdd|› d�fS dS | d	k    rUt          |¦  «        }t          |t          ¦  «        r1|                     d
¦  «        du rd|                     dd¦  «        v rdS |dd…                              ¦   «         }d|v sd|v s|                     d¦  «        rdS dS )a´  Safety-fallback classifier used only when callers don't pass ``failed``.

    Mirrors ``agent.display._detect_tool_failure`` exactly so the guardrail
    never disagrees with the CLI's user-visible ``[error]`` tag. Production
    callers in ``run_agent.py`` always pass an explicit ``failed=`` derived
    from ``_detect_tool_failure``; this function exists so standalone callers
    (tests, tooling) still get consistent behavior.
    N)Fr�   r   Ú	exit_coder   Tz [exit Ú]r   ÚsuccessFzexceed the limitÚerrorr�   )Tz [full]iô  z"error"z"failed"ÚError)Tz [error])r	   r   rU   ÚdictrV   ÚlowerÚ
startswith)rm   r    rK   r¤   rª   s        r@   Úclassify_tool_failurer¬   î   s6  € ð €~ØˆyÝ" 9¨fÑ5Ô5ð Øˆyà�JÒÐÝ˜vÑ&Ô&ˆÝ�d�DÑ!Ô!ð 	4ØŸš Ñ-Ô-ˆIØÐ$¨°aª¨ØÐ3 yÐ3Ð3Ð3Ð3Ð3Øˆyà�HÒÐÝ˜vÑ&Ô&ˆÝ�d�DÑ!Ô!ð 	'Ø�xŠx˜	Ñ"Ô" eÐ+Ð+Ð0BÀdÇhÂhÈwÐXZÑF[ÔF[Ð0[Ð0[Ø&�à�4�C�4ŒL×ÒÑ Ô €EØ�EÐÐ˜Z¨5Ð0Ð0°F×4EÒ4EÀgÑ4NÔ4NÐ0ØÐàˆ9r?   c                  ób   — e Zd ZdZddd„Zdd„Zed d
„¦   «         Zd!d„Zddœd"d„Z	d#d„Z
d$d„ZdS )%ÚToolCallGuardrailControllerzCPer-turn controller for repeated failed/non-progressing tool calls.NÚconfigúToolCallGuardrailConfig | Nonec                óX   — |pt          ¦   «         | _        |                      ¦   «          d S r<   )r,   r¯   Úreset_for_turn)rz   r¯   s     r@   Ú__init__z$ToolCallGuardrailController.__init__  s-   € ØÐ9Õ 7Ñ 9Ô 9ˆŒØ×ÒÑÔÐÐÐr?   rM   ÚNonec                óZ   — i | _         i | _        i | _        d | _        d| _        d| _        d S )Nr   )Ú_exact_failure_countsÚ_same_tool_failure_countsÚ_no_progressÚ_halt_decisionÚ_turn_web_search_countÚ_turn_subagent_country   s    r@   r²   z*ToolCallGuardrailController.reset_for_turn  s:   € ØCEˆÔ"Ø9;ˆÔ&ØFHˆÔØ<@ˆÔð '(ˆÔ#Ø$%ˆÔ!Ð!Ð!r?   úToolGuardrailDecision | Nonec                ó   — | j         S r<   )r¹   ry   s    r@   Úhalt_decisionz)ToolCallGuardrailController.halt_decision#  s   € àÔ"Ð"r?   rm   rl   ro   rL   r}   c           	     ót  — t                                |t          |¦  «        ¦  «        }|                      |t          |¦  «        |¦  «        }|�|S | j        j        st          ||¬¦  «        S | j                             |d¦  «        }|| j        j	        k    r%t          ddd|› d|› d�|||¬¦  «        }|| _
        |S |                      |¦  «        rV| j                             |¦  «        }|�:|\  }}	|	| j        j        k    r%t          dd	d|› d
|	› d�||	|¬¦  «        }|| _
        |S t          ||¬¦  «        S )N©rm   r„   r   rŒ   Úrepeated_exact_failure_blockzBlocked z: the same tool call failed zd times with identical arguments. Stop retrying it unchanged; change strategy or explain the blocker.©r   r€   r‚   rm   rƒ   r„   Úidempotent_no_progress_blockz/: this read-only call returned the same result z^ times. Stop repeating it unchanged; use the result already provided or try a different query.)rk   rv   Ú_coerce_argsÚ_check_loop_capr¯   r/   r}   r¶   rV   r4   r¹   Ú_is_idempotentr¸   r:   )
rz   rm   ro   r„   Ú	cap_blockÚexact_countÚdecisionÚrecordÚ_result_hashÚrepeat_counts
             r@   Úbefore_callz'ToolCallGuardrailController.before_call'  s§  € Ý%×/Ò/°	½<ÈÑ;MÔ;MÑNÔNˆ	ð ×(Ò(¨µLÀÑ4FÔ4FÈ	ÑRÔRˆ	ØÐ ØÐàŒ{Ô,ð 	SÝ(°9È	ÐRÑRÔRÐRàÔ0×4Ò4°YÀÑBÔBˆØ˜$œ+Ô?Ò?Ð?Ý,ØØ3ð>˜yð >ð >Àkð >ð >ð >ð $Ø!Ø#ðñ ô ˆHð #+ˆDÔØˆOà×Ò˜yÑ)Ô)ð 	$ØÔ&×*Ò*¨9Ñ5Ô5ˆFØÐ!Ø-3Ñ*�˜lØ 4¤;Ô#FÒFÐFÝ4Ø&Ø;ðX yð Xð XØ&2ðXð Xð Xð #,Ø*Ø"+ð ñ  ô  �Hð +3�DÔ'Ø#�Oå$¨yÀIÐNÑNÔNÐNr?   )Úfailedr    r¡   rÎ   úbool | Nonec          	     ó  — t          |¦  «        }t                               ||¦  «        }|€t          ||¦  «        \  }}|�r4| j                             |d¦  «        dz   }|| j        |<   | j                             |d ¦  «         | j                             |d¦  «        dz   }|| j        |<   | j	        j
        r5|| j	        j        k    r%t          ddd|› d|› d�|||¬¦  «        }	|	| _        |	S | j	        j        r+|| j	        j        k    rt          d	d
|› d|› d�|||¬¦  «        S | j	        j        r3|| j	        j        k    r#t          d	dt#          ||¦  «        |||¬¦  «        S t          |||¬¦  «        S | j                             |d ¦  «         | j                             |d ¦  «         |                      |¦  «        s,| j                             |d ¦  «         t          ||¬¦  «        S t'          |¦  «        }
| j                             |¦  «        }d}|�|d         |
k    r|d         dz   }|
|f| j        |<   | j	        j        r+|| j	        j        k    rt          d	d|› d|› d�|||¬¦  «        S t          |||¬¦  «        S )Nr   é   r‹   Úsame_tool_failure_haltzStopped z: it failed z[ times this turn. Stop retrying the same failing tool path and choose a different approach.rÂ   r†   Úrepeated_exact_failure_warningú has failed z€ times with identical arguments. This looks like a loop; inspect the error and change strategy instead of retrying it unchanged.Úsame_tool_failure_warning)rm   rƒ   r„   rÀ   Úidempotent_no_progress_warningz returned the same result z^ times. Use the result already provided or change the query instead of repeating it unchanged.)rÄ   rk   rv   r¬   r¶   rV   r¸   Úpopr·   r¯   r/   r8   r}   r¹   r.   r2   r6   Ú_tool_failure_recovery_hintrÆ   rË   r9   )rz   rm   ro   r    rÎ   r„   Ú_rÈ   Ú
same_countrÉ   Úresult_hashÚpreviousrÌ   s                r@   Ú
after_callz&ToolCallGuardrailController.after_call^  sA  € õ ˜DÑ!Ô!ˆÝ%×/Ò/°	¸4Ñ@Ô@ˆ	Øˆ>Ý-¨i¸Ñ@Ô@‰IˆF�Aàñ /	fØÔ4×8Ò8¸ÀAÑFÔFÈÑJˆKØ4?ˆDÔ& yÑ1ØÔ×!Ò! )¨TÑ2Ô2Ð2àÔ7×;Ò;¸IÀqÑIÔIÈAÑMˆJØ8BˆDÔ*¨9Ñ5àŒ{Ô,ð  °¸t¼{Ô?gÒ1gÐ1gÝ0Ø!Ø1ðd 9ð dð d¸*ð dð dð dð (Ø$Ø'ð
ñ 
ô 
�ð '/�Ô#Ø�àŒ{Ô+ð °¸t¼{Ô?cÒ0cÐ0cÝ,Ø!Ø9à$ð <ð <°+ð <ð <ð <ð (Ø%Ø'ðñ ô ð ð Œ{Ô+ð °
¸d¼kÔ>fÒ0fÐ0fÝ,Ø!Ø4Ý7¸	À:ÑNÔNØ'Ø$Ø'ðñ ô ð õ )°9ÀKÐ[dÐeÑeÔeÐeàÔ"×&Ò& y°$Ñ7Ô7Ð7ØÔ&×*Ò*¨9°dÑ;Ô;Ð;à×"Ò" 9Ñ-Ô-ð 	SØÔ×!Ò! )¨TÑ2Ô2Ð2Ý(°9È	ÐRÑRÔRÐRå" 6Ñ*Ô*ˆØÔ$×(Ò(¨Ñ3Ô3ˆØˆØÐ H¨Q¤K°;Ò$>Ð$>Ø# Aœ;¨™?ˆLØ(3°\Ð'BˆÔ˜)Ñ$àŒ;Ô'ð 	¨L¸D¼KÔ<^Ò,^Ð,^Ý(ØØ5à ð .ð .¸Lð .ð .ð .ð $Ø"Ø#ðñ ô ð õ %¨yÀÐXaÐbÑbÔbÐbr?   r-   c                ó>   — || j         j        v rdS || j         j        v S )NF)r¯   rF   rC   )rz   rm   s     r@   rÆ   z*ToolCallGuardrailController._is_idempotentº  s'   € Ø˜œÔ2Ð2Ð2Ø�5Ø˜DœKÔ8Ð8Ð8r?   r‘   r„   rk   c           	     óœ  — | j         j        }|dk    rM|j        }|r2| j        |k    r't	          ddd|› d�|| j        |¬¦  «        }|| _        |S | xj        dz  c_        dS |d	k    rf|j        }|sdS t          |¦  «        }| j        |k    r/t	          dd
d| j        › d|› d�|| j        |¬¦  «        }|| _        |S | xj        |z  c_        dS dS )aN  Enforce and advance the per-turn runaway-loop counters.

        Returns a ``block`` decision when the cap is already reached, otherwise
        increments the relevant counter for the allowed call and returns
        ``None``. A cap of 0 disables that limit entirely. Counters reset each
        turn via ``reset_for_turn``.
        r   rŒ   Úloop_web_search_capz/Blocked web_search: this turn has already made z� web searches, the per-turn limit. This looks like a runaway search loop. Work with the results you already have and give the user your answer.rÂ   rÑ   Nr'   Úloop_subagent_capz5Blocked delegate_task: this turn has already spawned z subagents (limit zl). This looks like a runaway delegation loop. Finish the work with the results you have and answer the user.)	r¯   rJ   rd   rº   r}   r¹   re   Ú_subagent_spawn_countr»   )rz   rm   ro   r„   ÚcapsÚcaprÉ   Úspawn_counts           r@   rÅ   z+ToolCallGuardrailController._check_loop_cap¿  sY  € ð Œ{Ô$ˆà˜Ò$Ð$ØÔ'ˆCØð  �tÔ2°cÒ9Ð9Ý0Ø"Ø.ð>È#ð >ð >ð >ð
 (ØÔ5Ø'ðñ ô �ð '/�Ô#Ø�ØÐ'Ô'¨1Ñ,Ð'Ô'Ø�4à˜Ò'Ð'ØÔ$ˆCØð Ø�tÝ/°Ñ5Ô5ˆKØÔ(¨CÒ/Ð/Ý0Ø"Ø,ðNØÔ4ðNð NØHKðNð Nð Nð
 (ØÔ3Ø'ðñ ô �ð '/�Ô#Ø�ØÐ%Ô%¨Ñ4Ð%Ô%Ø�4àˆtr?   r<   )r¯   r°   )rM   r´   )rM   r¼   )rm   rl   ro   rL   rM   r}   )
rm   rl   ro   rL   r    r¡   rÎ   rÏ   rM   r}   )rm   rl   rM   r-   )rm   rl   ro   r‘   r„   rk   rM   r¼   )r\   r]   r^   r_   r³   r²   r�   r¾   rÍ   rÝ   rÆ   rÅ   r>   r?   r@   r®   r®     sØ   € € € € € ØMÐMðð ð ð ð ð	&ð 	&ð 	&ð 	&ð ð#ð #ð #ñ „Xð#ð5Oð 5Oð 5Oð 5Oðz #ðZcð Zcð Zcð Zcð Zcð Zcðx9ð 9ð 9ð 9ð
<ð <ð <ð <ð <ð <r?   r®   rÉ   c                ób   — t          j        | j        |                      ¦   «         dœd¬¦  «        S )zCBuild a synthetic role=tool content string for a blocked tool call.)r§   Ú	guardrailF)r—   )r�   rž   r‚   r{   )rÉ   s    r@   Útoolguard_synthetic_resultrè   þ  s?   € åŒ:àÔ%Ø!×-Ò-Ñ/Ô/ð	
ð 	
ð ðñ ô ð r?   c           	     óŽ   — |j         dvs|j        s| S |j         dk    rdnd}d|› d|j        › d|j        › d|j        › d	�	}| pd
|z   S )z;Append runtime guidance to the current tool result content.>   r‹   r†   r‹   zTool loop hard stopzTool loop warningz

[z: z; count=z; r¥   r�   )r   r‚   r€   rƒ   )r    rÉ   ÚlabelÚsuffixs       r@   Úappend_toolguard_guidancerì   	  s™   € à„Ð.Ð.Ð.°hÔ6FÐ.ØˆØ%-¤_¸Ò%>Ð%>Ð!Ð!ÐDW€Eð	H�ð 	Hð 	HØŒ=ð	Hð 	HØ"*¤.ð	Hð 	HØ4<Ô4Dð	Hð 	Hð 	Hð ð ˆL�b˜FÑ"Ð"r?   rƒ   r1   c                ó2   — | › d|› d�}| dk    r|dz   S |dz   S )zDAction-oriented guidance for recovering from repeated tool failures.rÔ   zÁ times this turn. This looks like a loop. Do not switch to text-only replies; keep using tools, but diagnose before retrying. First inspect the latest error/output and verify your assumptions. r   zÜFor terminal failures, run a small diagnostic such as `pwd && ls -la` in the same tool, then try an absolute path, a simpler command, a different working directory, or a different tool such as read_file/write_file/patch.zóTry different arguments, a narrower query/path, an absolute path when relevant, or a different tool that can make progress. If the blocker is external, report the blocker after one diagnostic attempt instead of repeating the same failing path.r>   )rm   rƒ   Úcommons      r@   rØ   rØ     sa   € ð ð 	Nð 	N %ð 	Nð 	Nð 	Nð ð
 �JÒÐØðYñ
ð 	
ð
 ð	_ñð r?   rL   c                ó4   — t          | t          ¦  «        r| ni S r<   )rU   r   rŸ   s    r@   rÄ   rÄ   )  s   € Ý˜d¥GÑ,Ô,Ð4ˆ4ˆ4°"Ð4r?   c                óÌ   — t          | pd¦  «        }|�?	 t          j        |dddt          ¬¦  «        }n## t          $ r t          |¦  «        }Y nw xY w| pd}t          |¦  «        S )Nr�   FTr“   r–   )r   r�   rž   rl   r›   rt   )r    Úparsedru   s      r@   rË   rË   -  sŠ   € Ý˜V˜\ rÑ*Ô*€FØÐð		$Ýœ
ØØ"ØØ%Ýðñ ô ˆIˆIøõ ð 	$ð 	$ð 	$Ý˜F™œˆIˆIˆIð	$øøøð �L˜bˆ	Ý�9ÑÔÐs   •4 ´AÁAÚvaluer   rš   r-   c                ó   — | €|S t          | t          ¦  «        r| S t          | t          t          f¦  «        rt          | ¦  «        S t          | t          ¦  «        r2|                      ¦   «                              ¦   «         }|dv rdS |dv rdS |S )N>   Ú1ÚonÚyesÚtrueÚenabledT>   Ú0ÚnoÚoffÚfalseÚdisabledF)rU   r-   r1   Úfloatrl   Ústriprª   )rò   rš   Úlowereds      r@   rW   rW   ?  s—   € Ø€}ØˆÝ�%�ÑÔð ØˆÝ�%�#�u˜Ñ&Ô&ð Ý�E‰{Œ{ÐÝ�%�ÑÔð Ø—+’+‘-”-×%Ò%Ñ'Ô'ˆØÐ;Ð;Ð;Ø�4ØÐ=Ð=Ð=Ø�5Ø€Nr?   c                ór   — | €|S 	 t          | ¦  «        }n# t          t          f$ r |cY S w xY w|dk    r|n|S )NrÑ   ©r1   r›   Ú
ValueError©rò   rš   rñ   s      r@   rX   rX   O  sY   € Ø€}ØˆðÝ�U‘”ˆˆøÝ•zÐ"ð ð ð Øˆˆˆðøøøà˜q’[�[ˆ6ˆ6 gÐ-ó   † –,«,c                ór   — | €|S 	 t          | ¦  «        }n# t          t          f$ r |cY S w xY w|dk    r|n|S )ziParse a session-cap value. 0 is a valid (disable) value; negatives and
    junk fall back to the default.Nr   r  r  s      r@   rg   rg   Y  s[   € ð €}ØˆðÝ�U‘”ˆˆøÝ•zÐ"ð ð ð Øˆˆˆðøøøà˜q’[�[ˆ6ˆ6 gÐ-r  c                óª   — t          | t          ¦  «        r|                      d¦  «        nd}t          |t          ¦  «        r|rt	          |¦  «        S dS )aU  How many subagents a single delegate_task call spawns.

    delegate_task runs in one of two modes: a batch (``tasks`` is a non-empty
    list, one child per item) or a single task (``goal``). Count the batch size
    when present, otherwise 1, so the session subagent cap reflects real spawns
    rather than delegate_task invocations.
    ÚtasksNrÑ   )rU   r   rV   ÚlistÚlen)ro   r  s     r@   râ   râ   e  sS   € õ ",¨Dµ'Ñ!:Ô!:ÐDˆD�HŠH�WÑÔÐÀ€EÝ�%�ÑÔð  5ð Ý�5‰zŒzÐØˆ1r?   c                óv   — t          j        |                      dd¦  «        ¦  «                             ¦   «         S )Nzutf-8Úsurrogatepass)ÚhashlibÚsha256ÚencodeÚ	hexdigest)rò   s    r@   rt   rt   s  s.   € õ
 Œ>˜%Ÿ,š, w°Ñ@Ô@ÑAÔA×KÒKÑMÔMÐMr?   )ro   r‘   rM   rl   )rm   rl   r    r¡   rM   r¢   )rÉ   r}   rM   rl   )r    rl   rÉ   r}   rM   rl   )rm   rl   rƒ   r1   rM   rl   )ro   rL   rM   r‘   )r    r¡   rM   rl   )rò   r   rš   r-   rM   r-   )rò   r   rš   r1   rM   r1   )ro   r‘   rM   r1   )rò   rl   rM   rl   )%r_   Ú
__future__r   r  r�   Údataclassesr   r   Útypingr   r   Úutilsr   Ú agent.tool_result_classificationr	   Ú	frozensetr=   rE   r,   rh   ri   rH   rk   r}   rs   r¬   r®   rè   rì   rØ   rÄ   rË   rW   rX   rg   râ   rt   r>   r?   r@   ú<module>r     s!  ððð ð #Ð "Ð "Ð "Ð "Ð "à €€€Ø €€€Ø (Ð (Ð (Ð (Ð (Ð (Ð (Ð (Ø Ð Ð Ð Ð Ð Ð Ð à !Ð !Ð !Ð !Ð !Ð !Ø HÐ HÐ HÐ HÐ HÐ Hð "˜	ðð ð ñô Ð ð*  �iðð ð ñô Ð ð, €�$ÐÑÔð>
ð >
ð >
ð >
ð >
ñ >
ô >
ñ Ôð>
ðN &(Ð "Ø"$Ð ð €�$ÐÑÔð!
ð !
ð !
ð !
ð !
ñ !
ô !
ñ Ôð!
ðH €�$ÐÑÔðJð Jð Jð Jð Jñ Jô Jñ ÔðJð  €�$ÐÑÔðð ð ð ð ñ ô ñ Ôðð>
ð 
ð 
ð 
ð ð  ð  ð  ðFjð jð jð jð jñ jô jð jðZð ð ð ð	#ð 	#ð 	#ð 	#ðð ð ð ð(5ð 5ð 5ð 5ðð ð ð ð$ð ð ð ð .ð .ð .ð .ð	.ð 	.ð 	.ð 	.ðð ð ð ðNð Nð Nð Nð Nð Nr?   