Ë
    ãß²j�q  ã                   óØ  — U d Z ddlZddl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 ddlmZmZ ddlmZmZ d	Zd
dhZdZedz  ed<   dZdZedz  ed<   dZdefd„Zdefd„Zdedz  fd„Zdedz  fd„Zde edf   fd„Z!dededefd„Z"d3dedededz  fd„Z# e$h d£«      Z%de e&e'e   f   fd„Z(d3dddœdedede&dz  de'e   dz  dedz  f
d„Z)d Z*d!Z+d3d"e'e   dededz  fd#„Z,d3d$e'e   dededz  fd%„Z-d3d$e'e   dededz  fd&„Z.d3dededz  fd'„Z/d3dedededz  fd(„Z0d3dedededz  fd)„Z1d*Z2ded+edz  dededz  fd,„Z3d4ded-ed+edz  de4fd.„Z5d/ede&fd0„Z6d/ede&fd1„Z7d/ede&fd2„Z8y)5aq  Write-side safety guards for write_file / patch.

Every guard returns ``None`` when the write may proceed, else an error string
the tool returns verbatim.
Guards, in the order the tools apply them: ``_check_sensitive_path`` (hard
deny), ``_check_binary_document_write``, ``_check_protected_instruction_write``
(ALWAYS ask), ``_check_approval_required_write`` (normal gate),
``_check_cross_profile_path`` (sandbox-mirror lost-work), ``_is_internal_file_tool_content``.
``_stale_overwrite_blocker`` (write_file only, under the per-path lock) refuses a
whole-file overwrite of content this task never saw or that changed since.
é    N)ÚPath)Úget_nt_namespace_error)Ú
file_state)Úhas_binary_extensionÚhas_opaque_document_extensionÚis_pdf_pathÚis_sqlite_sidecar)Ú_expand_tildeÚ_resolve_path_for_task)Ú_has_full_write_baselineÚ_read_mtime_drifted)z/etc/z/boot/z/usr/lib/systemd/z/private/etc/z/private/var/db/z/private/var/root/z/var/run/docker.sockz/run/docker.sockÚ_hermes_config_resolvedFÚ_real_hermes_home_cachedÚreturnc                  óJ   — ddl m}  t         | «       j                  «       «      S )Nr   ©Úget_config_path)Úhermes_cli.configr   ÚstrÚresolver   s    úB/home/ubuntu/.hermes/hermes-agent/tools/file_tools_write_guards.pyÚ_config_path_resolvedr   4   s   € Ý1Ü‰Ó ×(Ñ(Ó*Ó+Ð+ó    c                  óh   — ddl m}  t        j                  j	                  t         | «       «      «      S )Nr   ©Úget_hermes_home)Úhermes_constantsr   ÚosÚpathÚrealpathr   r   s    r   Ú_hermes_home_realr!   9   s"   € Ý0Ü�7‰7×ÑœC¡Ó 1Ó2Ó3Ð3r   c            	      óâ   — t         rt        S 	 t        «       S # t        $ rM 	 ddlm}  t        t        t         | «       «      «      dz  j                  «       «      cY S # t        $ r Y Y yw xY ww xY w)zöResolved absolute path of the Hermes config file for the ACTIVE profile.

    Resolved per call so it tracks the per-turn ``HERMES_HOME`` scope (#107327);
    a test may pin it via ``_hermes_config_resolved`` + ``_hermes_config_resolved_loaded``.r   r   zconfig.yamlN)	Ú_hermes_config_resolved_loadedr   r   Ú	Exceptionr   r   r   r   r   r   s    r   Ú_get_hermes_config_resolvedr%   >   sj   € õ
 &Ü&Ð&ðÜ$Ó&Ð&øÜò ð	Ý8ÜœœS¡Ó!2Ó3Ó4°}ÑD×MÑMÓOÓPÒPøÜò 	Úð	úðús,   Ž	 ˜	A.¢8AÁA.Á	A*Á&A.Á)A*Á*A.c                  óÖ   — t         rt        S 	 t        «       S # t        $ rG 	 ddlm}  t        j                  j                  t         | «       «      «      cY S # t        $ r Y Y yw xY ww xY w)aŽ  Realpath of the authoritative Hermes home for the ACTIVE profile.

    Resolved per call so it tracks the per-turn ``HERMES_HOME`` scope (#107327);
    a test may pin it via ``_real_hermes_home_cached`` + ``_real_hermes_home_loaded``.
    Consumers exempting a whole TREE want ``_hermes_exempt_homes()``: under a named
    profile this home is ``<root>/profiles/<name>`` and the root is exempt too.r   r   N)
