
    `gj                    Z   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j4                  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%d@dZ&dAdZ'dBdZ(dCdZ)dDdZ*	 dE	 	 	 	 	 	 	 	 	 dFdZ+dGdZ,dZ-dHdZ.dZ/dId Z0dJd"Z1d#d$hZ2d#d$d$d%Z3d?d&Z4d'd'd'd'd(d'd)d)d'd(d*Z5	 	 	 	 	 	 dKd+Z6e	 G d, d-             Z7 e       Z8d.e9d/<   da:d!e9d0<   d1a;d2e9d3<   dLd4Z<dLd5Z=dMd6Z>dNd7Z?dOd8Z@dPdQd9ZAdRd:ZBy)Sa  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hermesc                    | r| dv rt         S dj                  d | D              j                  d      }t          d|xs d S )z5Return the safe Honcho host key for a Hermes profile.>   customdefault c              3  L   K   | ]  }|j                         s|d v r|nd  yw)z_-_N)isalnum).0cs     O/root/.hermes/venv/lib/python3.12/site-packages/plugins/memory/honcho/client.py	<genexpr>z#profile_host_key.<locals>.<genexpr>*   s#     P1QYY[AI3>Ps   "$r   profile)HOSTjoinstrip)r   	sanitizeds     r   profile_host_keyr    &   sI    g!66PPPVVWZ[IV1Y+),--    c                    | j                  d      xs i }|j                  |i       }|s|j                  t         d      s|S t         d|t        t              dz   d  }|j                  |i       S )z@Return host config, accepting legacy dot-form profile host keys.hostsr   .   N)get
startswithr   len)rawhostr#   blocklegacys        r   _host_blockr-   .   so    GGG"EIIdBEDOOtfAJ/vQtCIMN+,-F99VR  r!   c                    t         j                  j                  dd      j                         } | r| S 	 ddlm}  |       }t        |      }|t        k(  ro	 t               }|j                         rRt        j                  |j                  d            }t        |j                  dd            j                         }|r|S |S |S # t        $ r	 t        }Y w xY w# t        $ r Y |S w xY w)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.profilesr0   r    	Exceptionr   resolve_config_pathexistsjsonloads	read_textstr)explicitr0   r   profile_hostpathr)   default_hosts          r   resolve_active_hostrC   8   s     zz~~2B7==?H?)+'0 t	&(D{{}jj!AB"377="#=>DDF'' <%    		s$   C A+C CC	C('C(c                 6    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_pathrG   ^   s    99;"]22r!   c                     t               dz  } | j                         r| S t               dz  }|| k7  r|j                         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	   rG   )
local_pathdefault_paths     r   r9   r9   c   sT     !"]2J ,-=Lz!l&9&9&;%''r!   autohybrid>   toolsrL   contextc                F    t         j                  | |       } | t        v r| S dS )u?   Normalize legacy recall mode values (e.g. 'auto' → 'hybrid').rL   )_RECALL_MODE_ALIASESr&   _VALID_RECALL_MODESvals    r   _normalize_recall_moderT   }   s'    

"
"3
,C,,3:(:r!   c                2    |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.
    )bool)r   valsrS   s      r   _resolve_boolrX      s)      ?9 Nr!   c                `    | |fD ]  }|	 t        |      c S  y# t        t        f$ r Y 'w xY w)z@Parse contextTokens: host wins, then root, then None (uncapped).Nint
