
    `gjB                        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	 ddl
mZmZmZ ddlmZ  ej                   e      Z e	e      j(                  ZdZddZdd	Zdd
ZddZddZddZddZddZddZ G d d      Z ddZ!ddZ"y) a  Memory provider plugin discovery.

Scans two directories for memory provider plugins:

1. Bundled providers: ``plugins/memory/<name>/`` (shipped with hermes-agent)
2. User-installed providers: ``$HERMES_HOME/plugins/<name>/``

Each subdirectory must contain ``__init__.py`` with a class implementing
the MemoryProvider ABC.  On name collisions, bundled providers take
precedence.

Only ONE provider can be active at a time, selected via
``memory.provider`` in config.yaml.

Usage:
    from plugins.memory import discover_memory_providers, load_memory_provider

    available = discover_memory_providers()   # [(name, desc, available), ...]
    provider = load_memory_provider("mnemosyne")  # MemoryProvider instance
    )annotationsN)Path)ListOptionalTuple)cfg_get_hermes_user_memoryc                    | t         j                  v ryt        j                  j	                  | dd      }||_        t        j                  j                  |      t         j                  | <   y)u  Register an empty package shell in sys.modules.

    User-installed providers import as ``_hermes_user_memory.<name>``, a
    dotted name whose parents exist nowhere on disk.  Unless those parents
    are present in ``sys.modules``, any relative import inside the plugin
    (``from . import config``) fails with
    ``ModuleNotFoundError: No module named '_hermes_user_memory'`` — the
    same reason the loader already registers ``plugins`` and
    ``plugins.memory`` for bundled providers.
    NT)
is_package)sysmodules	importlib	machinery
ModuleSpecsubmodule_search_locationsutilmodule_from_spec)namesearch_locationsspecs      J/root/.hermes/venv/lib/python3.12/site-packages/plugins/memory/__init__.py_register_synthetic_packager   *   sU     s{{))$)FD&6D#!77=CKK    c                 j    	 ddl m}   |        dz  }|j                         r|S dS # t        $ r Y yw xY w)z8Return ``$HERMES_HOME/plugins/`` or None if unavailable.r   )get_hermes_homepluginsN)hermes_constantsr   is_dir	Exception)r   ds     r   _get_user_plugins_dirr!   @   s<    4	)HHJq(D( s   !& & 	22c                    | dz  }|j                         sy	 |j                  d      dd }d|v xs d|v S # t        $ r Y yw xY w)	u   Heuristic: does *path* look like a memory provider plugin?

    Checks for ``register_memory_provider`` or ``MemoryProvider`` in the
    ``__init__.py`` source.  Cheap text scan — no import needed.
    __init__.pyFreplace)errorsNi    register_memory_providerMemoryProvider)exists	read_textr   )path	init_filesources      r   _is_memory_provider_dirr-   J   sd     }$I$$I$6u=)V3Q7G67QQ s   7 	AAc                    t               } g }t        j                         rt        t        j	                               D ]z  }|j                         r|j
                  j                  d      r/|dz  j                         sC| j                  |j
                         |j                  |j
                  |f       | t               }|rt        |j	                               D ]f  }|j                         r|j
                  j                  d      r/|j
                  | v r>t        |      sJ|j                  |j
                  |f       h |S )zYield ``(name, path)`` for all discovered provider directories.

    Scans bundled first, then user-installed.  Bundled takes precedence
    on name collisions (first-seen wins via ``seen`` set).
    )_.r#   )set_MEMORY_PLUGINS_DIRr   sortediterdirr   
startswithr(   addappendr!   r-   )seendirschilduser_dirs       r   _iter_provider_dirsr<   Z   s    D#%D !!#/779: 	-E<<>UZZ%:%::%FM)113HHUZZ KKU+,	- %&HH,,./ 	-E<<>UZZ%:%::%FzzT!*51KKU+,	- Kr   c                    t         | z  }|j                         r|dz  j                         r|S t               }|r"|| z  }|j                         rt	        |      r|S y)z^Resolve a provider name to its directory.

    Checks bundled first, then user-installed.
    r#   N)r2   r   r(   r!   r-   )r   bundledr;   users       r   find_provider_dirr@   |   sZ     "D(G~~W}4<<>$&H$;;=4T:Kr   c                 Z    t        t               D  ch c]  \  } }| 	 c}}       S c c}} w )u/  Cheap name-only listing of discoverable memory providers.

    Unlike :func:`discover_memory_providers`, this does NOT import provider
    modules or run availability checks — it's a directory scan only, safe to
    call at module-import time (e.g. when building the dashboard config
    schema).
    )r3   r<   )r   r/   s     r   list_memory_provider_namesrB      s%     ':'<=GD!4=>>=s   '
c                    g } t               D ]  \  }}d}|dz  }|j                         rB	 ddl}t        |d      5 }|j	                  |      xs i }ddd       j                  dd      }d}	 t        |      }	|	r|	j                         }nd	}| j                  |||f        | S # 1 sw Y   VxY w# t        $ r Y Rw xY w# t        $ r d	}Y @w xY w)
zScan bundled and user-installed directories for available providers.

    Returns list of (name, description, is_available) tuples.
    Bundled providers take precedence on name collisions.
     plugin.yamlr   N	utf-8-sigencodingdescriptionTF)
r<   r(   yamlopen	safe_loadgetr   _load_provider_from_diris_availabler7   )
resultsr   r:   desc	yaml_filerJ   fmeta	availableproviders
             r   discover_memory_providersrW      s     G*, 0eM)	)k: 3a>>!,2D3xxr2
 		.u5H$113	!	 	dI./104 N'3 3    	I	s:   B3B'B3/ C'B0	,B33	B?>B?CCc                    t        |       }|st        j                  d|        y	 t        |      }|r|S t        j	                  d|        y# t
        $ r!}t        j	                  d| |       Y d}~yd}~ww xY w)a'  Load and return a MemoryProvider instance by name.

    Checks both bundled (``plugins/memory/<name>/``) and user-installed
    (``$HERMES_HOME/plugins/<name>/``) directories.  Bundled takes
    precedence on name collisions.

    Returns None if the provider is not found or fails to load.
    z9Memory provider '%s' not found in bundled or user pluginsNz:Memory provider '%s' loaded but no provider instance foundz'Failed to load memory provider '%s': %s)r@   loggerdebugrN   warningr   )r   provider_dirrV   es       r   load_memory_providerr^      sr     %T*LPRVW*<8OSUYZ @$Js   A A 	A6A11A6c                L   | j                   }t        | j                  v xs | j                  t        k(  }|rd| n
t         d| }| dz  }|j                         syt        j                  j                  |      }|t        |dd      r|}n>dD ]  }|t        j                  vst        t              j                  }|dk(  r|j                  }|dz  }	|	j                         sVt        j                  j                  |t        |	      t        |      g      }
|
st        j                  j!                  |
      }|t        j                  |<   	 |
j"                  j%                  |        |st)        t        g        t        j                  j                  |t        |      t        |       g      }
|
syt        j                  j!                  |
      }|t        j                  |<   | j+                  d	      D ]  }|j                   dk(  r|j,                  }| d| }|t        j                  vs9t        j                  j                  |t        |            }|set        j                  j!                  |      }|t        j                  |<   	 |j"                  j%                  |        	 |
j"                  j%                  |       t5        |d      r5t7               }	 |j9                  |       |j:                  r|j:                  S 	 ddlm} tA        |      D ];  }t        ||d      }tC        |tD              s!tG        ||      s.||us3	  |       c S  y# t&        $ r Y w xY w# t&        $ r"}t.        j1                  d
||       Y d}~d}~ww xY w# t&        $ rA}t.        j1                  d||       t        j                  j3                  |d       Y d}~yd}~ww xY w# t&        $ r"}t.        j1                  d||       Y d}~d}~ww xY w# t&        $ r Y w xY w)u   Import a provider module and extract the MemoryProvider instance.

    The module must have either:
    - A register(ctx) function (plugin-style) — we simulate a ctx
    - A top-level class that extends MemoryProvider — we instantiate it
    plugins.memory.r0   r#   N__file__)r   zplugins.memoryr   )r   z*.pyzFailed to load submodule %s: %szFailed to exec_module %s: %sregisterzregister() failed for %s: %sr   )r'   )$r   r2   parentsparent_USER_NAMESPACEr(   r   r   rM   getattrr   ra   r   r   spec_from_file_locationstrr   loaderexec_moduler   r   globstemrY   rZ   pophasattr_ProviderCollectorrb   rV   agent.memory_providerr'   dir
isinstancetype
issubclass)r\   r   _is_bundledmodule_namer+   cachedmodrd   parent_pathparent_initr   
parent_modsub_filesub_namefull_sub_namesub_specsub_modr]   	collectorr'   	attr_nameattrs                         r   rN   rN      s    D &)=)==kATATXkAkK.9OD6*/ARRSTXSY?ZK},I
 [[__[)Fgfj$? 4 	!FS[[("8n33Y&"-"4"4K)M9%%'$>>AAK 0474D3E B D %.^^%D%DT%J
.8F+! KK33J?	!* '< ~~55Y(+L(9': 6 
 nn--d3#&K  %))&1 	ZH}}-}}H*m1XJ7MCKK/$>>AA!3x= 'nn==hGG18CKK.Z 33G<	Z"	KK##C( sJ&(		BLL#!! ))) " 5X 	sIt,tT"z$'G.v E  ) ! !F % Z%FWXYYZ
  	LL7aHKKOOK.	  	BLL7qAA	B  sl   MM-1N #(O( P	M*)M*-	N6NN	O%$7O  O%(	P1PP	P#"P#c                  .    e Zd ZdZd Zd Zd Zd Zd Zy)ro   zAFake plugin context that captures register_memory_provider calls.c                    d | _         y NrV   )selfs    r   __init__z_ProviderCollector.__init__M  s	    r   c                    || _         y r   r   )r   rV   s     r   r&   z+_ProviderCollector.register_memory_providerP  s	     r   c                     y r    r   argskwargss      r   register_toolz _ProviderCollector.register_toolT      r   c                     y r   r   r   s      r   register_hookz _ProviderCollector.register_hookW  r   r   c                     y r   r   r   s      r   register_cli_commandz'_ProviderCollector.register_cli_commandZ  r   r   N)	__name__
__module____qualname____doc__r   r&   r   r   r   r   r   r   ro   ro   J  s    K!r   ro   c                 ^    	 ddl m}   |        }t        |dd      xs dS # t        $ r Y yw xY w)u   Read the active memory provider name from config.yaml.

    Returns the provider name (e.g. ``"honcho"``) or None if no
    external provider is configured.  Lightweight — only reads config,
    no plugin loading.
    r   )load_configmemoryrV   N)hermes_cli.configr   r   r   )r   configs     r   _get_active_memory_providerr   ^  s7    1vx4<< s     	,,c            	        g } t         j                         s| S t               }|s| S t        |      }|s| S |dz  }|j	                         s| S t         |j
                  v xs |j                  t         k(  }|rd| dnt         d| d}	 |t        j                  v rt        j                  |   }n|s/t        t        g        t        t         d| t        |      g       t        j                  j                  |t        |            }|r|j                  s| S t        j                  j!                  |      }|t        j                  |<   |j                  j#                  |       t%        |dd      }t'        |      s| S d| d}	d	}
|d
z  }|j	                         rH	 ddl}t+        |d      5 }|j-                  |      xs i }ddd       j/                  dd	      }|r|}	|}
t%        || dd      xs t%        |dd      }| j3                  ||	|
|||d       | S # 1 sw Y   ZxY w# t0        $ r Y Pw xY w# t0        $ r"}t4        j7                  d||       Y d}~| S d}~ww xY w)u  Return CLI commands for the **active** memory plugin only.

    Only one memory provider can be active at a time (set via
    ``memory.provider`` in config.yaml).  This function reads that
    value and only loads CLI registration for the matching plugin.
    If no provider is active, no commands are registered.

    Looks for a ``register_cli(subparser)`` function in the active
    plugin's ``cli.py``.  Returns a list of at most one dict with
    keys: ``name``, ``help``, ``description``, ``setup_fn``,
    ``handler_fn``.

    This is a lightweight scan — it only imports ``cli.py``, not the
    full plugin module.  Safe to call during argparse setup before
    any provider is loaded.
    zcli.pyr`   z.clir0   register_cliNzManage z memory pluginrD   rE   r   rF   rG   rI   _commandhoncho_command)r   helprI   setup_fn