Ú_real_hermes_home_loadedr   r!   r$   r   r   r   r   r    r   r   s    r   Ú_get_real_hermes_homer(   W   sa   € õ  Ü'Ð'ðÜ Ó"Ð"øÜò ð	Ý8Ü—7‘7×#Ñ#¤C©Ó(9Ó$:Ó;Ò;øÜò 	Úð	úðús,   Ž	 ˜	A(¢2AÁA(Á	A$Á A(Á#A$Á$A(.c            	      ó  — t        «       } | sy	 ddlm}  || «      }|€| fS t        j
                  j                  t        t        t        |«      «      j                  j                  «      «      }|r	|| k7  r| |fS | fS # t        $ r d}Y Œnw xY w)u8  Realpaths of the Hermes home tree(s) the protected-instruction gate must stay out of:
    the ACTIVE profile's home, plus the Hermes ROOT when that home is a named profile
    (``<root>/profiles/<name>``). Exempting only the profile dir left the root's DIRECT files
    (LEDGER.md / MEMORY.md / SOUL.md / AGENTS.md ...) to the ``.hermes`` component rule, which
    gated them like a project-local ``<repo>/.hermes/config.yaml`` â€” fail-closed headless
    (#110630). They are the agent's own store, governed by their own guards, exactly like
    ``~/.hermes`` under the default profile. The root is added only when the shape really is a
    named profile (``named_profile_home``), so a coincidental ``profiles/`` dir elsewhere never
    exempts its parent; the home comes from the ACTIVE scope, never ``HERMES_HOME`` alone.© r   )Únamed_profile_homeN)
r(   r   r+   r$   r   r   r    r   r   Úparent)Úhomer+   Úprofile_homeÚroots       r   Ú_hermes_exempt_homesr0   q   s‘   € ô !Ó"€DÙØðÝ7Ù)¨$Ó/ˆð ÐØˆwˆÜ�7‰7×ÑœC¤¤S¨Ó%6Ó 7× >Ñ >× EÑ EÓFÓG€DÙ D¨D¢LˆD�$ˆ<Ð=°t°gÐ=øô ò ØŠðús   �A> Á>BÂBÚfilepathÚtask_idc                 ó\   — 	 t        t        | |«      «      S # t        t        f$ r | cY S w xY w)zOTask-resolved path string, falling back to the raw input on resolution failure.)r   r   ÚOSErrorÚ
ValueError)r1   r2   s     r   Ú_resolved_or_rawr6   ‰   s3   € ðÜÔ)¨(°GÓ<Ó=Ð=øÜ”ZÐ ò ØŠðús   ‚ —+ª+c                 óê   — t        | d¬«      }|r|S t        | |«      t        j                  j	                  t        | «      «      f}t        d„ |D «       «      rd| › d�S t        «       }|r
||v rd| › d�S y)	zHReturn an error message if the path targets a sensitive system location.ÚWrite)Úverbc              3   óZ   K  — | ]#  }|j                  t        «      xs |t        v –— Œ% y ­w©N)Ú
startswithÚ_SENSITIVE_PATH_PREFIXESÚ_SENSITIVE_EXACT_PATHS)Ú.0Úcs     r   ú	<genexpr>z(_check_sensitive_path.<locals>.<genexpr>œ   s)   è ø€ Ò
eÐUVˆ1�<‰<Ô0Ó1ÒP°QÔ:PÐ5PÓPÑ
eùs   ‚)+z,Refusing to write to sensitive system path: zD
Use the terminal tool with sudo if you need to modify system files.z)Refusing to write to Hermes config file: zz
Agent cannot modify security-sensitive configuration. Edit ~/.hermes/config.yaml directly or use 'hermes config' instead.N)r   r6   r   r   Únormpathr
   Úanyr%   )r1   r2   Únt_errÚ
