
    [j                    L   U d Z ddlmZ ddlZddl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dlmZ ddlmZ ddlmZ dd	lmZ dd
lmZmZ erddlmZ  ej        e          ZdZd\dZd]dZd^dZ d_dZ!d_dZ"ddiZ#h dZ$d`dZ%dad!Z&dbd#Z'dcd%Z(ddd'Z)ded,Z*	 dfdgd.Z+dhd/Z,d0Z-did3Z.d4Z/djd6Z0dkd:Z1d;d<hZ2d;d<d<d=Z3d`d>Z4d?d?d?d?d@d?dAdAd?d@dBZ5dldFZ6e	 G dG dH                      Z7 e            Z8dIe9dJ<   da:d9e9dK<   dLa;dMe9dN<   dmdOZ<dmdPZ=dndSZ>dodUZ?dpdXZ@dqdrdZZAdsd[ZBdS )ta  Honcho client initialization and configuration.

Resolution order for config file:
  1. $HERMES_HOME/honcho.json  (instance-local, enables isolated Hermes instances)
  2. ~/.honcho/config.json     (global, shared across all Honcho-enabled apps)
  3. Environment variables     (HONCHO_API_KEY, HONCHO_ENVIRONMENT)

Resolution order for host-specific settings:
  1. Explicit host block fields (always win)
  2. Flat/global fields from config root
  3. Defaults (host name as workspace/peer)
    )annotationsN)	dataclassfield)Path)urlparse)get_hermes_home)_get_default_hermes_home)SingletonSlot)AnyTYPE_CHECKINGHonchohermesprofile
str | Nonereturnstrc                    | r| dv rt           S d                    d | D                                           d          }t            d|pd S )z5Return the safe Honcho host key for a Hermes profile.>   customdefault c              3  N   K   | ] }|                                 s|d v r|ndV  !dS )z_-_N)isalnum).0cs     >/home/ice/.hermes/hermes-agent/plugins/memory/honcho/client.py	<genexpr>z#profile_host_key.<locals>.<genexpr>*   s;      PP1QYY[[>AII3PPPPPP    r   r   )HOSTjoinstrip)r   	sanitizeds     r   profile_host_keyr$   &   sb     g!666PPPPPPPVVWZ[[I--Y+)---r   rawdicthostc                   |                      d          pi }|                     |i           }|s|                    t           d          s|S t           d|t          t                    dz   d          }|                     |i           S )z@Return host config, accepting legacy dot-form profile host keys.hostsr   .   N)get
startswithr    len)r%   r'   r)   blocklegacys        r   _host_blockr1   .   s    GGG"EIIdBE DOOtJJJ// --tCIIMNN+--F99VR   r   c                 "   t           j                            dd                                          } | r| S 	 ddlm}  |            }t          |          }n# t          $ r
 t          }Y nw xY w|t          k    r	 t                      }|
                                rat          j        |                    d                    }t          |                    dd                                                    }|r|S n# t          $ r Y nw xY w|S )aW  Derive the Honcho host key from the active Hermes profile.

    Resolution order:
      1. HERMES_HONCHO_HOST env var (explicit override)
      2. Active profile name via profiles system -> ``hermes_<profile>``
      3. defaultHost from the active config, but only for the default profile
      4. Fallback: ``"hermes"`` (default profile)
    HERMES_HONCHO_HOSTr   r   )get_active_profile_nameutf-8encodingdefaultHost)osenvironr,   r"   hermes_cli.profilesr4   r$   	Exceptionr    resolve_config_pathexistsjsonloads	read_textr   )explicitr4   r   profile_hostpathr%   default_hosts          r   resolve_active_hostrF   8   s3    z~~2B77==??H ??????))++'00    t	&((D{{}} (j!A!ABB"377="#=#=>>DDFF ('' 	 	 	D	 s$   A A,+A,;BC? ?
DDr   c                 4    t          j                    dz  dz  S )z:Return the shared Honcho config path for the current HOME.z.honchozconfig.json)r   home r   r   resolve_global_config_pathrJ   ^   s    9;;"]22r   c                     t                      dz  } |                                 r| S t                      dz  }|| k    r|                                r|S t                      S )uo  Return the active Honcho config path.

    Resolution order:
      1. $HERMES_HOME/honcho.json      (profile-local, if it exists)
      2. ~/.hermes/honcho.json          (default profile — shared host blocks live here)
      3. ~/.honcho/config.json          (global, cross-app interop)

    Returns the global path if none exist (for first-time setup writes).
    zhoncho.json)r   r>   r	   rJ   )