ValueError	TypeErrorhost_valroot_valrS   s      r   _parse_context_tokensra      sG    (# ?3x  	* s   
--c                b    | |fD ]  }|	 t        |      c S  |S # t        t        f$ r Y (w xY w)z<Parse an integer config: host wins, then root, then default.rZ   r_   r`   r   rS   s       r   _parse_int_configrd      sI    (# ?3x N 	* s   
..c                v    | |fD ]  }|	 t        dt        |            c S  |S # t        t        f$ r Y 2w xY w)uH   Parse a float config: host wins, then root, then default. Clamped ≥ 0.g        )maxfloatr\   r]   rc   s       r   _parse_float_configrh      sQ    (# ?3c
++ N 	* s   &88c                   || v r| |   n|j                  |      }t        |t              si S i }|j                         D ]F  \  }}t	        |      j                         }|t	        |      j                         nd}|s?|sB|||<   H |S )z@Parse a string-to-string map with host-level whole-map override.r   )r&   
isinstancedictitemsr>   r   )	host_objroot_objkeysourceresultraw_key	raw_value	alias_keyalias_values	            r   _parse_string_maprv      s    !XoXc]8<<3DFfd#	F$lln ,L&&(	090Ec)n**,2 +F9	,
 Mr!   c                    || v r| j                  |      }n|j                  ||      }||S t        |      j                         S )zEParse a string field where host-level empty string can override root.)r&   r>   r   )rm   rn   ro   r   values        r   _parse_optional_stringry      sF     hS!S'*}u:r!   c           	         | |fD ]&  }|	 t        dt        t        |      d            c S  y# t        t        f$ r Y ;w xY w)zCParse dialecticDepth: host wins, then root, then 1. Clamped to 1-3.r%      )rf   minr[   r\   r]   r^   s      r   _parse_dialectic_depthr}      sV    (# ?1c#c(A.//  	* s   /A A)minimallowmediumhighrf   c                    | |fD ]c  }|t        |t              s|d| D cg c]  }|t        v r|nd }}t        |      |k  r |j	                  d       t        |      |k  r |c S  yc c}w )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.
    Nr   )rj   list_VALID_REASONING_LEVELSr(   append)r_   r`   depthrS   lvllevelss         r   _parse_dialectic_depth_levelsr      s     (# 	?z#t4 v; 555@F 
 f+%e$ f+%M	 s   A,g      >@c                   | sy	 t        |       }|j                  xs dj                         j                         }|dv ry|sy	 t        j                  |      }|j                  s|j                  s|j                  ry|j                  dk(  r4t        j                  d      |cxk  rt        j                  d      k  ry yy# t        $ r d}Y w xY w# t        $ r Y yw xY w)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   lowerr8   	ipaddress
ip_addressr\   is_loopback
is_privateis_link_localversion)base_urlparsedr*   ips       r   _is_local_base_urlr      s     (#%2,,.446 00!!$' 
~~"*:*: 
zzQ9//=nyG[G[\mGnn o )    s#   7C C CC	C C float | Nonec                     | D ]=  }|t        |t              r|j                         }|s)	 t        |      }|dkD  s;|c S  y# t        t
        f$ r Y Rw xY w)z=Return the first non-empty value coerced to a positive float.Nr   )rj   r>   r   rg   r]   r\   )valuesrx   r   s      r   _resolve_optional_floatr     sn     =eS!KKME	5\F A:M 	 :& 		s   AAAunifieddirectional)sharedseparatecrossc                F    t         j                  | |       } | t        v r| S dS )z"Normalize observation mode values.r   )_OBSERVATION_MODE_ALIASESr&   _VALID_OBSERVATION_MODESrR   s    r   _normalize_observation_moder   4  s'    
#
'
'S
1C113D}Dr!   Tuser_observe_meuser_observe_othersai_observe_meai_observe_othersF)r   r   c                p   t         j                  | t         d         }|rt        |t              st        |      S |j                  d      xs i }|j                  d      xs i }|j                  d|d         |j                  d|d         |j                  d|d         |j                  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&   rj   rk   )modeobservation_objpreset
user_blockai_blocks        r   _resolve_observationr   H  s     "%%d,@,OPF*_d"CF| $$V,2J""4(.BH &>>+v>O7PQ)~~ovF[?\]!k6/3JK%\\/6BU;V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	 	 dR	 	 	 	 	 dSdK       Z3e2	 	 dT	 	 	 	 	 dUdL       Z4e5dVdM       Z6dNZ7dOZ8e2dWdP       Z9	 	 	 	 dX	 	 	 	 	 	 	 	 	 dYdQZ:y)ZHonchoClientConfigz>Configuration for Honcho client, resolved for a specific host.r>   r*   r   workspace_idN
str | Noneapi_key
productionenvironmentr   r   timeout	peer_nameai_peerFrV   pin_peer_name)default_factorydict[str, str]user_peer_aliasesr   runtime_peer_prefixenabledTsave_messagesasyncz	str | intwrite_frequency
int | Nonecontext_tokensr   dialectic_reasoning_leveldialectic_dynamicX  r[   dialectic_max_charsr%   dialectic_depthlist[str] | Nonedialectic_depth_levelsreasoning_heuristicr   reasoning_level_capa  message_max_chars'  dialectic_max_input_charsrL   recall_modeinit_on_session_start
every-turninjection_frequencycontext_cadencedialectic_cadencequery_rewrite      @rg   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_configuredc                   |xs
 t               }t        j                  j                  d      }t        j                  j                  dd      j	                         xs d}t        t        j                  j                  d            } | |||t        j                  j                  dd      |||t        |xs |            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   )rC   r5   r6   r&   r   r   rV   )clsr   r*   resolved_hostr   r   r   s          r   from_envzHonchoClientConfig.from_env  s     5 3 5**..!12::>>"3R8>>@HD)"**..9I*JK%

';\J!,H-	
 		
r!   c                0   |xs
 t               }|xs
 t               }|j                         s(t        j	                  d|       | j                  |      S 	 t        j                  |j                  d            }t        ||      }t        |      xs |j                  d      du }|j                  d	      xs |j                  d	      xs |}	|j                  d
      xs |j                  d
      xs |}
|j                  d      xs2 |j                  d      xs t        j                   j                  d      }|j                  d      xs |j                  dd      }|j                  d      xsE |j                  d      xs2 t        j                   j                  dd      j#                         xs d}t%        |j                  d      |j                  d      |j                  d      |j                  d      t        j                   j                  d            }|j                  d      }|j                  d      }||}n||}nt        |xs |      }|j                  d      xs |j                  d      xs d}	 t'        |      }|j                  d      }||n|j                  dd      }|j                  d      xs |j                  dd      }|j                  d      }||n|j                  dd      } | dci d|d|	d|d|d|d|d |j                  d!      xs |j                  d!      d"|
d#t/        |j                  d$      |j                  d%      |j                  d$      |j                  d%      d&      d't1        ||d(      d)t3        ||d*      d|d+|d,|d-t5        |j                  d.      |j                  d.            d/|j                  d0      xs |j                  d0      xs d1d2t/        |j                  d3      |j                  d3      d&      d4t7        |j                  d5      |j                  d5      d6&      d7t9        |j                  d8      |j                  d8            d9t;        |j                  d:      |j                  d:      t9        |j                  d8      |j                  d8            ;      d<t/        |j                  d=      |j                  d=      d&      d>|j                  d?      xs |j                  d?      xs d@dAt7        |j                  dB      |j                  dB      dC&      dDt7        |j                  dE      |j                  dE      dF&      dGt=        |j                  dH      xs |j                  dH      xs dI      dJt/        |j                  dK      |j                  dK      d&      dL|j                  dM      xs |j                  dMdN      dOt7        |j                  dP      |j                  dP      dQ&      dRt7        |j                  dS      |j                  dS      dQ&      dTt/        |j                  dU      |j                  dU      d&      dVt?        |j                  dW      |j                  dW      dX&      dYt?        |j                  dZ      |j                  dZ      d[&      d\tA        |j                  d]      xs |j                  d]      xs |rd^nd_      tC        tA        |j                  d]      xs |j                  d]      xs |rd^nd_      |j                  d`      xs |j                  d`            |||j                  dai       ||dbS # t        j                  t        f$ r3}t        j                  d||       | j                  |      cY d}~S d}~ww xY w# t(        t*        f$ r t-        |      }Y w xY w)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*   r1   r2   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   
recallModerL   r   initOnSessionStartr   injectionFrequencyr   r   contextCadencer%   r   dialecticCadencer   queryRewriter   firstTurnBaseWaitr   r   firstTurnDialecticWaitr   r   observationModer   r   observationr   )r   r   r   r)   r   rF   )"rC   r9   r:   loggerdebugr   r;   r<   r=   JSONDecodeErrorOSErrorwarningr-   rV   r&   r5   r6   r   r   r[   r]   r\   r>   rX   rv   ry   ra   rd   r}   r   rT   rh   r   r   )r   r*   config_pathr   rA   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313{{}LLMtT<<]<33	4**T^^W^=>C
 !m4
 "&j!1!OSWWY5G45O NN;' ww{# 	 NN8$ wwx  	 NN8$ 0wwx 0zz~~./ 	 NN=) 4ww}l3 	 GGI wwz"zz~~/4::< 	 	 *NN9%NN+,GGIGG$%JJNN+,
 "~~i0wwy)#"G%"G 7.h/G NN+, ww'( 	
	*),VO
 NN>2	%.%:	X\@] NN,- ;ww(/: 	 !nn%89&2K,e4 	
  R
R
"R
 R
 $	R

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

subprocessrunDEVNULL
returncoder   stdoutr   namer  TimeoutExpired)r+  r-  roots      r   _git_repo_namez!HonchoClientConfig._git_repo_name  s     			>>7#$C (( " D
 !#DKK--/0555 $  223 		s   AA' 'A?>A?d      c                   | j                   }t        |      |k  r|S | j                  }t        j                  |j                  d            j                         d| }||z
  dz
  }|d| j                  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.
        r1   Nr%   -)_HONCHO_SESSION_ID_MAX_LENr(   _HONCHO_SESSION_ID_HASH_LENhashlibsha256encode	hexdigestrstrip)r   r   originalmax_lenhash_lendigest
prefix_lenprefixs           r   _enforce_session_id_limitz,HonchoClientConfig._enforce_session_id_limit  s     00y>W$22 89CCEixP x'!+
;J'..s36(##r!   c                v   ddl }|st        j                         }|r6|j                  dd|      j	                  d      }|r| j                  ||      S | j                  dk(  r-|r+| j                  r| j                  r| j                   d| S |S | j                  j                  |      }|r|S |rO|j                  dd|      j	                  d      }|r+| j                  r| j                  r| j                   d| S |S | j                  dk(  rS| j                  |      xs t        |      j                  }| j                  r| j                  r| j                   d| S |S | j                  dv r@t        |      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_-]+r9  per-sessionzper-repo>   rI  r   )rer5   getcwdsubr   rG  r   r   r   r   r&   r5  r   r2  r   )	selfr+  session_title
session_idgateway_session_keyrJ  r   manualbases	            r   resolve_session_namez'HonchoClientConfig.resolve_session_name  s   & 	))+C 138KLRRSVWI55iATUU
   M1j''DNN..):,77 ""3'M 13FLLSQI++"nn-Qyk::     J.&&s+=tCy~~D''DNN..)4&11K   $DD9>>D''DNN..)4&11K    r!   )r   N)r   r>   r*   r   returnr   NN)r*   r   r  zPath | NonerT  r   )r+  r>   rT  r   )r   r>   rA  r>   rT  r>   )NNNN)
r+  r   rN  r   rO  r   rP  r   rT  r   );__name__
__module____qualname____doc__r   r*   __annotations__r   r   r   r   r   r   r   r   r   rk   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%  staticmethodr5  r:  r;  rG  rS  rF   r!   r   r   r   g  s   HD# L# GZ#K#Hj G\  Iz GS  M4 ).d(C~C!!GTM4 ")OY(!%NJ% &+s* #t""" OS 04,3 !%$%% #s"%*s*
  K #(4'++OSs  M4 #&%%'*u* *c) !OT  $$M4"t"+c+ %%$T:Hn:5C5 #(4' %

 
 
	
 
*   #'x
x
 !x
 
	x
 x
t  * "%"#$ $4 $(!%*.C!C! "C! 	C!
 (C! 
C!r!   r   r
   _honcho_client_slot_cached_timeoutrU  ztuple[int | None, float | None]_honcho_json_timeout_memoc                     	 ddl m}   |        j                  di       }t        |t              r*t        |j                  d      |j                  d            S y# t        $ r Y yw xY w)zJRead honcho.timeout / honcho.request_timeout via the cached config loader.r   )load_config_readonlyhonchor   request_timeoutN)hermes_cli.configra  r&   rj   rk   r   r8   )ra  
honcho_cfgs     r   _config_yaml_timeoutrf  d  sf    :)+//"=
j$'*y)01   s   AA 	A&%A&c                    	 t               } 	 | j                         j                  }t        d   |k(  r	t        d   S d}|dk7  rt        j                  | j                  d            }t        |t                     }t        |j                  d      |j                  d      |j                  d      |j                  d            }||fa|S # t        $ r d}Y w xY w# t        $ r Y yw xY w)	zRRead timeout/requestTimeout from honcho.json (host block wins), memoized on mtime.r   r%   Nr1   r2   r   r   )r9   statst_mtime_nsr  r_  r;   r<   r=   r-   rC   r   r&   r8   )rA   mtime_nsr   r)   r  s        r   _honcho_json_timeoutrl  t  s    "$	 IIK33H %Q'83,Q//r>**T^^W^=>C$S*=*?@J-y)/0	"()	G &.w$7!!  	H	"  s9   
C C C BC CC CC 	C)(C)c                    | | j                   }n4t               }|(t        t        j                  j                  d            }|
t               }||S 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.
    r   )r   rl  r   r5   r6   r&   rf  _DEFAULT_HTTP_TIMEOUT)configr   s     r   _resolve_timeout_from_sourcesrp    sV     ..&(?-bjjnn=M.NOG&()7D/DDr!   c                    	 ddl m} |j                  t               | j                        \  }}|r|| _        yy# t        $ r t        j                  dd       Y y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.honchors  ensure_fresh_tokenr9   r*   r   r8   r  r  )ro  rs  tokenr   s       r   _apply_fresh_oauth_tokenry    sZ    O/++,?,A6;;Oq"FN  O>NOs   6:  AAc                "   	 ddl m} ||j                  n	t               }|j	                  t               |      \  }}|r+|r(|j                  | |      st        j                          yyyy# t        $ r t        j                  dd       Y y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   rr  Nz"Honcho OAuth cached refresh failedTrt  )rv  rs  r*   rC   rw  r9   apply_token_to_clientr]  resetr8   r  r  )clientro  rs  r*   rx  	refresheds         r   _refresh_cached_oauthr    s    L/$0v{{6I6K 334G4I4Pyu'B'B65'Q%%' (R9 L;dKLs   A%A+ + BBc                d    t         j                         }|;t               }|t        k7  rt         j	                          dad}nt        |        |S  t        j                          t                 j                  s j                  st        d      d fd}t         j                  |      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.c                 b   	 ddl m} m}  |dd       	 ddlm} j                  }j                  }|r|	 ddl	m
}  |       }|j                  d	i       }t        |t              rR|s$|j                  d
d      j                         xs d }|*t        |j                  d      |j                  d            }|t         }|r"t"        j%                  d|j&                         n+t"        j%                  dj(                  j&                         t+        |      }|rjj,                  xs i }	|	j                  d      xs i j                  j(                  i       }
t/        |
j                  d            }|rj0                  nd}nj0                  }|r&dd l}|j5                  dd|      j7                  d      }j&                  |j8                  d}|r||d
<   |||d<   |a |di |S # t        $ r Y t        $ r Y w xY w# t        $ r t        d      w xY w# t        $ r Y sw xY w)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_configrb  r   r   r   rc  z8Initializing Honcho client (base_url: %s, workspace: %s)z4Initializing Honcho client (host: %s, workspace: %s)r#   r   localz/v\d+/*$/)r   r   r   rF   )tools.lazy_depsr  r  ImportErrorr8   rb  r   r   r   rd  r  r&   rj   rk   r   r   rn  r  infor   r*   r   r)   rV   r   rJ  rL  r@  r   r^  )r  _lazy_ensurer   resolved_base_urlresolved_timeoutr  
hermes_cfgre  	_is_local_rawr-   _host_has_keyeffective_api_key_rekwargsro  s                  r   _buildz!get_honcho_client.<locals>._build  s;   		R7	% #OO!>> $4$<9(]
'^^Hb9
j$/,,6NN:r,J,P,P,R,ZVZ)'/+B&NN95&NN+<=,( #4KKRTegmgzgz{KKNPVP[P[]c]p]pq ''89	
 ::#D88G,277RHK !:;M2?W &  #R9J K R RSV W #//(!--

 !2F:' 0F9 +u  	 	 	  	? 	2  s4   G. H	 BH! .	H:HH	H!	H.-H.)rT  'Honcho')r]  peekrp  r^  r|  r  r   r%  ry  r   r   r\   r&   )ro  cachednew_timeoutr  s   `   r   get_honcho_clientr    s     !%%'F 4F;/)%%'"OF!&&1M~#668 V$>>&//@
 	
