§
    ‰eîi/  ã                   óL  — d Z ddlZddlZddlZddlZddlmZmZmZ  ej	        e
¦  «        ZdZdZdZdZdZd	efd
„Zefdeded	eeef         fd„Zded	efd„Zdeded	efd„Zdedededed	ef
d„Zdedfdededededeez  dz  d	efd„Zdefdee         ded	ee         fd„ZdS )aÉ  Tool result persistence -- preserves large outputs instead of truncating.

Defense against context-window overflow operates at three levels:

1. **Per-tool output cap** (inside each tool): Tools like search_files
   pre-truncate their own output before returning. This is the first line
   of defense and the only one the tool author controls.

2. **Per-result persistence** (maybe_persist_tool_result): After a tool
   returns, if its output exceeds the tool's registered threshold
   (registry.get_max_result_size), the full output is written INTO THE
   SANDBOX temp dir (for example /tmp/hermes-results/{tool_use_id}.txt on
   standard Linux, or $TMPDIR/hermes-results/{tool_use_id}.txt on Termux)
   via env.execute(). The in-context content is replaced with a preview +
   file path reference. The model can read_file to access the full output
   on any backend.

3. **Per-turn aggregate budget** (enforce_turn_budget): After all tool
   results in a single assistant turn are collected, if the total exceeds
   MAX_TURN_BUDGET_CHARS (200K), the largest non-persisted results are
   spilled to disk until the aggregate is under budget. This catches cases
   where many medium-sized results combine to overflow context.
é    N)ÚDEFAULT_PREVIEW_SIZE_CHARSÚBudgetConfigÚDEFAULT_BUDGETz<persisted-output>z</persisted-output>z/tmp/hermes-resultsÚHERMES_PERSIST_EOFÚ__budget_enforcement__Úreturnc                 ó  — | �|t          | dd¦  «        }t          |¦  «        r\	  |¦   «         }|r|                     d¦  «        pd}|› d�S n2# t          $ r%}t                               d|¦  «         Y d}~nd}~ww xY wt          S )z=Return the best temp-backed storage dir for this environment.NÚget_temp_dirú/z/hermes-resultsz"Could not resolve env temp dir: %s)ÚgetattrÚcallableÚrstripÚ	ExceptionÚloggerÚdebugÚSTORAGE_DIR)Úenvr
   Útemp_dirÚexcs       ú;/home/div/.hermes/hermes-agent/tools/tool_result_storage.pyÚ_resolve_storage_dirr   ,   s½   € à
€Ý˜s N°DÑ9Ô9ˆÝ�LÑ!Ô!ð 	8ð8Ø'˜<™>œ>�ð ð 8Ø'Ÿš¨sÑ3Ô3Ð:°s�HØ&Ð7Ð7Ð7Ð7ð8øõ ð Hð Hð HÝ—’ÐAÀ3ÑGÔGÐGÐGÐGÐGÐGÐGøøøøðHøøøõ Ðs   ¤
A Á
A<ÁA7Á7A<ÚcontentÚ	max_charsc                 ó¢   — t          | ¦  «        |k    r| dfS | d|…         }|                     d¦  «        }||dz  k    r|d|dz   …         }|dfS )zGTruncate at last newline within max_chars. Returns (preview, has_more).FNÚ
é   é   T)ÚlenÚrfind)r   r   Ú	truncatedÚlast_nls       r   Úgenerate_previewr"   <   sg   € å
ˆ7�|„|�yÒ Ð Ø˜ˆ~ÐØ˜
˜˜
Ô#€IØ�oŠo˜dÑ#Ô#€GØ�˜a‘ÒÐØ˜l˜w¨™{˜lÔ+ˆ	Ø�dˆ?Ðó    c                 óh   — t           | vrt           S dt          j        ¦   «         j        dd…         › �S )z=Return a heredoc delimiter that doesn't collide with content.ÚHERMES_PERSIST_Né   )ÚHEREDOC_MARKERÚuuidÚuuid4Úhex)r   s    r   Ú_heredoc_markerr+   G   s3   € å˜WÐ$Ð$ÝÐØ3�TœZ™\œ\Ô-¨b¨q¨bÔ1Ð3Ð3Ð3r#   Úremote_pathc           
      ó*  — t          | ¦  «        }t          j                             |¦  «        }dt	          j        |¦  «        › dt	          j        |¦  «        › d|› d| › d|› �
}|                     |d¬¦  «        }|                     dd	¦  «        d
k    S )zJWrite content into the sandbox via env.execute(). Returns True on success.z	mkdir -p z
 && cat > z << 'z'
