
    #jD                     L   U d Z ddlZddlZddlZddlmZmZ ddlmZ da	e
ed<    e            Z ede          Zeeez           ed<   d	eez  dz  d
efdZded
dfdZd
edz  fdZd
efdZd
efdZd
efdZded
edz  fdZd/dedz  d
efdZd/dedz  d
efdZd/dedz  d
efdZdeded
efdZd
efdZd	ed
dfdZd
edz  fdZdZded
edz  fd Z d
e
fd!Z!da"e
dz  ed"<   d
e
fd#Z#da$e
dz  ed$<   d
e
fd%Z%d
efd&Z&d
efd'Z'd
efd(Z(d0d)e
d
dfd*Z)d+Z*d,Z+d-Z,e, d.Z-dS )1u   Shared constants for Hermes Agent.

Import-safe module with no dependencies — can be imported from anywhere
without risk of circular imports.
    N)
ContextVarToken)PathF_profile_fallback_warned_HERMES_HOME_OVERRIDE)defaultpathreturnc                 f    | t           nt          |           }t                              |          S )zSet a context-local Hermes home override and return its reset token.

    This is for in-process, per-task scoping.  It deliberately does not mutate
    ``os.environ`` because that is shared by every thread in the process.
    )_UNSETstrr   set)r	   values     5/home/ubuntu/.hermes/hermes-agent/hermes_constants.pyset_hermes_home_overrider      s+     %)L&&c$iiE $$U+++    tokenc                 :    t                               |            dS )z8Restore the previous context-local Hermes home override.N)r   reset)r   s    r   reset_hermes_home_overrider      s    &&&&&r   c                  l    t                                           } | t          u s| sdS t          |           S )z=Return the active context-local Hermes home override, if any.N)r   getr   r   )overrides    r   get_hermes_home_overrider   $   s4    $((**H6tx==r   c                     t           j        dk    rat          j                            dd                                          } | rt          |           nt          j                    dz  dz  }|dz  S t          j                    dz  S )z4Return the platform-native default Hermes home path.win32LOCALAPPDATA AppDataLocalhermesz.hermes)sysplatformosenvironr   stripr   home)local_appdatabases     r   !_get_platform_default_hermes_homer*   ,   ss    
|w
~r::@@BB&3ZtM"""y9PSZ9Zh9;;""r   c                     t                      } | rt          |           S t          j                            dd                                          }|rt          |          S t          s	 t                      }|dz  }|                                r&|	                                                                nd}n# t          t          f$ r d}Y nw xY w|rf|dk    r`dad|d| d|d	}	 t          j                            |d
z              t          j                                         n# t           $ r Y nw xY wt                      S )u  Return the Hermes home directory (default: platform-native path).

    Reads HERMES_HOME env var, falls back to the platform-native default.
    This is the single source of truth — all other copies should import this.

    When ``HERMES_HOME`` is unset but an ``active_profile`` file indicates
    a non-default profile is active, logs a loud one-shot warning to
    ``errors.log`` so cross-profile data corruption is diagnosable instead
    of silent.  Behavior is unchanged otherwise — we still return
    the platform-native default — because raising here would brick 30+ module-level
    callers that import this at load time.  Subprocess spawners are
    expected to propagate ``HERMES_HOME`` explicitly (see the systemd
    template in ``hermes_cli/gateway.py`` and the kanban dispatcher in
    ``hermes_cli/kanban_db.py``).  See https://github.com/NousResearch/hermes-agent/issues/18594.
    HERMES_HOMEr   active_profiler   TzB[HERMES_HOME fallback] HERMES_HOME is unset but active profile is z. Falling back to u'   , which is the DEFAULT profile — not z. Any data this process writes will land in the wrong profile. The subprocess spawner should pass HERMES_HOME explicitly (see issue #18594).
)r   r   r$   r%   r   r&   r   r*   exists	read_textUnicodeDecodeErrorOSErrorr"   stderrwriteflush	Exception)r   valfallback_homeactive_pathactivemsgs         r   get_hermes_homer<   5   s     ())H H~~
*..
+
+
1
1
3
3C
 Cyy
 $ 	=??M'*::K8C8J8J8L8LT[**,,22444RTFF"G, 	 	 	FFF	 	f	))'+$'$' ':G' '28' ' ' 
  t,,,
  """"    -...s&   +AB; ;CC+A D, ,