_ B ""6**r!   c                 4    t         j                          daday)z7Reset the Honcho client singleton (useful for testing).NrU  )r]  r|  r^  r_  rF   r!   r   reset_honcho_clientr  S  s     O ,r!   )r   r   rT  r>   )r)   rk   r*   r>   rT  rk   )rT  r>   )rT  r   )rS   r>   rT  r>   )r   rV   rT  rV   )rT  r   )r   r[   rT  r[   )r   rg   rT  rg   )rm   rk   rn   rk   ro   r>   rT  r   )r   )
rm   rk   rn   rk   ro   r>   r   r>   rT  r>   )rT  r[   )r   r[   rT  r   )r   r   rT  rV   )r   r   rT  r   )r   r>   r   zdict | NonerT  rk   )rT  r   )ro  HonchoClientConfig | NonerT  rg   )ro  r   rT  None)r}  r  ro  r  rT  r  )N)ro  r  rT  r   )rT  r  )CrY  
__future__r   r;   r5   loggingr<  r   dataclassesr   r   pathlibr   urllib.parser   hermes_constantsr   r7   r	   plugins.plugin_utilsr
   typingr   r   rb  r   	getLoggerrV  r  r   r    r-   rC   rG   r9   rP   rQ   rT   rX   ra   rd   rh   rv   ry   r}   r   r   rn  r   r   r   r   r   r   r   r   r]  rZ  r^  r_  rf  rl  rp  ry  r  r  r  rF   r!   r   <module>r     s   #  	    (  ! , 8 . %			8	$.!#L3
(, ) 4 ;  >@

"
),
7:

 F 2  #L$ &}5 '0mVcd E  D
  T	 
  
> n! n! n!b &3_ ] 4 $ $ >J : I 8E*O L"J+Z-r!   