candidatesÚhermes_configs        r   Ú_check_sensitive_pathrG   ‘   sš   € ô $ H°7Ô;€FÙØˆÜ" 8¨WÓ5´r·w±w×7GÑ7GÌÐV^ÓH_Ó7`Ða€JÜ
Ñ
eÐZdÔ
eÔeà:¸8¸*ð ERð Rð	Sô
 0Ó1€MÙ˜¨*Ñ4à7¸°zð BRð Rð	Sð r   >   úsoul.mdú	agents.mdú	claude.mdú.cursorrulesc                  ó  — 	 ddl m} m}  | «       } ||ddd¬«      } ||ddg ¬«      }t	        |t
        «      sd}t	        |t        «      sg }||D �cg c]  }|sŒt        |«      ‘Œ c}fS # t        $ r dg fcY S w xY wc c}w )uò   Return ``(enabled, extra_patterns)`` from ``security.protected_instruction_files`` /
    ``security.protected_instruction_extra_patterns`` (fnmatch on basename). Config read
    failures keep the gate ON â€” fail-safe for a security boundary.r   )Úload_configÚcfg_getÚsecurityÚprotected_instruction_filesT©ÚdefaultÚ$protected_instruction_extra_patterns)r   rM   rN   r$   Ú
isinstanceÚboolÚlistr   )rM   rN   ÚcfgÚenabledÚextraÚps         r   Ú_protected_instruction_configr[   »   s�   € ðß:Ù‹mˆÙ˜#˜zÐ+HÐRVÔWˆÙ˜˜ZÐ)OÐY[Ô\ˆô �gœtÔ$ØˆÜ�eœTÔ"ØˆØ UÖ0 ªa”S˜•VÒ0Ð0Ð0øô ò Ø�RˆxŠðüò 1s   ‚'A+ ÁA>ÁA>Á+A;Á:A;©rX   Úextra_patternsrX   r]   c                ó  ‡
— |�|€t        «       \  }}|syt        j                  j                  t	        | «      «      }	 t        j                  j                  t        t        | |«      «      «      }t        «       D ]+  }||k(  s#|j                  |t        j                  z   «      sŒ+ y ||fD ]œ  }t        j                  j                  |«      }|j                  «       Š
‰
t         v st#        ˆ
fd„|D «       «      r|c S |j%                  dd«      j'                  d«      j)                  d«      }	t+        |	«      dk\  sŒ‘|	d   dk(  sŒš|c S  y# t        t        t        f$ r# t        j                  j                  |«      }Y �Œw xY w)aÓ  Return a short label when ``filepath`` targets a protected instruction file, else ``None``.
    Matches BOTH the normalized input and its realpath so no symlink direction escapes.

    Matching runs on BOTH the normalized input path and its realpath so neither a symlink pointing AT a
    protected file (#41351) nor a protected name that is itself a symlink escapes the gate. ``..`` traversal
    is neutralized by normpath/realpath before the basename compare.
    Nc              3   óf   •K  — | ](  }t        j                   ‰|j                  «       «      –— Œ* y ­wr;   )ÚfnmatchÚlower)r?   ÚpatternÚ
base_lowers     €r   rA   z0_protected_instruction_reason.<locals>.<genexpr>î   s+   øè ø€ ò A\ØAH”—‘ 
¨G¯M©M«O×<ñA\ùs   ƒ.1ú\ú/é   éþÿÿÿz.hermes)r[   r   r   rB   r
   r    r   r   r4   r5   ÚRuntimeErrorr0   r<   ÚsepÚbasenamera   Ú _PROTECTED_INSTRUCTION_BASENAMESrC   ÚreplaceÚrstripÚsplitÚlen)r1   r2   rX   r]   Ú
normalizedÚresolvedÚ	real_homeÚ	candidateÚbaseÚpartsrc   s             @r   Ú_protected_instruction_reasonrv   Í   si  ø€ ð €˜.Ð0Ü"?Ó"AÑˆ�ÙØä—‘×!Ñ!¤-°Ó"9Ó:€Jð0Ü—7‘7×#Ñ#¤CÔ(>¸xÈÓ(QÓ$RÓSˆô *Ó+ò ˆ	Ø�yÒ  H×$7Ñ$7¸	ÄBÇFÁFÑ8JÕ$KÙðð ! (Ð+ò ˆ	Ü�w‰w×Ñ 	Ó*ˆØ—Z‘Z“\ˆ
ØÔ9Ñ9¼Só A\ØLZôA\ô >\àŠKð ×!Ñ! $¨Ó,×3Ñ3°CÓ8×>Ñ>¸sÓCˆÜˆu‹:˜‹?˜u R™y¨IÓ5ØÒðð øô1 ”Z¤Ð.ò 0Ü—7‘7×#Ñ# JÓ/‹ð0ús   ¿2E Å3FÆFz<requires approval but the approval subsystem is unavailable.zNrequires approval but no interactive user or gateway is present to approve it.Úreasonsc                 óê  — dj                  t        j                  | «      «      }d|› d�}d|› d�}d|› d�}|j                  d¬	«      }|j                  d
¬	«      }	 ddlm} ddlm}	 ddlm	}
 ddl