D98D9c                  X   t                      } t          j                            dd          }|s| S t	          |          }	 |                                                    |                                            | S # t          $ r Y nw xY w|j        j	        dk    r|j        j        S |S )u  Return the root Hermes directory for profile-level operations.

    In standard deployments this is the platform-native Hermes home
    (``~/.hermes`` on POSIX, ``%LOCALAPPDATA%\hermes`` on native Windows).

    In Docker or custom deployments where ``HERMES_HOME`` points outside
    ``~/.hermes`` (e.g. ``/opt/data``), returns ``HERMES_HOME`` directly
    — that IS the root.

    In profile mode where ``HERMES_HOME`` is ``<root>/profiles/<name>``,
    returns ``<root>`` so that ``profile list`` can see all profiles.
    Works both for standard (``~/.hermes/profiles/coder``) and Docker
    (``/opt/data/profiles/coder``) layouts.

    Import-safe — no dependencies beyond stdlib.
    r,   r   profiles)
r*   r$   r%   r   r   resolverelative_to
ValueErrorparentname)native_homeenv_homeenv_paths      r   get_default_hermes_rootrG   o   s    " 455Kz~~mR00H H~~H&&{':':'<'<===    z))%% Os   :A> >
B
BrC   c                     g }dD ]=}t          j        |          }|r%|                    t          |          | z             >|D ]}|                                r|c S dS )zReturn an installed data-files directory if one exists.

    Used to discover bundled skills/optional-skills when Hermes is installed
    from a wheel that emitted them via setuptools data_files.
    )datapurelibplatlibN)	sysconfigget_pathappendr   r/   )rC   