local_pathdefault_paths     r   r=   r=   c   sn     !""]2J  ,--=Lz!!l&9&9&;&;!%'''r   autohybrid>   toolsrO   contextvalc                R    t                               | |           } | t          v r| ndS )u?   Normalize legacy recall mode values (e.g. 'auto' → 'hybrid').rO   )_RECALL_MODE_ALIASESr,   _VALID_RECALL_MODESrR   s    r   _normalize_recall_moderW   }   s,    

"
"3
,
,C,,,33(:r   r   boolc                6    |D ]}|t          |          c S | S )aE  Resolve a bool config field: first non-None wins, else default.

    Variadic to support aliased keys (e.g. ``pinUserPeer`` shadowing
    ``pinPeerName`` for backwards compatibility).  Pass values in
    precedence order: caller's preferred alias first, then fallback
    aliases, in (host, root) interleaving as needed.
    )rX   )r   valsrR   s      r   _resolve_boolr[      s4       ?99 Nr   
int | Nonec                j    | |fD ]-}|)	 t          |          c S # t          t          f$ r Y )w xY w.dS )z@Parse contextTokens: host wins, then root, then None (uncapped).Nint
ValueError	TypeErrorhost_valroot_valrR   s      r   _parse_context_tokensre      s`    (#  ?3xx	*    
 4   //r_   c                j    | |fD ]-}|)	 t          |          c S # t          t          f$ r Y )w xY w.|S )z<Parse an integer config: host wins, then root, then default.r^   rc   rd   r   rR   s       r   _parse_int_configri      s`    (#  ?3xx	*    
 Nrf   floatc                    | |fD ];}|7	 t          dt          |                    c S # t          t          f$ r Y 7w xY w<|S )uH   Parse a float config: host wins, then root, then default. Clamped ≥ 0.Ng        )maxrj   r`   ra   rh   s       r   _parse_float_configrm      sm    (#  ?3c

+++++	*    
 Ns   )==host_objroot_objkeydict[str, str]c                L   || v r| |         n|                     |          }t          |t                    si S i }|                                D ]T\  }}t	          |                                          }|!t	          |                                          nd}|r|r|||<   U|S )z@Parse a string-to-string map with host-level whole-map override.Nr   )r,   
isinstancer&   itemsr   r"   )	rn   ro   rp   sourceresultraw_key	raw_value	alias_keyalias_values	            r   _parse_string_mapr{      s    !XooXc]]8<<3D3DFfd## 	F$llnn , ,LL&&((	090Ec)nn**,,,2 	, 	, +F9Mr   r   c                    || v r|                      |          }n|                     ||          }||S t          |                                          S )zEParse a string field where host-level empty string can override root.)r,   r   r"   )rn   ro   rp   r   values        r   _parse_optional_stringr~      sU     hS!!S'**}u::r   c           	         | |fD ]I}|E	 t          dt          t          |          d                    c S # t          t          f$ r Y Ew xY wJdS )zCParse dialecticDepth: host wins, then root, then 1. Clamped to 1-3.Nr+      )rl   minr_   r`   ra   rb   s      r   _parse_dialectic_depthr      sw    (#  ?1c#c((A../////	*    
 1s   *7A
A)minimallowmediumhighrl   depthlist[str] | Nonec                    | |fD ]l}|ht          |t                    rSd |d|         D             }t          |          |k     r(|                    d           t          |          |k     (|c S mdS )zParse dialecticDepthLevels: optional array of reasoning levels per pass.

    Returns None when not configured (use proportional defaults).
    When configured, validates each level and truncates/pads to match depth.
    Nc                (    g | ]}|t           v r|nd S )r   )_VALID_REASONING_LEVELS)r   lvls     r   
<listcomp>z1_parse_dialectic_depth_levels.<locals>.<listcomp>   s7        5555  r   r   )rs   listr.   append)rc   rd   r   rR   levelss        r   _parse_dialectic_depth_levelsr      s     (# 	 	?z#t44? vv;  F
 f++%%e$$$ f++%%MMM4r   g      >@base_urlc                   | sdS 	 t          |           }|j        pd                                                                }n# t          $ r d}Y nw xY w|dv rdS |sdS 	 t          j        |          }n# t          $ r Y dS w xY w|j        s|j	        s|j
        rdS |j        dk    r6t          j        d          |cxk    rt          j        d          k    rn ndS dS )an  Return True for loopback/LAN/VPN self-hosted Honcho URLs.

    Local Honcho deployments can run without auth, but the SDK requires a
    non-empty api_key argument.  Treat loopback plus RFC1918/link-local/ULA
    and carrier-grade-NAT IPs as local so LAN/VPN URLs such as
    ``http://192.168.2.112:8000`` get the same placeholder-key behavior as
    localhost.
    Fr   >   ::1	127.0.0.1	localhostT   z
100.64.0.0z100.127.255.255)r   hostnamer"   lowerr<   	ipaddress
ip_addressr`   is_loopback
is_privateis_link_localversion)r   parsedr'   ips       r   _is_local_base_urlr      s9     u(##%2,,..4466    000t u!$''   uu 
~  "*: t 
zQ9/==nnnnyG[\mGnGnnnnnnt5s#   <A AA A5 5
BBvaluesr   float | Nonec                     | D ]c}|t          |t                    r|                                }|s1	 t          |          }n# t          t
          f$ r Y Uw xY w|dk    r|c S ddS )z=Return the first non-empty value coerced to a positive float.Nr   )rs   r   r"   rj   ra   r`   )r   r}   r   s      r   _resolve_optional_floatr     s      =eS!! 	KKMME 	5\\FF:& 	 	 	H	A::MMM 4s   AAAunifieddirectional)sharedseparatecrossc                R    t                               | |           } | t          v r| ndS )z"Normalize observation mode values.r   )_OBSERVATION_MODE_ALIASESr,   _VALID_OBSERVATION_MODESrV   s    r   _normalize_observation_moder   4  s,    
#
'
'S
1
1C11133}Dr   Tuser_observe_meuser_observe_othersai_observe_meai_observe_othersF)r   r   modeobservation_objdict | Nonec                   t                               | t           d                   }|rt          |t                    st          |          S |                    d          pi }|                    d          pi }|                    d|d                   |                    d|d                   |                    d|d                   |                    d|d	                   d
S )aj  Resolve per-peer observation booleans.

    Config forms:
      String shorthand:  ``"observationMode": "directional"``
      Granular object:   ``"observation": {"user": {"observeMe": true, "observeOthers": true},
                                           "ai": {"observeMe": true, "observeOthers": false}}``

    Granular fields override preset defaults.
    r   userai	observeMer   observeOthersr   r   r   r   )_OBSERVATION_PRESETSr,   rs   r&   )r   r   preset
user_blockai_blocks        r   _resolve_observationr   H  s     "%%d,@,OPPF *_d"C"C F|| $$V,,2J""4((.BH &>>+v>O7PQQ)~~ovF[?\]]!k6/3JKK%\\/6BU;VWW	  r   c                  "   e Zd ZU dZeZded<   dZded<   dZded<   d	Z	ded
<   dZ
ded<   dZded<   dZded<   dZded<   dZded<    ee          Zded<   dZded<   dZded<   dZded<   dZded<   dZded<   d Zded!<   dZded"<   d#Zd$ed%<   d&Zd$ed'<   dZd(ed)<   dZded*<   d+Zded,<   d-Zd$ed.<   d/Zd$ed0<   d1Z ded2<   dZ!ded3<   d4Z"ded5<   d&Z#d$ed6<   d&Z$d$ed7<   dZ%ded8<   d9Z&d:ed;<   d<Z'd:ed=<   d>Z(ded?<   dZ)ded@<   dZ*dedA<   dZ+dedB<   dZ,dedC<   dDZ-dedE<   dZ.dedF<    ee          Z/dedG<    ee          Z0dHedI<   dZ1dedJ<   e2	 	 d[d\dL            Z3e2	 	 d]d^dO            Z4e5d_dQ            Z6dRZ7dSZ8e2d`dV            Z9	 	 	 	 dadbdZZ:dS )cHonchoClientConfigz>Configuration for Honcho client, resolved for a specific host.r   r'   r   workspace_idNr   api_key
productionenvironmentr   r   timeout	peer_nameai_peerFrX   pin_peer_name)default_factoryrq   user_peer_aliasesr   runtime_peer_prefixenabledTsave_messagesasyncz	str | intwrite_frequencyr\   context_tokensr   dialectic_reasoning_leveldialectic_dynamicX  r_   dialectic_max_charsr+   dialectic_depthr   dialectic_depth_levelsreasoning_heuristicr   reasoning_level_capa  message_max_chars'  dialectic_max_input_charsrO   recall_modeinit_on_session_start
every-turninjection_frequencycontext_cadencedialectic_cadencequery_rewrite      @rj   first_turn_base_wait       @first_turn_dialectic_waitr   observation_moder   r   r   r   per-directorysession_strategysession_peer_prefixsessionszdict[str, Any]r%   explicitly_configuredr   c                   |pt                      }t          j                            d          }t          j                            dd                                          pd}t          t          j                            d                    } | |||t          j                            dd          |||t          |p|                    S )	z4Create config from environment variables (fallback).HONCHO_API_KEYHONCHO_BASE_URLr   NHONCHO_TIMEOUTHONCHO_ENVIRONMENTr   )r'   r   r   r   r   r   r   r   )rF   r9   r:   r,   r"   r   rX   )clsr   r'   resolved_hostr   r   r   s          r   from_envzHonchoClientConfig.from_env  s     5 3 5 5*..!122:>>"3R88>>@@HD)"*..9I*J*JKKs%
';\JJ!,H--	
 	
 	
 		
r   config_pathPath | Nonec                B   |pt                      }|pt                      }|                                s1t                              d|           |                     |          S 	 t          j        |                    d                    }nU# t          j	        t          f$ r<}t                              d||           |                     |          cY d}~S d}~ww xY wt          ||          }t          |          p|                    d          du }|                    d	          p|                    d	          p|}	|                    d
          p|                    d
          p|}
|                    d          p3|                    d          pt          j                            d          }|                    d          p|                    dd          }|                    d          pH|                    d          p3t          j                            dd                                          pd}t%          |                    d          |                    d          |                    d          |                    d          t          j                            d                    }|                    d          }|                    d          }||}n||}nt          |p|          }|                    d          p|                    d          pd}	 t'          |          }n&# t(          t*          f$ r t-          |          }Y nw xY w|                    d          }||n|                    dd          }|                    d          p|                    dd          }|                    d          }||n|                    dd          } | dci d|d|	d|d|d|d|d |                    d!          p|                    d!          d"|
d#t/          |                    d$          |                    d%          |                    d$          |                    d%          d&          d't1          ||d(          d)t3          ||d*          d|d+|d,|d-t5          |                    d.          |                    d.                    d/|                    d0          p|                    d0          pd1d2t/          |                    d3          |                    d3          d&          d4t7          |                    d5          |                    d5          d6&          d7t9          |                    d8          |                    d8                    d9t;          |                    d:          |                    d:          t9          |                    d8          |                    d8                    ;          d<t/          |                    d=          |                    d=          d&          d>|                    d?          p|                    d?          pd@dAt7          |                    dB          |                    dB          dC&          dDt7          |                    dE          |                    dE          dF&          dGt=          |                    dH          p|                    dH          pdI          dJt/          |                    dK          |                    dK          d&          dL|                    dM          p|                    dMdN          dOt7          |                    dP          |                    dP          dQ&          dRt7          |                    dS          |                    dS          dQ&          dTt/          |                    dU          |                    dU          d&          dVt?          |                    dW          |                    dW          dX&          dYt?          |                    dZ          |                    dZ          d[&          d\tA          |                    d]          p|                    d]          p|rd^nd_          tC          tA          |                    d]          p|                    d]          p|rd^nd_          |                    d`          p|                    d`                    |||                    dai           ||dbS )dzCreate config from the resolved Honcho config path.

        Resolution: $HERMES_HOME/honcho.json -> ~/.honcho/config.json -> env vars.
        When host is None, derives it from the active Hermes profile.
        z2No global Honcho config at %s, falling back to env)r'   r5   r6   z*Failed to read %s: %s, falling back to envNr   T	workspaceaiPeerapiKeyr   r   r   baseUrlr   r   r   r   requestTimeoutr   writeFrequencyr   saveMessagessessionStrategyr   sessionPeerPrefixFr'   r   r   r   peerNamer   r   pinUserPeerpinPeerName)r   r   userPeerAliasesr   runtimePeerPrefixr   r   r   contextTokensr   dialecticReasoningLevelr   r   dialecticDynamicr   dialecticMaxCharsr   r   dialecticDepthr   dialecticDepthLevels)r   r   reasoningHeuristicr   reasoningLevelCapr   r   messageMaxCharsr   r   dialecticMaxInputCharsr   r   
recallModerO   r   initOnSessionStartr   injectionFrequencyr   r   contextCadencer+   r   dialecticCadencer   queryRewriter   firstTurnBaseWaitr   r   firstTurnDialecticWaitr   r   observationModer   r   observationr   )r   r   r   r%   r   rI   )"rF   r=   r>   loggerdebugr   r?   r@   rA   JSONDecodeErrorOSErrorwarningr1   rX   r,   r9   r:   r"   r   r_   ra   r`   r   r[   r{   r~   re   ri   r   r   rW   rm   r   r   )r   r'   r   r   rD   r%   e
host_block_explicitly_configuredr   r   r   r   r   r   host_enabledroot_enabledr   raw_wfr   	host_saver   r   host_prefixr   s                            r   from_global_configz%HonchoClientConfig.from_global_config  s'    5 3 5 53133{{}} 	4LLMtTTT<<]<333	4*T^^W^==>>CC$g. 	4 	4 	4NNGqQQQ<<]<33333333	4 !m44
 "&j!1!1!OSWWY5G5G45O NN;'' ww{## 	 NN8$$ wwx   	 NN8$$ 0wwx  0z~~.// 	 NN=)) 4ww}l33 	 GGI wwz""z~~/44::<< 	 	 *NN9%%NN+,,GGIGG$%%JNN+,,
 
 "~~i00wwy))#"GG%"GG 7.h//G NN+,, ww'(( 	
	*),VOO:& 	* 	* 	*!&kkOOO	* NN>22	%.%:		X\@]@] NN,-- ;ww(/:: 	 !nn%899&2KK,e44 	
 s R
 R
 R
R
"R
 GR
 $	R

 XR
 GR
 !nnZ00GCGGJ4G4GR
 GR
 ( }--}--&&&&   R
, 0!  -R
6 !7#! ! !7R
@ GAR
B (-CR
D ,OER
F 1//((  GR
P 899 77455UR
X ,122*++   YR
b !2233+,,! ! ! !cR
l 3/00())  mR
t $A566.//,Z^^<L-M-MswwWgOhOhii$ $ $ $uR
~ !.344,--! ! ! !R
J 233 77.//OR
R 0011)**   SR
\ '8788011' ' ' ']R
f /|,, 77<((  gR
p #0344,--# # # #qR
~ 344 ?77/>>AR
D ./00())   ER
N 0122*++   OR
X (~..''   YR
b "5233+,," " " "cR
l ':788011' ' ' 'mR
@ 9011 L77,--L!7JII]  
 #+NN#455 Pww011P%;N		 
 }--G1G1G KR
Z . 3WWZ,,"8cR
 R
 R
 R
 R	
s0   '(B C"&1CC"C"3M  M&%M&cwdc                    ddl }	 |                    g ddd| d|j                  }|j        dk    r+t	          |j                                                  j        S n# t          |j	        f$ r Y nw xY wdS )zBReturn the git repo root directory name, or None if not in a repo.r   N)gitz	rev-parsez--show-toplevelT   )capture_outputtextr,  r   stdin)

subprocessrunDEVNULL
returncoder   stdoutr"   namer!  TimeoutExpired)r,  r3  roots      r   _git_repo_namez!HonchoClientConfig._git_repo_name  s     			>>777#$C ( "  D
 !##DK--//0055 $23 	 	 	D	ts   AA A32A3d      r#   originalc                "   | j         }t          |          |k    r|S | j        }t          j        |                    d                                                    d|         }||z
  dz
  }|d|                             d          }| d| S )a]  Truncate a sanitized session ID to Honcho's 100-char limit.

        The common case (short keys) short-circuits with no modification.
        For over-limit keys, keep a prefix of the sanitized ID and append a
        deterministic ``-<sha256 prefix>`` suffix so two distinct long keys
        that share a leading segment don't collide onto the same truncated ID.
        The hash is taken over the *original* pre-sanitization key, so two
        inputs that sanitize to the same string still collide intentionally
        (same logical session), but two inputs that only share a prefix do not.
        r5   Nr+   -)_HONCHO_SESSION_ID_MAX_LENr.   _HONCHO_SESSION_ID_HASH_LENhashlibsha256encode	hexdigestrstrip)r   r#   r>  max_lenhash_lendigest
prefix_lenprefixs           r   _enforce_session_id_limitz,HonchoClientConfig._enforce_session_id_limit  s     0y>>W$$2 8 899CCEEixiP x'!+
;J;'..s33##6###r   session_title
session_idgateway_session_keyc                   ddl }|st          j                    }|rB|                    dd|                              d          }|r|                     ||          S | j        dk    r|r| j        r| j        r| j         d| S |S | j	        
                    |          }|r|S |rH|                    dd|                              d          }|r| j        r| j        r| j         d| S |S | j        dk    rE|                     |          pt          |          j        }| j        r| j        r| j         d| S |S | j        dv r0t          |          j        }| j        r| j        r| j         d| S |S | j        S )ua  Resolve Honcho session name.

        Resolution order:
          1. Gateway session key (stable per-chat identifier from gateway platforms)
          2. per-session strategy — Hermes session_id ({timestamp}_{hex}); authoritative,
             so a generated title never remaps a live conversation
          3. Manual directory override from sessions map
          4. Hermes session title (from /title command; non-per-session)
          5. per-repo strategy — git repo root directory name
          6. per-directory strategy — directory basename
          7. global strategy — workspace name
        r   Nz[^a-zA-Z0-9_-]+r@  per-sessionzper-repo>   rR  r   )rer9   getcwdsubr"   rM  r   r   r   r   r,   r;  r   r8  r   )	selfr,  rN  rO  rP  rS  r#   manualbases	            r   resolve_session_namez'HonchoClientConfig.resolve_session_name  s   & 				 	)++C  	V138KLLRRSVWWI V55iATUUU
  M11j1' 8DN 8.77:777 ""3'' 	M  	!13FFLLSQQI !+ ; ;"n::y:::    J..&&s++=tCyy~D' 2DN 2.114111K  $DDD99>D' 2DN 2.114111K   r   )r   N)r   r   r'   r   r   r   NN)r'   r   r   r   r   r   )r,  r   r   r   )r#   r   r>  r   r   r   )NNNN)
r,  r   rN  r   rO  r   rP  r   r   r   );__name__
__module____qualname____doc__r    r'   __annotations__r   r   r   r   r   r   r   r   r   r&   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r%   r   classmethodr   r+  staticmethodr;  rA  rB  rM  rY  rI   r   r   r   r   g  s        HHD L    G#K####H G     I    G  M ).d(C(C(CCCCC!!!!!GM ")O((((!%N%%%% &+**** #""""""""" O 043333 !%$$$$%%%%% #""""%*****
  K #(''''+++++O  M #&%%%%'***** *)))) !O     $$$$$M"""""+++++ %%%%%$uT:::H::::%555C5555 #('''' %
 
 
 
 [
*   #'x
 x
 x
 x
 [x
t    \* "%"#$ $ $ [$4 $(!%*.C! C! C! C! C! C! C!r   r   r
   _honcho_client_slot_cached_timeoutrZ  ztuple[int | None, float | None]_honcho_json_timeout_memoc                    	 ddl m}   |                                 di           }t          |t                    r6t          |                    d          |                    d                    S dS # t          $ r Y dS w xY w)zJRead honcho.timeout / honcho.request_timeout via the cached config loader.r   )load_config_readonlyhonchor   request_timeoutN)hermes_cli.configrf  r,   rs   r&   r   r<   )rf  
honcho_cfgs     r   _config_yaml_timeoutrk  d  s    ::::::))++//"==
j$'' 	*y))011   t   tts   A.A3 3
B Bc                 8   	 t                      } 	 |                                 j        }n# t          $ r d}Y nw xY wt          d         |k    rt          d         S d}|dk    rt          j        |                     d                    }t          |t                                }t          |                    d          |                    d          |                    d          |                    d                    }||fa|S # t          $ r Y dS w xY w)	zRRead timeout/requestTimeout from honcho.json (host block wins), memoized on mtime.r   r+   Nr5   r6   r   r   )r=   statst_mtime_nsr!  rd  r?   r@   rA   r1   rF   r   r,   r<   )rD   mtime_nsr   r%   r$  s        r   _honcho_json_timeoutrq  t  s*   "$$	 IIKK3HH 	 	 	HHH	$Q'833,Q//r>>*T^^W^==>>C$S*=*?*?@@J-y))/00	""())	 G &.w$7!   tts3   D + D :D : D B/D 
DDconfigHonchoClientConfig | Nonec                    | | j         }n<t                      }|,t          t          j                            d                    }|t                      }||nt          S )a  Mirror the build path's timeout resolution so the staleness check agrees with it.

    With an explicit config this matches ``_build`` (config.timeout, then
    config.yaml, then default).  With no config it matches what
    ``from_global_config`` + ``_build`` would produce: honcho.json host
    block/root keys, then HONCHO_TIMEOUT, then config.yaml, then default.
    Any source skew here makes the check disagree with the built client
    forever and rebuild it on every call.
    Nr   )r   rq  r   r9   r:   r,   rk  _DEFAULT_HTTP_TIMEOUT)rr  r   s     r   _resolve_timeout_from_sourcesrv    s_     .&((?-bjnn=M.N.NOOG&(()77/DDr   Nonec                    	 ddl m} |                    t                      | j                  \  }}|r	|| _        dS dS # t          $ r  t                              dd           Y dS w xY w)zRefresh a near-expiry OAuth grant and point ``config.api_key`` at it.

    No-op for static API keys or when refresh fails (fail-open: the stale token
    is left in place and the existing 401 handling degrades gracefully).
    r   oauthz%Honcho OAuth pre-build refresh failedTexc_infoN)	plugins.memory.honchorz  ensure_fresh_tokenr=   r'   r   r<   r  r"  )rr  rz  tokenr   s       r   _apply_fresh_oauth_tokenr    s    O//////++,?,A,A6;OOq 	#"FNNN	# 	# O O O>NNNNNNOs   9? &A)(A)client'Honcho'c                X   	 ddl m} ||j        nt                      }|                    t                      |          \  }}|r3|r3|                    | |          st                                           dS dS dS dS # t          $ r  t                              dd           Y dS w xY w)zRotate the cached client's Bearer in place when its OAuth token is stale.

    If the SDK shape changed and the in-place rotation can't apply, the slot is
    reset so the next acquisition rebuilds with the fresh token.
    r   ry  Nz"Honcho OAuth cached refresh failedTr{  )r}  rz  r'   rF   r~  r=   apply_token_to_clientrb  resetr<   r  r"  )r  rr  rz  r'   r  	refresheds         r   _refresh_cached_oauthr    s    L//////$0v{{6I6K6K 334G4I4I4PPy 	( 	(u'B'B65'Q'Q 	(%%'''''	( 	( 	( 	( 	( 	( L L L;dKKKKKKLs   A5A? ?&B)(B)r   c                    t                                           }|Jt                     }|t          k    rt                                            dad}nt          |            |S  t                                           t                       j	        s j
        st          d          d fd}t                               |          S )a  Get or create the Honcho client singleton.

    When no config is provided, attempts to load ~/.honcho/config.json
    first, falling back to environment variables.

    Thread-safe: the client is built exactly once even under concurrent
    first calls (double-checked locking via ``SingletonSlot``), so racing
    threads can't each construct a client and leak the loser's connection.
    NzHoncho API key not found. Get your API key at https://app.honcho.dev, then run 'hermes honcho setup' or set HONCHO_API_KEY. For local instances, set HONCHO_BASE_URL instead.r   r  c                    	 ddl m} m}  |dd           n# t          $ r Y nt          $ r Y nw xY w	 ddlm} n# t          $ r t          d          w xY wj        }j        }|r|	 ddl	m
}  |            }|                    d	i           }t          |t                    rd|s*|                    d
d                                          pd }|6t          |                    d          |                    d                    }n# t          $ r Y nw xY w|t           }|r"t"                              d|j                   n&t"                              dj        j                   t+          |          }|rgj        pi }	|	                    d          pi                     j        i           }
t/          |
                    d                    }|rj        nd}nj        }|r.dd l}|                    dd|                              d          }j        |j        d}|r||d
<   |||d<   |a |di |S )Nr   )FeatureUnavailableensurezmemory.honchoF)promptr   zhoncho-ai is required for Honcho integration. Install it with: pip install honcho-ai  (or run `hermes honcho setup` to configure).)load_configrg  r   r   r   rh  z8Initializing Honcho client (base_url: %s, workspace: %s)z4Initializing Honcho client (host: %s, workspace: %s)r)   r   localz/v\d+/*$/)r   r   r   rI   )tools.lazy_depsr  r  ImportErrorr<   rg  r   r   r   ri  r  r,   rs   r&   r"   r   ru  r  infor   r'   r   r%   rX   r   rS  rU  rG  r   rc  )r  _lazy_ensurer   resolved_base_urlresolved_timeoutr  
hermes_cfgrj  	_is_local_rawr1   _host_has_keyeffective_api_key_rekwargsrr  s                  r   _buildz!get_honcho_client.<locals>._build  s   		RRRRRRRRL77777 	 	 	D 	 	 	 D	
	%%%%%%% 	 	 	?  	 #O!>  	$4$<999999([]]
'^^Hb99
j$// , [,6NN:r,J,J,P,P,R,R,ZVZ)'/+B&NN955&NN+<==, ,(    
 #4 	rKKRTegmgz{{{{KKNPVP[]c]pqqq ''899	 
	/
 :#D88G,,277RHHK !:!:;;M2? LW &  	X #R9J K K R RSV W W #/(!-
 

  	3!2F:' 0F9 +vs,    
1	11< A,BD 
DD)r   r  )rb  peekrv  rc  r  r  r   r+  r  r   r   r`   r,   )rr  cachednew_timeoutr  s   `   r   get_honcho_clientr    s     !%%''F 4F;;/))%%'''"OFF!&&111M~#6688 V$$$> 
&/ 
@
 
 	
_  _  _  _  _  _ B ""6***r   c                 @    t                                            dadadS )z7Reset the Honcho client singleton (useful for testing).NrZ  )rb  r  rc  rd  rI   r   r   reset_honcho_clientr  S  s'     O ,r   )r   r   r   r   )r%   r&   r'   r   r   r&   )r   r   )r   r   )rR   r   r   r   )r   rX   r   rX   )r   r\   )r   r_   r   r_   )r   rj   r   rj   )rn   r&   ro   r&   rp   r   r   rq   )r   )
rn   r&   ro   r&   rp   r   r   r   r   r   )r   r_   )r   r_   r   r   )r   r   r   rX   )r   r   r   r   )r   r   r   r   r   r&   )r   r   )rr  rs  r   rj   )rr  r   r   rw  )r  r  rr  rs  r   rw  )N)rr  rs  r   r   )r   rw  )Cr^  
__future__r   r?   r9   loggingrC  r   dataclassesr   r   pathlibr   urllib.parser   hermes_constantsr   r;   r	   plugins.plugin_utilsr
   typingr   r   rg  r   	getLoggerr[  r  r    r$   r1   rF   rJ   r=   rT   rU   rW   r[   re   ri   rm   r{   r~   r   r   r   ru  r   r   r   r   r   r   r   r   rb  r_  rc  rd  rk  rq  rv  r  r  r  r  rI   r   r   <module>r     s7     # " " " " "  				       ( ( ( ( ( ( ( (       ! ! ! ! ! ! , , , , , , 8 8 8 8 8 8 . . . . . . % % % % % % % % 		8	$	$. . . .! ! ! !# # # #L3 3 3 3
( ( ( (, ) 444 ; ; ; ;                 >@
 
 
 
 
    F    2  # # # #L   $ &}5 '0mVcdd E E E E  D 
  T 	 	    > n! n! n! n! n! n! n! n!b &3]__  4 4 4 4 $ $ $ $ $ >J  I I I I       8E E E E*O O O O L L L L"J+ J+ J+ J+ J+Z- - - - - -r   