
    PL
j-                       d Z ddlmZ ddlZddlZddlZddlZddlmZ ddl	m
Z
mZmZmZ ddlmZ  ej        e          Z edh          Z eh d          Z eh d	          Z ej        d
ej                  Z ej        d          Zd/dZd0dZd1dZd2dZd3dZ d4dZ!d5dZ"d6d#Z#d7d&Z$d8d9d+Z%d:d-Z&g d.Z'dS );u  Tool-dispatch helpers — parallelism gating, multimodal envelopes, mutation tracking.

Pure module-level utilities extracted from ``run_agent.py``:

* ``_is_destructive_command`` — terminal-command heuristic used to gate
  parallel batch dispatch.
* ``_should_parallelize_tool_batch`` / ``_extract_parallel_scope_path`` /
  ``_paths_overlap`` — the rules engine deciding when a multi-tool batch
  can run concurrently.
* ``_is_multimodal_tool_result`` / ``_multimodal_text_summary`` /
  ``_append_subdir_hint_to_multimodal`` — envelope helpers for the
  ``{"_multimodal": True, "content": [...], "text_summary": ...}`` dict
  shape returned by tools like ``computer_use``.
* ``_extract_file_mutation_targets`` / ``_extract_error_preview`` —
  per-turn file-mutation verifier inputs.
* ``_trajectory_normalize_msg`` — strip image blobs from a message for
  trajectory saving.

All helpers are stateless.  ``run_agent`` re-exports each name so existing
``from run_agent import ...`` imports in tests and other modules keep
working unchanged.
    )annotationsN)Path)AnyDictListOptional)FILE_MUTATING_TOOL_NAMESclarify>   	read_file
