
    epjF                        U d Z ddlmZ ddlZddlmZmZmZ dZde	d<   dZ
d%dZd&dZ	 d'd(dZd)dZddedd*d"Zddd#d+d$ZdS ),u  Gateway runtime-metadata footer.

Renders a compact footer showing runtime state (model, context %, cwd) and
appends it to the FINAL message of an agent turn when enabled.  Off by default
to keep replies minimal.

Config (``~/.hermes/config.yaml``)::

    display:
      runtime_footer:
        enabled: true                       # off by default
        fields: [model, context_pct, cwd]   # order shown; drop any to hide

Available fields:
    model        — bare model id, vendor prefix dropped (``gpt-5.4``)
    context_pct  — last-call context occupancy as a percent (``5%``)
    latency      — wall-clock duration of the turn (``22s``, ``1m05s``)
    cwd          — home-relative working dir (``~``)

``latency`` is opt-in: it is NOT in the default field set, so a footer whose
``fields`` are unset renders exactly as before.

Per-platform overrides live under ``display.platforms.<platform>.runtime_footer``.
Users can toggle the global setting with ``/footer on|off`` from both the CLI
and any gateway platform.

The footer is appended to the final response text in ``gateway/run.py`` right
before returning the response to the adapter send path — so it only lands on
the final message a user sees, not on tool-progress updates or streaming
partials.  When streaming is on and the final text has already been delivered
piecemeal, the footer is sent as a separate trailing message via
``send_trailing_footer()``.
    )annotationsN)AnyIterableOptional)modelcontext_pctcwdztuple[str, ...]_DEFAULT_FIELDSu    · r	   strreturnc                8   | sdS 	 t           j                            d          }t           j                            |           }|rB||k    s"|                    |t           j        z             rd|t          |          d         z   S |S # t          $ r | cY S w xY w)zGReturn *cwd* with ``$HOME`` collapsed to ``~``.  Empty string if unset. ~N)ospath
expanduserabspath
startswithseplen	Exception)r	   homeps      </home/thesage/.hermes/hermes-agent/gateway/runtime_footer.py_home_relative_cwdr   ,   s     rw!!#&&GOOC   	'Q$YY!,,tbf}"="=Y3t99::&&   


s   BB
 B
 
BBr   Optional[str]c                B    | sdS |                      dd          d         S )uM   Drop ``vendor/`` prefix for readability (``openai/gpt-5.4`` → ``gpt-5.4``).r   /   )rsplit)r   s    r   _model_shortr"   :   s(     r<<Q##    user_configdict[str, Any] | Noneplatform_key
str | Nonedict[str, Any]c                L   dt          t                    d}| pi                     d          pi }|                    d          }t          |t                    rnd|v r%t          |                    d                    |d<   t          |                    d          t                     r|d         rd |d         D             |d<   |r|                    d          pi }|                    |          }t          |t                    r|                    d          }t          |t                    rnd|v r%t          |                    d                    |d<   t          |                    d          t                     r|d         rd	 |d         D             |d<   |S )
zResolve effective runtime-footer config for *platform_key*.

    Merge order (later wins):
        1. Built-in defaults (enabled=False)
        2. ``display.runtime_footer``
        3. ``display.platforms.<platform_key>.runtime_footer``
    F)enabledfieldsdisplayruntime_footerr*   r+   c                ,    g | ]}t          |          S  r   .0fs     r   
<listcomp>z)resolve_footer_config.<locals>.<listcomp>T   s    !G!G!GQ#a&&!G!G!Gr#   	platformsc                ,    g | ]}t          |          S r/   r0   r1   s     r   r4   z)resolve_footer_config.<locals>.<listcomp>_   s    )P)P)PQ#a&&)P)P)Pr#   )listr
   get
isinstancedictbool)r$   r&   resolvedcfg
global_cfgr5   plat_cfgplat_footers           r   resolve_footer_configrA   A   s    !D,A,ABBH"
!
!)
,
,
2C)**J*d## H
"""&z~~i'@'@"A"AHYjnnX..55 	H*X:N 	H!G!G*X2F!G!G!GHX 	QGGK((.B	==..h%% 	Q",,'788K+t,, Q++*.{y/I/I*J*JHY'kooh77>> Q;xCX Q)P)P+h:O)P)P)PHX&Or#   secondsfloatc                    | dk     rdS t          t          |                     }|dk     r| dS t          |d          \  }}| d|ddS )z6Humanize a turn duration: ``<1s``, ``22s``, ``1m05s``.r   z<1s<   sm02d)introunddivmod)rB   totalrG   secs       r   _format_latencyrN   d   se    {{ugErzz{{{E2FAs#r#   )r	   turn_secondsr+   context_tokensrI   context_lengthOptional[int]rO   Optional[float]r+   Iterable[str]c                `   g }|D ]}|dk    r't          |           }|r|                    |           0|dk    rX|rU|dk    rO|dk    rIt          dt          dt	          ||z  dz                                }	|                    |	 d           |dk    r+|(|dk    r"|                    t          |                     |dk    rFt          |pt          j        	                    d	d
                    }
|
r|                    |
           |sd
S t                              |          S )u   Render the footer line, or return "" if no fields have data.

    Fields are skipped silently when their underlying data is missing — a
    partially-populated footer is better than a line with ``?%`` or empty slots.
    r   r   r   d   %latencyNr	   TERMINAL_CWDr   )r"   appendmaxminrJ   rN   r   r   environr8   _SEPjoin)r   rP   rQ   r	   rO   r+   partsfieldrG   pctrels              r   format_runtime_footerrd   o   sS    E " "GU##A  Qm## (.1"4"419L9L!Se^n-LPS,S&T&TUUVVYYY'''i 'LA,=,=_\::;;;e^^$S%NBJNN>2,N,NOOC "S!!!  r99Ur#   )r	   rO   c           
         t          | |          }|                    d          sdS t          ||||||                    d          pt                    S )a  Top-level entry point used by gateway/run.py.

    Returns the footer text (empty string when disabled or no data).  Callers
    append this to the final response themselves, preserving a single blank
    line of separation.

    ``turn_seconds`` is the wall-clock duration of the agent run, measured by
    the caller with ``time.monotonic()``.  Callers that don't measure it leave
    it ``None`` and the ``latency`` field is skipped.
    r*   r   r+   )r   rP   rQ   r	   rO   r+   )rA   r8   rd   r
   )r$   r&   r   rP   rQ   r	   rO   r=   s           r   build_footer_linerf      sf    (  \
:
:C779 r %%!wwx  3O   r#   )r	   r   r   r   )r   r   r   r   )N)r$   r%   r&   r'   r   r(   )rB   rC   r   r   )r   r   rP   rI   rQ   rR   r	   r   rO   rS   r+   rT   r   r   )r$   r%   r&   r'   r   r   rP   rI   rQ   rR   r	   r   rO   rS   r   r   )__doc__
__future__r   r   typingr   r   r   r
   __annotations__r^   r   r"   rA   rN   rd   rf   r/   r#   r   <module>rk      s       D # " " " " " 				 * * * * * * * * * *#B B B B B   $ $ $ $  $         F     $(+% % % % % %^ $(       r#   