r   é   )ÚtimeoutÚ
returncoder   r   )r+   ÚosÚpathÚdirnameÚshlexÚquoteÚexecuteÚget)r   r,   r   ÚmarkerÚstorage_dirÚcmdÚresults          r   Ú_write_to_sandboxr<   N   s¯   € å˜WÑ%Ô%€FÝ”'—/’/ +Ñ.Ô.€Kð	•E”K Ñ,Ô,ð 	ð 	½¼ÀKÑ8PÔ8Pð 	ð 	ÐW]ð 	ð 	Øð	ð 	àð	ð 	ð ð
 �[Š[˜ bˆ[Ñ)Ô)€FØ�:Š:�l AÑ&Ô&¨!Ò+Ð+r#   ÚpreviewÚhas_moreÚoriginal_sizeÚ	file_pathc                 óä   — |dz  }|dk    r
|dz  d›d�}n|d›d�}t           › d�}|d|d›d|› d	�z  }|d
|› d�z  }|dz  }|dt          | ¦  «        › d�z  }|| z  }|r|dz  }|dt          › �z  }|S )z/Build the <persisted-output> replacement block.i   z.1fz MBz KBr   z This tool result was too large (Ú,z characters, z).
zFull output saved to: zZUse the read_file tool with offset and limit to access specific sections of this output.

zPreview (first z	 chars):
z
...)ÚPERSISTED_OUTPUT_TAGr   ÚPERSISTED_OUTPUT_CLOSING_TAG)r=   r>   r?   r@   Úsize_kbÚsize_strÚmsgs          r   Ú_build_persisted_messagerH   [   sÞ   € ð ˜dÑ"€GØ�$‚€Ø ‘nÐ-Ð-Ð-Ð-ˆˆàÐ&Ð&Ð&Ð&ˆå!Ð
%Ð
%Ð
%€CØÐZ¨mÐZÐZÐZÈHÐZÐZÐZÑZ€CØÐ1 IÐ1Ð1Ð1Ñ1€CØÐiÑi€CØÐ5�S ™\œ\Ð5Ð5Ð5Ñ5€CØˆ7�N€CØð Øˆw‰ˆØÐ.Õ,Ð.Ð.Ñ.€CØ€Jr#   Ú	tool_nameÚtool_use_idÚconfigÚ	thresholdc                 óŠ  — |�|n|                      |¦  «        }|t          d¦  «        k    r| S t          | ¦  «        |k    r| S t          |¦  «        }|› d|› d�}t	          | |j        ¬¦  «        \  }	}
|��	 t          | ||¦  «        rJt                               d||t          | ¦  «        |¦  «         t          |	|
t          | ¦  «        |¦  «        S n3# t          $ r&}t                               d||¦  «         Y d}~nd}~ww xY wt                               d|t          | ¦  «        ¦  «         |	› d	t          | ¦  «        d
›d�S )aì  Layer 2: persist oversized result into the sandbox, return preview + path.

    Writes via env.execute() so the file is accessible from any backend
    (local, Docker, SSH, Modal, Daytona). Falls back to inline truncation
    if write fails or no env is available.

    Args:
        content: Raw tool result string.
        tool_name: Name of the tool (used for threshold lookup).
        tool_use_id: Unique ID for this tool call (used as filename).
        env: The active BaseEnvironment instance, or None.
        config: BudgetConfig controlling thresholds and preview size.
        threshold: Explicit override; takes precedence over config resolution.

    Returns:
        Original content if small, or <persisted-output> replacement.
    NÚinfr   z.txt)r   z4Persisted large tool result: %s (%s, %d chars -> %s)zSandbox write failed for %s: %szDInline-truncating large tool result: %s (%d chars, no sandbox write)z 

[Truncated: tool response was rB   z3 chars. Full output could not be saved to sandbox.])Úresolve_thresholdÚfloatr   r   r"   Úpreview_sizer<   r   ÚinforH   r   Úwarning)r   rI   rJ   r   rK   rL   Úeffective_thresholdr9   r,   r=   r>   r   s               r   Úmaybe_persist_tool_resultrU   t   s—  € ð2 (1Ð'<˜)˜)À&×BZÒBZÐ[dÑBeÔBeÐà�e E™lœlÒ*Ð*Øˆå
ˆ7�|„|Ð*Ò*Ð*Øˆå& sÑ+Ô+€KØ Ð4Ð4 ;Ð4Ð4Ð4€KÝ(¨¸FÔ<OÐPÑPÔPÑ€GˆXà
€ð	PÝ  ¨+°sÑ;Ô;ð ^Ý—’ØJØ˜{­C°©L¬L¸+ñô ð õ 0°¸Å3ÀwÁ<Ä<ÐQ\Ñ]Ô]Ð]ð^øõ ð 	Pð 	Pð 	PÝ�NŠNÐ<¸kÈ3ÑOÔOÐOÐOÐOÐOÐOÐOøøøøð	Pøøøõ ‡K‚KØNØ•3�w‘<”<ñô ð ð
 ð 	7ð 	7Ý),¨W©¬ÐAð	7ð 	7ð 	7ðs   Á7AC Ã