m}  |	«       }	 |j                  5  |j                  j!                  |«      }ddd«       �‡|ddg|dddœ} |
|||d¬«      }|j!                  d«      r|j                  d¬	«      S |j!                  d«      r|j                  d|d   › d�¬	«      S |j!                  d«      |j!                  d«       }}n^	 ddlm}  |«       }|€|j                  t&        ¬	«      S  |||dd|¬«      }|dk(  r!|j                  dt)        |dd «      › d�¬	«      S |d!k(  }|s|d"v ry|r|S |S # t        $ r |j                  t        ¬	«      cY S w xY w# 1 sw Y   �Œ#xY w# t        $ r d}Y �Œ4w xY w# t        $ r d}Y Œ®w xY w)#a,  Ask the human to approve a write to protected instruction file(s); ``None`` when approved.

    Deliberately NOT routed through ``_run_approval_gate`` (honors --yolo and
    allowlists): this gate is one-operation approval EVERY time, no persisted
    scope, fail-closed without a human channel.
    ú, z.Write to protected agent-instruction file(s): zf. These files steer future agent behavior; approval is always required (not bypassed by auto-approve).ú
<write to Ú>z7BLOCKED: write to protected agent-instruction file(s) (z‹) {why} The user has NOT consented to this write. Do NOT retry it or attempt the same edit via another path (terminal, execute_code, etc.).zJapproval prompt timed out without a user response. Silence is not consent.©Úwhyzwas denied by the user.r   N)Úget_current_session_key)Ú_await_gateway_decision)Úprompt_dangerous_approvalÚprotected_instruction_fileF)ÚcommandÚpattern_keyÚpattern_keysÚdescriptionÚallow_permanentÚallow_sessionÚgateway)ÚsurfaceÚnotify_failedzBrequires approval but the approval request could not be delivered.Ú	cancelledz1approval was withdrawn before the user answered (z).Úchoicerq   )Ú_get_approval_callback)r†   r‡   Úapproval_callbackz<approval prompt could not be delivered or was not answered (Úcausez	no answerÚtimeout>   ÚonceÚalwaysÚsession)ÚjoinÚdictÚfromkeysÚformatÚtools.approvalÚapprovalÚtools.approval_contextr~   Útools.approval_gateway_waitr   Útools.approval_promptr€   r$   Ú_APPROVAL_UNAVAILABLEÚ_lockÚ_gateway_notify_cbsÚgetÚtools.terminal_toolr�   Ú	_NO_HUMANÚgetattr)rw   r2   Útargetsr…   ÚdisplayÚblockedÚ	timed_outÚdeniedÚ	_approvalr~   r   r€   Úsession_keyÚ	notify_cbÚapproval_dataÚdecisionrŒ   Útimedr�   Úcallbacks                       r   Ú'_request_protected_instruction_approvalr°   þ   sc  € ð �i‰iœŸ™ gÓ.Ó/€Gà
8¸¸	ð B3ð 	3ð ð ˜7˜) 1Ð%€Gà
AÀ'Àð Kð 	ð ð
 —‘Ð#o�Óp€IØ�^‰^Ð 9ˆ^Ó:€Fð9Ý*ÝBÝGÝCñ *Ó+€KðØ�_‰_ñ 	GØ!×5Ñ5×9Ñ9¸+ÓFˆI÷	Gð
 ÐàØ7Ø9Ð:Ø&Ø$Ø"ñ$ˆñ +¨;¸	À=ÐZcÔdˆØ�<‰<˜Ô(Ø—>‘>Ð&j�>ÓkÐkØ�<‰<˜Ô$Ø—>‘>Ð(YÐZbÐcnÑZoÐYpÐprÐ&s�>ÓtÐtØ Ÿ™ XÓ.°H·L±LÀÓ4LÐ0L�‰ð	ÝBÙ-Ó/ˆHð Ðð —>‘>¤i�>Ó0Ð0Ù*Ø�[°%ÀuÐ`hôjˆà�[Ò Ø—>‘>ð '*Ü*1°&¸'À;Ó*OÐ)PÐPRð'T�>ó Uð Uà˜)Ñ#ˆá�VÐ<Ñ<ØÙˆ9Ð) 6Ð)øô] ò 9Ø�~‰~Ô"7ˆ~Ó8Ò8ð9ú÷	Gñ 	Gûäò Ø‹	ðûô, ò 	ØŠHð	úsN   ÁF  Á<G ÂGÂ$G Ä6G$ Æ GÇGÇGÇ