skill_view
web_searchskills_listweb_extractha_get_statesearch_filessession_searchvision_analyzeha_list_entitiesha_list_services>   patchr   
write_filez(?:^|\s|&&|\|\||;|`)(?:
        rm\s|rmdir\s|
        cp\s|install\s|
        mv\s|
        sed\s+-i|
        truncate\s|
        dd\s|
        shred\s|
        git\s+(?:reset|clean|checkout)\s
    )z[^>]>[^>]|^>[^>]cmdstrreturnboolc                ~    | sdS t                               |           rdS t                              |           rdS dS )zJHeuristic: does this terminal command look like it modifies/deletes files?FT)_DESTRUCTIVE_PATTERNSsearch_REDIRECT_OVERWRITE)r   s    ?/home/kuhnn/.hermes/hermes-agent/agent/tool_dispatch_helpers.py_is_destructive_commandr!   O   sJ     u##C(( t!!#&& t5    	tool_namec                H    	 ddl m}  ||           S # t          $ r Y dS w xY w)zCheck if an MCP tool comes from a server with parallel tool calls enabled.

    Lazy-imports from ``tools.mcp_tool`` to avoid circular dependencies.
    Returns False if the MCP module is not available.
    r   )is_mcp_tool_parallel_safeF)tools.mcp_toolr%   	Exception)r#   r%   s     r    _is_mcp_tool_parallel_safer(   Z   sL    <<<<<<((333   uus    
!!c           	        t          |           dk    rdS d | D             }t          d |D                       rdS g }| D ]}|j        j        }	 t	          j        |j        j                  }n:# t          $ r- t          j	        d||j        j        dd                    Y  dS w xY wt          |t                    s+t          j	        d|t          |          j                    dS |t          v rIt          ||           dS t          fd	|D                       r dS |                               |t"          vrt%          |          s dS d
S )z?Return True when a tool-call batch is safe to run concurrently.   Fc                &    g | ]}|j         j        S  )functionname).0tcs     r    
<listcomp>z2_should_parallelize_tool_batch.<locals>.<listcomp>l   s    888r"+"888r"   c              3  (   K   | ]}|t           v V  d S N)_NEVER_PARALLEL_TOOLS)r/   r.   s     r    	<genexpr>z1_should_parallelize_tool_batch.<locals>.<genexpr>m   s(      
@
@T4((
@
@
@
@
@
@r"   u@   Could not parse args for %s — defaulting to sequential; raw=%sN   u6   Non-dict args for %s (%s) — defaulting to sequentialc              3  8   K   | ]}t          |          V  d S r3   )_paths_overlap)r/   existingscoped_paths     r    r5   z1_should_parallelize_tool_batch.<locals>.<genexpr>   s-      XXX>+x88XXXXXXr"   T)lenanyr-   r.   jsonloads	argumentsr'   loggingdebug
isinstancedicttype__name___PATH_SCOPED_TOOLS_extract_parallel_scope_pathappend_PARALLEL_SAFE_TOOLSr(   )
tool_calls
tool_namesreserved_paths	tool_callr#   function_argsr:   s         @r    _should_parallelize_tool_batchrO   g   s   
:!u88Z888J

@
@Z
@
@
@@@ u!#N  	&+		 Jy'9'CDDMM 	 	 	MR",TcT2  
 555	 -.. 	MH]##,  
 55***6y-PPK"uuXXXXXXXXX uu!!+...000-i88 uu4s   A112B('B(rN   rC   Optional[Path]c                   | t           vrdS |                    d          }t          |t                    r|                                sdS t          |                                          }|                                r9t          t          j	        
                    t          |                              S t          t          j	        
                    t          t          j                    |z                                S )z8Return the normalized file target for path-scoped tools.Npath)rF   getrB   r   stripr   
expanduseris_absoluteosrR   abspathcwd)r#   rN   raw_pathexpandeds       r    rG   rG      s    ***t  ((Hh$$ HNN,<,< tH~~((**H 4BGOOCMM22333 DHJJ$9 : :;;<<<r"   leftr   rightc                   | j         }|j         }|r|s/t          |          t          |          k    ot          |          S t          t          |          t          |                    }|d|         |d|         k    S )z9Return True when two paths may refer to the same subtree.N)partsr   minr;   )r\   r]   
left_partsright_parts
common_lens        r    r8   r8      s    J+K J[ JJ4#4#44Ij9I9IIS__c+&6&677Jkzk"k+:+&>>>r"   valuer   c                    t          | t                    o>|                     d          du o't          |                     d          t                    S )a  True if the value is a multimodal tool result envelope.

    Multimodal handlers (e.g. tools/computer_use) return a dict with
    `_multimodal=True`, a `content` key holding OpenAI-style content
    parts, and an optional `text_summary` for string-only fallbacks.
    _multimodalTcontent)rB   rC   rS   list)rd   s    r    _is_multimodal_tool_resultri      sM     	5$ 	3IIm$$,	3uyy++T22r"   c           	     J   t          |           r|                     d          rt          | d                   S g }|                     d          pg D ]f}t          |t                    rO|                    d          dk    r6|                    t          |                    dd                               g|rd                    |          S dS t          | t                    r| S 	 t          j        | t                    S # t          $ r t          |           cY S w xY w)	u   Extract a plain text view of a multimodal tool result.

    Used wherever downstream code needs a string — logging, previews,
    persistence size heuristics, fall-back content for providers that
    don't support multipart tool messages.
    text_summaryrg   rD   text 
z[multimodal tool result])default)
ri   rS   r   rB   rC   rH   joinr=   dumpsr'   )rd   r_   ps      r    _multimodal_text_summaryrs      s-    "%(( 	*99^$$ 	.u^,---9%%+ 	5 	5A!T"" 5quuV}}'>'>Svr!2!233444 	$99U###))% z%----   5zzs   +D D"!D"Dict[str, Any]hintNonec                   t          |           sdS |                     d          pg }|D ][}t          |t                    rD|                    d          dk    r+t	          |                    dd                    |z   |d<    n\|                    dd|d           || d<   t          |                     d          t                    r| d         |z   | d<   dS dS )	zMutate a multimodal tool-result envelope to append a subdir hint.

    The hint is added to the first text part so the model sees it; image
    parts are left untouched. `text_summary` is also updated for
    string-fallback callers.
    Nrg   rD   rl   rm   r   rD   rl   rk   )ri   rS   rB   rC   r   insert)rd   ru   r_   rr   s       r    !_append_subdir_hint_to_multimodalrz      s     &e,, IIi  &BE ! !a 	155==F#:#:AEE&"--..5AfIEQ66777 i%))N++S11 = %n 5 <n= =r"   args	List[str]c                <   | t           vrg S | dk    r)|                    d          }|rt          |          gng S |                    d          pd}|dk    r)|                    d          }|rt          |          gng S |dk    r|                    d          pd}t          |t                    r|sg S g }t	          j        d|t          j                  D ]@}|                    d                                          }|r|	                    |           A|S g S )	ay  Return the file paths a ``write_file`` or ``patch`` call is targeting.

    For ``write_file`` and ``patch`` in replace mode this is just ``args["path"]``.
    For ``patch`` in V4A patch mode we parse the patch content for
    ``*** Update File:`` / ``*** Add File:`` / ``*** Delete File:`` headers so
    the verifier can track each file in a multi-file patch separately.
    r   rR   modereplacer   rm   z/^\*\*\*\s+(?:Update|Add|Delete)\s+File:\s*(.+)$r*   )
_FILE_MUTATING_TOOLSrS   r   rB   refinditer	MULTILINEgrouprT   rH   )r#   r{   rr   r~   bodypaths_ms          r    _extract_file_mutation_targetsr      s8    ,,,	L  HHV$Axx"$88F(yDyHHV$Axx"$wxx  &B$$$ 	D 	I+>L
 
 	  	 B
 !!##A  QIr"      resultmax_lenintc                V   | t          |           nd}t          |t                    s"	 t          |          }n# t          $ r Y dS w xY w|                                }|                    d          rk	 t          j        |          }t          |t                    r0t          |	                    d          t                    r|d         }n# t          $ r Y nw xY wd
                    |                                          }t          |          |k    r|d|dz
           dz   }|S )zFPull a one-line error summary out of a tool result for footer display.Nrm   {error r*   u   …)rs   rB   r   r'   rT   
startswithr=   r>   rC   rS   rp   splitr;   )r   r   rl   strippeddatas        r    _extract_error_previewr     s:   /5/A#F+++rDdC   	t99DD 	 	 	22	 zz||H3 	:h''D$%% %*TXXg5F5F*L*L %G} 	 	 	D	 88DJJLL!!D
4yy7MgkM"U*Ks"   : 
AA5AC 
CCmsgc                   t          | t                    s| S |                     d          }t          |          ri | dt	          |          iS t          |t
                    rig }|D ]\}t          |t                    r0|                    d          dv r|                    ddd           G|                    |           ]i | d|iS | S )a  Strip image blobs from a message for trajectory saving.

    Returns a shallow copy with multimodal tool results replaced by their
    text_summary, and image parts in content lists replaced by
    `[screenshot]` placeholders. Keeps the message schema otherwise intact.
    rg   rD   >   image	image_urlinput_imagerl   z[screenshot]rx   )rB   rC   rS   ri   rs   rh   rH   )r   rg   cleanedrr   s       r    _trajectory_normalize_msgr   )  s     c4   
ggi  G!'** ED#Dy":7"C"CDDD'4   + 	" 	"A!T"" "quuV}}8]']']GGHHHHq!!!!*#*y'***Jr"   )r4   rI   rF   r   r   r!   rO   rG   r8   ri   rs   rz   r   r   r   )r   r   r   r   )r#   r   r   r   )r   r   )r#   r   rN   rC   r   rP   )r\   r   r]   r   r   r   )rd   r   r   r   )rd   r   r   r   )rd   rt   ru   r   r   rv   )r#   r   r{   rt   r   r|   )r   )r   r   r   r   r   r   )r   rt   r   rt   )(__doc__
__future__r   r=   r@   rW   r   pathlibr   typingr   r   r   r    agent.tool_result_classificationr	   r   	getLoggerrE   logger	frozensetr4   rI   rF   compileVERBOSEr   r   r!   r(   rO   rG   r8   ri   rs   rz   r   r   r   __all__r,   r"   r    <module>r      s.   . # " " " " "   				 				       , , , , , , , , , , , ,      
	8	$	$ "	9+..  !y " " "    YCCCDD  #
		 J   !bj!455    
 
 
 
+ + + +\= = = ="? ? ? ?      2= = = =*       F    2   .  r"   