candidatesschemeraw	candidates        r   _get_packaged_data_dirrS      s     J0 0 0 (( 	0d3ii$.///  	 		4r   r   c                     t          j        dd                                          }|rt          |          S t	          d          }||S | | S t                      dz  S )zReturn the optional-skills directory, honoring package-manager wrappers.

    Packaged installs may ship ``optional-skills`` outside the Python package
    tree and expose it via ``HERMES_OPTIONAL_SKILLS``.
    HERMES_OPTIONAL_SKILLSr   zoptional-skillsr$   getenvr&   r   rS   r<   r   r   packageds      r   get_optional_skills_dirrZ      sl     y1266<<>>H H~~%&788H000r   c                     t          j        dd                                          }|rt          |          S t	          d          }||S | | S t                      dz  S )ao  Return the optional-mcps directory, honoring package-manager wrappers.

    Mirrors :func:`get_optional_skills_dir` for the MCP catalog (Nous-approved
    Model Context Protocol servers shipped with the repo but disabled by
    default). Packaged installs may ship ``optional-mcps`` outside the Python
    package tree and expose it via ``HERMES_OPTIONAL_MCPS``.
    HERMES_OPTIONAL_MCPSr   zoptional-mcpsrV   rX   s      r   get_optional_mcps_dirr]      sj     y/44::<<H H~~%o66H..r   c                     t          j        dd                                          }|rt          |          S t	          d          }||S | | S t                      dz  S )av  Return the bundled skills directory for source and packaged installs.

    Resolution order:
        1. ``HERMES_BUNDLED_SKILLS`` env var (Nix wrapper / explicit override)
        2. Wheel-installed ``<sysconfig data>/skills`` (pip install path)
        3. Caller-supplied ``default`` (typically the source-checkout path)
        4. ``<HERMES_HOME>/skills`` last-resort
    HERMES_BUNDLED_SKILLSr   skillsrV   rX   s      r   get_bundled_skills_dirra      sj     y0"55;;==H H~~%h//Hx''r   new_subpathold_namec                 ^    t                      }||z  }|                                r|S || z  S )u  Resolve a Hermes subdirectory with backward compatibility.

    New installs get the consolidated layout (e.g. ``cache/images``).
    Existing installs that already have the old path (e.g. ``image_cache``)
    keep using it — no migration required.

    Args:
        new_subpath: Preferred path relative to HERMES_HOME (e.g. ``"cache/images"``).
        old_name: Legacy path relative to HERMES_HOME (e.g. ``"image_cache"``).

    Returns:
        Absolute ``Path`` — old location if it exists on disk, otherwise the new one.
    )r<   r/   )rb   rc   r'   old_paths       r   get_hermes_dirrf      s:     DhH +r   c                      t                      } 	 dt          |                     t          j                                        z   S # t
          $ r t          |           cY S w xY w)a  Return a user-friendly display string for the current HERMES_HOME.

    Uses ``~/`` shorthand for readability::

        default:  ``~/.hermes``
        profile:  ``~/.hermes/profiles/coder``
        custom:   ``/opt/hermes-custom``

    Use this in **user-facing** print/log messages instead of hardcoding
    ``~/.hermes``.  For code that needs a real ``Path``, use
    :func:`get_hermes_home` instead.
    z~/)r<   r   r@   r   r'   rA   )r'   s    r   display_hermes_homerh      sh     Dc$**49;;778888   4yys   5A A"!A"c                     | j                                         }|t          d          k    st          |j                  dk     rdS 	 t          j        |d           dS # t          $ r Y dS w xY w)a  Chmod ``0o700`` on the parent directory of *path*, but only if safe.

    Refuses to chmod ``/`` or any top-level directory (resolved parent with
    fewer than 3 parts, i.e. ``/`` or any direct child like ``/usr``) to
    prevent catastrophic host bricking when ``HERMES_HOME`` or other path
    env vars resolve to an unexpected location.

    See https://github.com/NousResearch/hermes-agent/issues/25821.
    /   Ni  )rB   r?   r   lenpartsr$   chmodr2   )r	   rB   s     r   secure_parent_dirro   	  s     [  ""Fcc&,//!33
   s   A 
A-,A-c                      t                      pt          j        d          } | sdS t          j                            | d          }t          j                            |          r|S dS )u  Return a per-profile HOME directory for subprocesses, or None.

    When ``{HERMES_HOME}/home/`` exists on disk, subprocesses should use it
    as ``HOME`` so system tools (git, ssh, gh, npm …) write their configs
    inside the Hermes data directory instead of the OS-level ``/root`` or
    ``~/``.  This provides:

    * **Docker persistence** — tool configs land inside the persistent volume.
    * **Profile isolation** — each profile gets its own git identity, SSH
      keys, gh tokens, etc.

    The Python process's own ``os.environ["HOME"]`` and ``Path.home()`` are
    **never** modified — only subprocess environments should inject this value.
    Activation is directory-based: if the ``home/`` subdirectory doesn't
    exist, returns ``None`` and behavior is unchanged.
    r,   Nr'   )r   r$   rW   r	   joinisdir)hermes_homeprofile_homes     r   get_subprocess_homeru     sa    " +,,H	-0H0HK t7<<V44L	w}}\"" 4r   )minimallowmediumhighxhigheffortc                     | r|                                  sdS |                                                                  } | dk    rddiS | t          v rd| dS dS )aD  Parse a reasoning effort level into a config dict.

    Valid levels: "none", "minimal", "low", "medium", "high", "xhigh".
    Returns None when the input is empty or unrecognized (caller uses default).
    Returns {"enabled": False} for "none".
    Returns {"enabled": True, "effort": <level>} for valid effort levels.
    NnoneenabledFT)r~   r{   )r&   lowerVALID_REASONING_EFFORTS)r{   s    r   parse_reasoning_effortr   :  sn       t\\^^!!##F5!!(((62224r   c                  v    t          j        dd          } t          t          j        d          pd| v           S )u   Return True when running inside a Termux (Android) environment.

    Checks ``TERMUX_VERSION`` (set by Termux) or the Termux-specific
    ``PREFIX`` path.  Import-safe — no heavy deps.
    PREFIXr   TERMUX_VERSIONzcom.termux/files/usr)r$   rW   bool)prefixs    r   	is_termuxr   L  s9     Yx$$F	*++O/E/OPPPr   _wsl_detectedc                      t           t           S 	 t          ddd          5 } d|                                                                 v a ddd           n# 1 swxY w Y   n# t          $ r da Y nw xY wt           S )u   Return True when running inside WSL (Windows Subsystem for Linux).

    Checks ``/proc/version`` for the ``microsoft`` marker that both WSL1
    and WSL2 inject.  Result is cached for the process lifetime.
    Import-safe — no heavy deps.
    Nz/proc/versionrutf-8encoding	microsoftF)r   openreadr   r6   )fs    r   is_wslr   Y  s      /3999 	<Q'16688>>+;+;;M	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	< 	<   s4   A# )AA# AA# AA# #A21A2_container_detectedc                     t           t           S t          j                            d          rda dS t          j                            d          rda dS 	 t	          ddd          5 } |                                 }d	|v sd