DÃC>Ã>DÚtool_messagesc           	      óN  — g }d}t          | ¦  «        D ]O\  }}|                     dd¦  «        }t          |¦  «        }||z  }t          |vr|                     ||f¦  «         ŒP||j        k    r| S |                     d„ d¬¦  «         |D ]™\  }	}||j        k    r nˆ| |	         }|d         }|                     dd|	› �¦  «        }
t          |t          |
||d¬	¦  «        }||k    r>||z  }|t          |¦  «        z  }|| |	         d<   t           
                    d
|
|¦  «         Œš| S )a"  Layer 3: enforce aggregate budget across all tool results in a turn.

    If total chars exceed budget, persist the largest non-persisted results
    first (via sandbox write) until under budget. Already-persisted results
    are skipped.

    Mutates the list in-place and returns it.
    r   r   Ú c                 ó   — | d         S )Nr   © )Úxs    r   ú<lambda>z%enforce_turn_budget.<locals>.<lambda>È   s
   €  ! A¤$€ r#   T)ÚkeyÚreverseÚtool_call_idÚbudget_)r   rI   rJ   r   rK   rL   z7Budget enforcement: persisted tool result %s (%d chars))Ú	enumerater7   r   rC   ÚappendÚturn_budgetÚsortrU   Ú_BUDGET_TOOL_NAMEr   rR   )rV   r   rK   Ú
candidatesÚ
total_sizeÚirG   r   ÚsizeÚidxrJ   Úreplacements               r   Úenforce_turn_budgetrl   ¯   sx  € ð €JØ€JÝ˜MÑ*Ô*ð )ð )‰ˆˆ3Ø—'’'˜) RÑ(Ô(ˆÝ�7‰|Œ|ˆØ�dÑˆ
Ý wÐ.Ð.Ø×Ò˜q $˜iÑ(Ô(Ð(øà�VÔ'Ò'Ð'ØÐà‡O‚O˜˜°€OÑ5Ô5Ð5àð ð ‰	ˆˆTØ˜Ô+Ò+Ð+ØˆEØ˜CÔ ˆØ�i”.ˆØ—g’g˜n¨o¸¨o¨oÑ>Ô>ˆå/ØÝ'Ø#ØØØð
ñ 
ô 
ˆð ˜'Ò!Ð!Ø˜$ÑˆJØ�#˜kÑ*Ô*Ñ*ˆJØ,7ˆM˜#Ô˜yÑ)Ý�KŠKØIØ˜Tñô ð øð
 Ðr#   )Ú__doc__Úloggingr1   r4   r(   Útools.budget_configr   r   r   Ú	getLoggerÚ__name__r   rC   rD   r   r'   re   Ústrr   ÚintÚtupleÚboolr"   r+   r<   rH   rP   rU   ÚlistÚdictrl   rZ   r#   r   ú<module>rx      s5  ððð ð0 €€€Ø 	€	€	€	Ø €€€Ø €€€ðð ð ð ð ð ð ð ð ð ð 
ˆÔ	˜8Ñ	$Ô	$€Ø+Ð Ø4Ð Ø#€Ø%€Ø,Ð ð ð ð ð ð ð  5Oð ð ˜cð ¨cð ÐSXÐY\Ð^bÐYbÔScð ð ð ð ð4˜Sð 4 Sð 4ð 4ð 4ð 4ð
,˜sð 
,°ð 
,¸dð 
,ð 
,ð 
,ð 
,ðØðàðð ðð ð	ð
 	ðð ð ð ð: 	Ø)Ø$(ð8ð 8Øð8àð8ð ð8ð
 ð8ð �U‰{˜TÑ!ð8ð 	ð8ð 8ð 8ð 8ðz 	Ø)ð3ð 3Ø˜”:ð3ð ð3ð 
ˆ$„Zð	3ð 3ð 3ð 3ð 3ð 3r#   