G ÇG!Ç G!Ç$G2Ç1G2Úpathsc                 óˆ   ‡‡‡— t        «       \  ŠŠ‰syˆˆˆfd„| D «       D �cg c]  }|r|‘Œ }}|syt        |‰«      S c c}w )z�Gate a write/patch touching protected instruction files. ONE protected file gates
    the ENTIRE multi-file patch (one prompt, all-or-nothing).Nc              3   ó<   •K  — | ]  }t        |‰‰‰¬ «      –— Œ y­w)r\   N)rv   )r?   rZ   rX   rY   r2   s     €€€r   rA   z5_check_protected_instruction_write.<locals>.<genexpr>O  s(   øè ø€ ò +Ø ô 9¸¸GÈWÐej×kÐkñ +ùs   ƒ)r[   r°   )r±   r2   Úrrw   rX   rY   s    `  @@r   Ú"_check_protected_instruction_writerµ   I  sX   ú€ ô 3Ó4�N€GˆUÙØõ+Ø$)ô+ö 1�QÙ./ò ð 1€Gð 1áØÜ2°7¸GÓDÐDùò	1s   £?c                 ó6  — 	 ddl m} | D �cg c]  } ||«      sŒ|‘Œ }}|sydj                  t        j                  |«      «      }d|› d�}d|› d�}	 ddlm} |j                  d
|d|› d�|j                  d¬	«      |j                  d¬	«      d
d|j                  t        ¬	«      ¬«      }	|	j                  d«      ry|	j                  d«      xs |j                  d¬	«      S # t        $ r Y yw xY wc c}w # t        $ r |j                  t        ¬	«      cY S w xY w)záGate a write/patch touching an approval-required path (``~/.ssh/config`` can steer
    execution via ``ProxyCommand``). Routine gate: once/session/always, honors --yolo,
    fail-closed without an interactive/gateway channel.r   )Úis_write_approval_requiredNry   z$Write to SSH client config file(s): zs. The SSH config can carry ProxyCommand / Match exec directives that run commands, so writes require your approval.z&BLOCKED: write to SSH config file(s) (zf) {why} Do NOT retry it via another path (terminal, execute_code) without the user's explicit consent.r|   Ússh_config_writerz   r{   z2requires approval but this cron session denies it.zÂrequires approval but single-query (-q) sessions run without a user present to approve it. To allow flagged actions in single-query mode, set approvals.single_query_mode: approve in config.yaml.T)rƒ   r…   Údisplay_targetÚcron_deny_messageÚsingle_query_deny_messageÚautoapprove_log_prefixÚfail_closed_when_no_humanÚno_human_block_messageÚapprovedÚmessagezwas denied.)Úagent.file_safetyr·   r$   r”   r•   r–   r˜   r™   r—   r�   Ú_run_approval_gater¢   r    )
r±   r2   r·   rZ   r¤   Údisplay_targetsr…   r¦   r©   Úresults
             r   Ú_check_approval_required_writerÅ   V  sP  € ðÝ@ð  ÖA�QÑ#=¸aÕ#@ŠqÐA€GÐAÙØà—i‘i¤§¡¨gÓ 6Ó7€Oà
.¨Ð.?ð @9ð 	9ð ð
 1°Ð0Að B/ð 	/ð ð
9Ý*ð ×)Ñ)Ø&ØØ# OÐ#4°AÐ6Ø!Ÿ.™.Ð-a˜.ÓbØ")§.¡.ð*ð #1ó #+ð
  2Ø"&Ø&Ÿ~™~´)˜~Ó<ð *ó >€Fð ‡z�z�*ÔØØ�:‰:�iÓ ÒE G§N¡N°} NÓ$EÐEøôK ò Ùðüò Bøô  ò 9Ø�~‰~Ô"7ˆ~Ó8Ò8ð9ús-   ‚C" ŒC1šC1ÁC6 Ã"	C.Ã-C.Ã6DÄDc                 ó�  — 	 ddl m}m}m}m}  || «      }|5  |j                  |«      xs |j                  | «      }ddd«       �7|j                  j                  dk(  xr t        t        |dd«      «      }|rdS dS  |«       }|j                  d«      d	k(  r|j                  d
d«      ryy# 1 sw Y   ŒqxY w# t        $ r Y yw xY w)zPReturn the container-side Hermes mirror prefix for persistent Docker file tools.r   )Ú_active_environmentsÚ	_env_lockÚ_get_env_configÚ_resolve_container_task_idNÚDockerEnvironmentÚ_persistentFz/root/.hermesÚenv_typeÚdockerÚcontainer_persistentT)r¡   rÇ   rÈ   rÉ   rÊ   r    Ú	__class__Ú__name__rU   r£   r$   )	r2   rÇ   rÈ   rÉ   rÊ   Úcontainer_keyÚenvÚpersistent_dockerÚconfigs	            r   Ú%_get_container_mirror_prefix_for_taskrÖ   „  sß   € ð÷	Zó 	Zá2°7Ó;ˆØñ 	_Ø&×*Ñ*¨=Ó9Ò^Ð=Q×=UÑ=UÐV]Ó=^ˆC÷	_àˆ?Ø!$§¡×!7Ñ!7Ð;NÑ!Nò "NÜ%)¬'°#°}ÀeÓ*LÓ%Mð á&7�?ÐA¸TÐAÙ Ó"ˆð ‡z�z�*Ó Ò)¨f¯j©jÐ9OÐQUÔ.VØØ÷	_ð 	_ûô ò Ùðús3   ‚B9 ˜%B-½>B9 Á<B9 Á>B9 Â-B6Â2B9 Â9	CÃCc                 óˆ   — 	 ddl m}m} t	        | |«      } ||«      }|�|S  ||t        |«      ¬«      S # t        $ r Y yw xY w)u	  Soft-guard: warn when ``filepath`` lands on a host-side or Docker sandbox MIRROR of
    Hermes state (a write the host never reads). Not profile isolation â€” that guard was
    removed; ``cross_profile=True`` keeps bypassing this one for replay compat. Fails open.r   )Úget_container_mirror_warningÚget_sandbox_mirror_warningN)Úmirror_prefix)rÁ   rØ   rÙ   r$   r6   rÖ   )r1   r2   rØ   rÙ   rq   Úwarnings         r   Ú_check_cross_profile_pathrÜ   ˜  sU   € ðß^ô   ¨'Ó2€HÙ(¨Ó2€GØÐØˆÙ'¨Ô@eÐfmÓ@nÔoÐoøô ò Ùðús   ‚5 µ	AÁ Ac                 ó¼  — t         j                  j                  | «      d   j                  «       }t	        | «      r	d| › d|› d�S t        | «      r	d| › d|› d�S t        | «      }|st        | «      r:	 t        t        | |«      «      }	 |j                  «       r|rd| › d�S d	| › d|› d
�S 	 yy# t        $ r t        t        | «      «      }Y ŒDw xY w# t        $ r Y yw xY w)a  Reject text-tool writes that would corrupt a binary document (read_file showed
    EXTRACTED text, so the model may write it back). Opaque document formats and
    SQLite sidecars (-wal/-shm/-journal) are always rejected; .pdf and every other
    BINARY_EXTENSIONS suffix only when OVERWRITING an existing file (raw PDF syntax
    is text-authorable and text fixtures named ``*.db`` exist).

    ``read_file`` auto-extracts .docx/.xlsx/.pptx (and PDF, via anydoc) to readable text, so the model
    plausibly believes it holds the file's contents and tries to write the edited text back with
    write_file/patch. A plain-text write can never produce a valid OOXML/OLE/ODF container, so that write
    silently destroys the document (port of nearai/ironclaw#7109).
    é   z1Refusing to write plain text to binary document 'z' (a  ). A text write cannot produce a valid document container and would corrupt the file (read_file showed you EXTRACTED text, not the real bytes). Use the docx/xlsx/powerpoint skills or a library like python-docx/openpyxl/python-pptx via the terminal to create or edit this document.z7Refusing to write plain text to binary SQLite sidecar 'zÔ). A -wal/-shm/-journal file holds raw database pages that SQLite reads on the next open; text there corrupts the database. Use the sqlite3 CLI or a SQLite library via the terminal to modify the database instead.z$Refusing to overwrite existing PDF 'uå   ' with plain text. read_file showed you EXTRACTED text, not the real bytes â€” writing text back would destroy the document. Use the pdf skill or a PDF library via the terminal to modify it. (Creating a NEW .pdf file is allowed.)z,Refusing to overwrite existing binary file 'u1  ) with plain text â€” read_file showed you extracted or mojibake text, not the real bytes, and writing text back would destroy the file. Use a binary-aware tool via the terminal to modify it (for SQLite databases, the sqlite3 CLI or a SQLite library). (Creating a NEW file with this extension is allowed.)N)r   r   Úsplitextra   r   r	   r   r   r   r   r$   r
   Úis_filer4   )r1   r2   ÚextÚpdfrq   s        r   Ú_check_binary_document_writerã   §  s1  € ô �'‰'×
Ñ
˜8Ó
$ QÑ
'×
-Ñ
-Ó
/€CÜ$ XÔ.à?À¸zÈÈSÈEð Rð ð	ô ˜Ô"àEÀhÀZÈsÐSVÐRWð X ð  ð	!ô �hÓ
€CÙ
Ô" 8Ô,ð	5ÜÔ2°8¸WÓEÓFˆHð	Ø×ÑÔ!Ùà>¸x¸jð I'ð 'ð(ð CÀ8À*ÈCÐPSÈuð ULð LðMð "ñ" øô) ò 	5ÜœM¨(Ó3Ó4ŠHð	5ûô$ ò 	ØØð	ús*   Á2B, ÂC Â C Â,CÃCÃ	CÃCuœ   File unchanged since last read. The content from the earlier read_file result in this conversation is still current â€” refer to that instead of re-reading.rq   c                 ó
  — t        j                  «       ry|rt        j                  ||«      nd}|r|S t        | |«      r| › d�S |rt	        ||«      ry	 t        |«      j                  «       }|sy|› d�S # t        $ r Y yw xY w)a  Reason write_file must NOT replace the existing file, else ``None``.

    Refuses BEFORE any disk mutation (the pre-#65604 warning arrived after the
    clobber): a sibling/external/partial-read staleness finding, or an existing
    file with no full-content baseline for this task (never read in full, read
    redacted, only patched). Net-new files, files this task fully read (in one
    page or by paging contiguously to the last line) or wrote, unresolvable
    paths and the file-state kill switch all let the write proceed.
    Nzj was modified since you last read it (external edit or concurrent agent). Re-read the file before writing.u"   exists but this task has not seen its full current content (never read, only patched, or only a redacted/partial view). Read the file â€” every page of it, if it needs offset/limit â€” or use patch for a targeted edit; a stale conversation copy must not overwrite the current disk content.)r   Úguard_disabledÚcheck_staler   r   r   Úexistsr4   )r1   rq   r2   Ústalerç   s        r   Ú_stale_overwrite_blockerré   ì  s«   € ô × Ñ Ô"ØÙ9AŒJ×"Ñ" 7¨HÔ5Àt€EÙØˆÜ˜8 WÔ-àˆjð Bð Bð	Cñ Ô/°¸'ÔBØðÜ�h“×&Ñ&Ó(ˆñ Øàˆ*ð ð 	ðøô	 ò Ùðús   ÁA6 Á6	BÂBÚreasonc                 ó.   — d| › d|› d�d| dœ}|r||d<   |S )ztModel-facing refusal payload for write_file; ``stale_write_blocked`` lets
    callers tell it apart from I/O errors.zRefusing to overwrite z: zû The file was NOT modified. Reload the current contents with read_file (every page, for a file that needs offset/limit), merge the requested change, then call write_file again. For small edits, prefer patch so existing unrelated changes are preserved.T)ÚerrorÚstale_write_blockedr   Úresolved_pathr*   )r1   rê   rq   rÄ   s       r   Ú_stale_write_refusalrï     s>   € ð
 % X J¨b°°ð 98ð 8ð
  $Øñ	€Fñ Ø"*ˆˆÑØ€Mr   Úcontentc                 ó®   — t        | t        «      sy| j                  «       }t        |«      xr( t        |v xr t        |«      dt        t        «      z  k  S )u¶   True when content is the read_file dedup status message, verbatim or lightly framed
    (contains the full message and is <=2x its length â€” a real file quoting it would be longer).Frf   )rT   r   ÚstriprU   Ú_READ_DEDUP_STATUS_MESSAGEro   )rð   Ústrippeds     r   Ú_is_internal_file_status_textrõ   !  sQ   € ô �gœsÔ#ØØ�}‰}‹€HÜ�‹>ò >Ô8¸HÐDò >ÜˆH‹˜œSÔ!;Ó<Ñ<Ñ<ð>r   c           	      ó  — t        | t        «      sy| j                  «       D �cg c]  }|j                  «       sŒ|‘Œ }}t	        |«      dk  ryg }|D ]S  }|j                  «       j                  d«      \  }}}|sŒ)|j                  «       sŒ:|j                  t        |«      «       ŒU t	        |«      dk  st	        |«      t	        |«      z  dk  ryt        d„ t        ||dd «      D «       «      }|t	        |«      dz
  k\  S c c}w )z¤True for content dominated by read_file's ``LINE_NUM|CONTENT`` display (>=60% of
    non-empty lines are consecutive numbered lines; a lone ``1|value`` is allowed).Frf   Ú|g333333ã?c              3   ó8   K  — | ]  \  }}||d z   k(  sŒd –— Œ y­w)rÞ   Nr*   )r?   ÚprevÚcurrents      r   rA   z>_looks_like_read_file_line_numbered_content.<locals>.<genexpr>:  s#   è ø€ Òf¡- $¨ÐRYÐ]aÐdeÑ]eÓReœAÑfùs   ‚“rÞ   N)rT   r   Ú
splitlinesrò   ro   ÚlstripÚ	partitionÚisdigitÚappendÚintÚsumÚzip)rð   ÚlineÚlinesÚnumberedÚprefixri   Ú_restÚconsecutive_pairss           r   Ú+_looks_like_read_file_line_numbered_contentr	  +  sí   € ô �gœsÔ#ØØ%×0Ñ0Ó2ÖC�d°d·j±jµlŠTÐC€EÐCÜ
ˆ5ƒz�A‚~ØØ€HØò )ˆØ!Ÿ[™[›]×4Ñ4°SÓ9Ñˆ��UÚ�6—>‘>Õ#Ø�O‰OœC ›KÕ(ð)ô ˆ8ƒ}�qÒœC ›M¬C°«JÑ6¸Ò<ØÜÑf´3°xÀÈ!È"ÀÓ3NÔfÓfÐØ¤ H£°Ñ 1Ñ1Ð1ùò Ds
   ¤DºDc                 ó2   — t        | «      xs t        | «      S )zLReturn True when content is file-tool display text, not intended file bytes.)rõ   r	  )rð   s    r   Ú_is_internal_file_tool_contentr  >  s   € ä(¨Ó1ÒiÔ5`ÐahÓ5iÐir   rQ   r;   )9Ú__doc__r`   r   Úpathlibr   rÁ   r   Útoolsr   Útools.binary_extensionsr   r   r   r	   Útools.file_tools_pathsr
   r   Útools.file_tools_read_trackingr   r   r=   r>   r   r   Ú__annotations__r#   r   r'   r   r!   r%   r(   Útupler0   r6   rG   Ú	frozensetrk   rU   rV   r[   rv   r�   r¢   r°   rµ   rÅ   rÖ   rÜ   rã   ró   ré   r•   rï   rõ   r	  r  r*   r   r   ú<module>r     sç  ðò
ó Û 	Ý å 4Ý ÷ó ÷ Iß Xð
.Ð ð 1Ð2DÐEÐ ð '+Ð ˜˜t™Ó *Ø!&Ð Ø'+Ð ˜# ™*Ó +Ø Ð ð,˜só ,ð
4˜3ó 4ð
 S¨4¡Zó ð2˜s T™zó ð4>˜e C¨ H™oó >ð0˜sð ¨Sð °Só ñ Cð °#ð ÀcÈDÁjó ñL $-ò .9ó $:Ð  ð1 u¨T°4¸±9¨_Ñ'=ó 1ð$*Ø<@ØEIò*¨Cð *¸#ð *Ø.2°T©kð*à26°s±)¸dÑ2Bð*àNQÐTXÉjó*ðZ WÐ Ø\€	ñH*°T¸#±Yð H*Èð H*Ð]`ÐcgÑ]gó H*ñV
E¨d°3©ið 
EÀ#ð 
EÐVYÐ\`ÑV`ó 
Eñ+F¨$¨s©)ð +F¸cð +FÐRUÐX\ÑR\ó +Fñ\°3ð ÀsÈTÁzó ñ(p¨ð p°cð pÈ#ÐPTÉ*ó pñ;¨3ð ;¸ð ;ÈSÐSWÉZó ;ð@=ð ð  sð  °c¸D±jð  È3ð  ÐSVÐY]ÑS]ó  ñF 3ð °ð ¸sÀT¹zð ÐUYó ð$>¨3ð >°4ó >ð2¸ð 2Àó 2ð&j¨Cð j°Dô jr   