|v sd|v rda 	 ddd           dS 	 ddd           n# 1 swxY w Y   n# t          $ r Y nw xY wda dS )u  Return True when running inside a Docker/Podman container.

    Checks ``/.dockerenv`` (Docker), ``/run/.containerenv`` (Podman),
    and ``/proc/1/cgroup`` for container runtime markers.  Result is
    cached for the process lifetime.  Import-safe — no heavy deps.
    Nz/.dockerenvTz/run/.containerenvz/proc/1/cgroupr   r   r   dockerpodmanz/lxc/F)r   r$   r	   r/   r   r   r2   )r   cgroups     r   is_containerr   n  sN    &""	w~~m$$ "t	w~~*++ "t"C'::: 	aVVXXF6!!X%7%77f;L;L&*#		 	 	 	 	 	 	 	;L	 	 	 	 	 	 	 	 	 	 	 	 	 	 	
    5s<   B2 ($B&B2 B2 &B**B2 -B*.B2 2
B?>B?c                  $    t                      dz  S )zReturn the path to ``config.yaml`` under HERMES_HOME.

    Replaces the ``get_hermes_home() / "config.yaml"`` pattern repeated
    in 7+ files (skill_utils.py, hermes_logging.py, hermes_time.py, etc.).
    zconfig.yamlr<    r   r   get_config_pathr     s     },,r   c                  $    t                      dz  S )z:Return the path to the skills directory under HERMES_HOME.r`   r   r   r   r   get_skills_dirr     s    x''r   c                  $    t                      dz  S )z7Return the path to the ``.env`` file under HERMES_HOME.z.envr   r   r   r   get_env_pathr     s    v%%r   forcec                     | sdS ddl t          j        dd          rdS j        dfd	}d|_        |_        dS )u  Monkey-patch ``socket.getaddrinfo`` to prefer IPv4 connections.

    On servers with broken or unreachable IPv6, Python tries AAAA records
    first and hangs for the full TCP timeout before falling back to IPv4.
    This affects httpx, requests, urllib, the OpenAI SDK — everything that
    uses ``socket.getaddrinfo``.

    When *force* is True, patches ``getaddrinfo`` so that calls with
    ``family=AF_UNSPEC`` (the default) resolve as ``AF_INET`` instead,
    skipping IPv6 entirely.  If no A record exists, falls back to the
    original unfiltered resolution so pure-IPv6 hosts still work.

    Safe to call multiple times — only patches once.
    Set ``network.force_ipv4: true`` in ``config.yaml`` to enable.
    Nr   _hermes_ipv4_patchedFc           	          |dk    r6	  | |j         |||          S # j        $ r  | |||||          cY S w xY w | |||||          S )Nr   )AF_INETgaierror)hostportfamilytypeprotoflags_original_getaddrinfosockets         r   _ipv4_getaddrinfoz0apply_ipv4_preference.<locals>._ipv4_getaddrinfo  s    Q;;U,,$eU   ? U U U,,T4ueTTTTTU %$T4ueLLLs    ;;T)r   r   r   r   )r   getattrgetaddrinfor   )r   r   r   r   s     @@r   apply_ipv4_preferencer     s       MMM v!#95AA ".	M 	M 	M 	M 	M 	M 	M .2**Fr   zpartial-stream-stublengthzhttps://openrouter.ai/api/v1z/models)N)F).__doc__r$   r"   rL   contextvarsr   r   pathlibr   r   r   __annotations__objectr   r   r   r   r   r   r*   r<   rG   rS   rZ   r]   ra   rf   rh   ro   ru   r   dictr   r   r   r   r   r   r   r   r   r   PARTIAL_STREAM_STUB_IDFINISH_REASON_LENGTHOPENROUTER_BASE_URLOPENROUTER_MODELS_URLr   r   r   <module>r      s     
			 



     ) ) ) ) ) ) ) )       "' $ & & &	2<*V3 3 3 z#,/   
,3:#4 , , , , ,'e ' ' ' ' '
#*    #4 # # # #7/ 7/ 7/ 7/ 7/t% % % % %P     "1 1TD[ 1D 1 1 1 1"/ /4$; /$ / / / /&( (D4K (4 ( ( ( (( s t    *S    (D T    (S4Z    4 H 3 4$;    $Q4 Q Q Q Q "td{ ! ! !    $ $( TD[ ' ' 'd    >- - - - -( ( ( ( (&d & & & &'+ '+ '+$ '+ '+ '+ '+Z /   5 .777   r   