
    \jJ                    Z    d Z ddlmZ ddlZddlmZ  ej        e          Zdd	Z	ddZ
dS )u  Best-effort accessors for the single-writer stream fence (#65991).

The fence itself lives on ``AIAgent`` (``_claim_stream_writer`` /
``_stream_writer_is_current`` in ``run_agent.py``), but the streaming code paths
that use it live in *other* modules — ``chat_completion_helpers`` (chat /
anthropic / bedrock) and ``codex_runtime`` (codex responses). Calling the fence
directly as ``agent._claim_stream_writer()`` from those modules makes them
hard-depend on the method being present on whatever object is passed in as
``agent``.

That coupling is a latent crash: a partially-updated checkout (the streaming
helper module newer than ``run_agent``), a hot-reloaded gateway, a duck-typed
agent, or a test double without the method turns an *additive* safety net into a
fatal ``AttributeError`` that aborts the whole turn. A cron job died exactly
this way with ``'AIAgent' object has no attribute '_claim_stream_writer'``.

The fence is only ever allowed to drop a *provably* superseded stream — never
the sole legitimate writer. So when the guard is unavailable (or raises), the
correct degradation is "no fence": keep streaming. These helpers make the
claim/check best-effort to guarantee that.
    )annotationsN)Anyagentr   returnintc                    t          | dd          }t          |          rD	 t           |                      S # t          $ r t                              dd           Y nw xY wdS )a  Claim the delta sink for the calling stream attempt, best-effort.

    Returns the agent's monotonic writer token when the fence is available, or
    ``0`` when the agent doesn't expose it (or the claim raised). A ``0`` token
    pairs with :func:`stream_writer_is_current` always returning ``True``, so a
    guard-less agent is simply never fenced instead of crashing the turn.
    _claim_stream_writerNz7stream single-writer: claim failed; proceeding unfencedTexc_infor   )getattrcallabler   	Exceptionloggerdebug)r   claims     @/home/thesage/.hermes/hermes-agent/agent/stream_single_writer.pyclaim_stream_writerr      s     E1488E 	uuww<< 	 	 	LLI      	
 1s   9 &A"!A"tokenboolc                    |sdS t          | dd          }t          |          rE	 t           ||                    S # t          $ r t                              dd           Y nw xY wdS )ad  True when ``token`` is still the active writer, best-effort.

    A falsy token (from a claim that no-oped) or an agent without the fence
    means we cannot prove supersession, so the stream is treated as current and
    never fenced. This preserves the single-writer invariant's one-way promise:
    only a demonstrably stale writer is ever stopped.
    T_stream_writer_is_currentNzBstream single-writer: is_current check failed; treating as currentr
   )r   r   r   r   r   r   )r   r   
is_currents      r   stream_writer_is_currentr   3   s      t ;TBBJ
 	

5))*** 	 	 	LLT      	
 4s   > &A'&A')r   r   r   r   )r   r   r   r   r   r   )__doc__
__future__r   loggingtypingr   	getLogger__name__r   r   r        r   <module>r"      s    , # " " " " "       		8	$	$   (     r!   