handler_fnpluginz-Failed to scan CLI for memory plugin '%s': %s)r2   r   r   r@   r(   rc   rd   re   r   r   r   rh   r   r   rg   ri   r   rj   rf   callablerJ   rK   rL   rM   r   r7   rY   rZ   )rP   active_provider
plugin_dircli_fileru   rv   cli_modr   r   	help_textrI   rR   rJ   rS   rT   rQ   r   r]   s                     r   discover_plugin_cli_commandsr   m  s   " G%%'13O #?3JH$H??%););;gz?P?PTg?gK=HOO#4D9Q`PaabcrbsswNxK:Z#++%kk+.G ,OR@+&'q(9:S_<M >>99S]D t{{nn55d;G'.CKK$KK##G,w=%N o.n=	.		)k: 3a>>!,2D3xxr2 $I"&K W(9&BDI >W&6= 	 	#&$$%
 	 N/3 3    ZDoWXYYNZsb   	BH9 A&H9  H9 H* /H H* %7H9 H'#H* *	H63H9 5H66H9 9	I$II$)r   rh   r   	List[str]returnNone)r   Optional[Path])r*   r   r   bool)r   zList[Tuple[str, Path]])r   rh   r   r   )r   r   )r   zList[Tuple[str, str, bool]])r   rh   r   Optional['MemoryProvider'])r\   r   r   r   )r   zOptional[str])r   z
List[dict])#r   
__future__r   r   importlib.machineryimportlib.utilloggingr   pathlibr   typingr   r   r   r   r   	getLoggerr   rY   ra   rd   r2   re   r   r!   r-   r<   r@   rB   rW   r^   rN   ro   r   r   r   r   r   <module>r      s   * #     
  ( ( %			8	$8n++  (>, D,?"J2l^ (`r   