Ë
    âß²j‡;  ã                  óœ  — d Z ddlmZ ddlZddlZddlmZ ddlm	Z	 ddl
mZmZmZ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mZmZmZmZmZ ddlm Z m!Z!  ejD                  d«      Z#dZ$dZ% e&h d£«      Z'd d„Z(d!d„Z)d"d„Z*d#d„Z+d$d„Z,ddddœ	 	 	 	 	 	 	 	 	 	 	 d%d„Z-d&d„Z.d'd„Z/ ed¬«       G d„ d«      «       Z0	 	 	 	 	 	 	 	 d(d„Z1y))zùPlugin discovery: directory scanning, entry-point manifests, and the enable/disable gate.

Split out of :mod:`hermes_cli.plugins`. Names that tests patch on the origin
(``get_bundled_plugins_dir``, ``_env_enabled``) are looked up lazily through it.
é    )ÚannotationsN)Ú	dataclass)ÚPath)ÚAnyÚDictÚListÚOptionalÚSet)Úget_hermes_home)Úcfg_get)ÚVALID_CAPABILITY_IDS)Úparse_declared_capabilities)ÚPluginManifestÚ_detect_kind_from_sourceÚmanifest_keyÚ_resolve_module_sourceÚparse_manifest_fileÚportable_plugin_manifest)ÚLEGACY_RELAY_PLUGIN_KEYSÚRELAY_PLUGINS_CONFIG_ENVzhermes_cli.pluginszhermes_agent.pluginsz hermes_agent.plugin_capabilities>   ú.kimi-pluginú.codex-pluginú.devin-pluginú.claude-pluginú.cursor-pluginc                óð   — t        | d«      rt        | j                  |¬«      «      S t        | t        «      rt        | j                  |g «      «      S | D �cg c]  }|j                  |k(  sŒ|‘Œ c}S c c}w )zCReturn one metadata entry-point group across supported Python APIs.Úselect)Úgroup)ÚhasattrÚlistr   Ú
isinstanceÚdictÚgetr   )Úentry_pointsr   Úeps      úA/home/ubuntu/.hermes/hermes-agent/hermes_cli/plugins_discovery.pyÚ_select_entry_point_groupr'   &   sf   € äˆ|˜XÔ&Ü�L×'Ñ'¨eÐ'Ó4Ó5Ð5Ü�,¤Ô%Ü�L×$Ñ$ U¨BÓ/Ó0Ð0Ø%Ö;�2¨¯©°UÓ):ŠBÒ;Ð;ùÒ;s   ÁA3Á,A3c                 ó¦  — g } 	 t         j                  j                  «       }t        |t        «      }t        |t
        «      }|D �]  }	 |D �ch c](  }|j                  |j                  k(  sŒ|j                  ’Œ* }}t        D �cg c]  }|j                  › d|› �|v sŒ|‘Œ }	}t        |dd«      }
t        |
dd«      }| j                  t        |j                  t        t        |
dd«      xs d«      |�t        |j!                  dd«      xs d«      ndd	|j                  |j                  t#        |j                  «      t%        |	|j                  «      ¬
«      «       �Œ | S # t        $ r"}t        j                  d|«       | cY d}~S d}~ww xY wc c}w c c}w # t        $ r-}t        j                  dt        |dd«      |«       Y d}~�Œ‡d}~ww xY w)a�  Return metadata-only manifests for installed entry-point plugins. Kind comes from an import-free source
    scan (memory/model providers route to their own discovery). Capabilities come from the companion
    ``hermes_agent.plugin_capabilities`` group (``<plugin-id>.<capability-id>`` entries pointing at the same
    object), so consent works without importing plugin code. Failures are isolated per entry point.zEntry-point scan failed: %sNú.ÚdistÚmetadataÚversionÚ ÚSummaryÚ
entrypoint)Únamer,   ÚdescriptionÚsourceÚpathÚkeyÚkindÚcapabilitiesz'Entry-point manifest for %r skipped: %sr0   ú?)Ú	importlibr+   r$   r'   ÚENTRY_POINTS_GROUPÚENTRY_POINT_CAPABILITIES_GROUPÚ	ExceptionÚloggerÚdebugÚvaluer0   r   ÚgetattrÚappendr   Ústrr#   Ú_classify_entrypoint_value_kindÚ_parse_declared_capabilities)Ú	manifestsÚepsÚ	group_epsÚcapability_epsÚexcr%   ÚdÚdeclaredÚcr6   r*   r+   s               r&   Údiscover_entrypoint_manifestsrL   /   s¦  € ð
 ')€IðÜ× Ñ ×-Ñ-Ó/ˆÜ-¨cÔ3EÓFˆ	Ü2°3Ô8VÓWˆð ó cˆð	cØ(6ÖN 1¸!¿'¹'ÀRÇXÁXÓ:M˜Ÿ›ÐNˆHÐNÜ';Ö\ !À"Ç'Á'ÀÈ!ÈAÈ3Ð?OÐS[Ò?[šAÐ\ˆLÐ\Ü˜2˜v tÓ,ˆDÜ˜t Z°Ó6ˆHØ×Ñœ^Ø—W‘W¤c¬'°$¸	À2Ó*FÒ*LÈ"Ó&MØFNÐFZœC §¡¨Y¸Ó ;Ò A¸rÔBÐ`bØ#¨"¯(©(¸¿¹Ü4°R·X±XÓ>Ü9¸,ÈÏÉÓPôö ðcð Ðøô% ò Ü�‰Ð2°CÔ8ØÕûðüò
 OùÚ\øô ò 	cÜ�L‰LÐBÄGÈBÐPVÐX[ÓD\Ð^a×bÒbûð	cús`   „>E" Á	FÁFÁ,FÁ:
FÂFÂFÂ!B<FÅ"	FÅ+FÆFÆFÆ
FÆ	GÆ#"GÇGc                ó²   — 	 t        | «      j                  dd«      d   j                  «       }|rt        t	        |«      «      ndxs dS # t
        $ r Y yw xY w)zWClassify an entry-point target by import-free source scan (unresolvable -> standalone).ú:é   r   NÚ
standalone)rA   ÚsplitÚstripr   r   r;   )r>   Úmodule_names     r&   rB   rB   N   sZ   € ðÜ˜%“j×&Ñ& s¨AÓ.¨qÑ1×7Ñ7Ó9ˆÙQ\Ô(Ô)?ÀÓ)LÔMÐbfÒwÐkwÐwøÜò Ùðús   ‚AA
 Á
	AÁAc                 ó´   — 	 ddl m}  t         | «       ddg ¬«      }t        |t        «      rt        |«      S t        «       S # t        $ r t        «       cY S w xY w)uM   Read ``plugins.disabled`` â€” a deny-list that wins over ``plugins.enabled``.r   ©Úload_configÚpluginsÚdisabled)Údefault©Úhermes_cli.configrV   r   r!   r    Úsetr;   )rV   rX   s     r&   Ú_get_disabled_pluginsr]   W   sL   € ðÝ1Ü™;›=¨)°ZÈÔLˆÜ *¨8´TÔ :Œs�8‹}ÐEÄÃÐEøÜò Ü‹uŠðús   ‚4A ·	A ÁAÁAc                 óŒ   — 	 ddl m}  t         | «       dd«      }t        |t        «      rt        |«      S dS # t        $ r Y yw xY w)zøRead the ``plugins.enabled`` allow-list (plugins are opt-in). ``None`` = key missing/malformed ("nothing
    enabled yet"; the first ``migrate_config`` run grandfathers installed user plugins); ``set()`` = explicitly
    empty; else the allow-list.r   rU   rW   ÚenabledNrZ   )rV   r_   s     r&   Ú_get_enabled_pluginsr`   a   sD   € ðÝ1Ü™+›-¨°IÓ>ˆÜ)¨'´4Ô8Œs�7‹|ÐB¸dÐBøÜò Ùðús   ‚27 µ7 ·	AÁAr-   )Ú
skip_namesÚprefixÚdepthc          
     ó�  — g }| j                  «       s|S 	 t        | j                  «       «      }|D �]†  }|j                  j                  d«      r2|j                  j                  d«      rt        j                  d|«       ŒQ|j                  t        v rt        j                  d|«       Œz	 |j                  «       r|dk(  r|r|j                  |v rŒ¡t        d„ |dz  |d	z  fD «       d«      }	|d
z  }
|
j                  «       xs |
j                  «       }|	�$t        |	|||«      }|€Œö|j                  |«       �Œ	|r	 |j                  t!        |||«      «       �Œ*|dk\  rt        j                  d|«       �ŒG|r|› d|j                  › �n|j                  }|j%                  t'        ||||dz   ¬«      «       �Œ‰ |S # t        $ r#}t        j                  d| |«       |cY d}~S d}~ww xY w# t        $ r"}t        j                  d||«       Y d}~�Œád}~ww xY w# t"        $ r"}t        j                  d|
|«       Y d}~�Œd}~ww xY w)aP  Read manifests under *path*: flat ``<root>/<name>/plugin.yaml`` (key ``name``) or category
    ``<root>/<cat>/<name>/plugin.yaml`` (key ``cat/name``; a manifest-less directory recurses one level, depth
    capped at two). *skip_names* ignores top-level names; portable ``plugin.json`` packages are accepted
    alongside YAML manifests.z&Failed to scan plugin directory %s: %sNÚ__zSkipping dunder plugin path %sz1Skipping %s (foreign-harness manifest convention)r   c              3  óB   K  — | ]  }|j                  «       sŒ|–— Œ y ­w©N)Úexists)Ú.0Úfs     r&   ú	<genexpr>z!scan_directory.<locals>.<genexpr>‹   s   è ø€ Ò!i¨Ð^_×^fÑ^fÕ^h¤!Ñ!iùs   ‚˜zplugin.yamlz
plugin.ymlzplugin.jsonz+Skipping unreadable plugin directory %s: %szFailed to parse %s: %srO   z/Skipping %s (no plugin.yaml, depth cap reached)ú/)rb   rc   )Úis_dirÚsortedÚiterdirÚOSErrorr<   Úwarningr0   Ú
startswithÚendswithr=   Ú_FOREIGN_HARNESS_MANIFEST_DIRSÚnextrh   Ú
is_symlinkr   r@   r   r;   ÚextendÚscan_directory)r3   r2   ra   rb   rc   rD   ÚchildrenrH   ÚchildÚmanifest_fileÚportable_fileÚhas_portableÚmanifestÚ
sub_prefixs                 r&   rx   rx   m   s'  € ð ')€IØ�;‰;Œ=ØÐðÜ˜$Ÿ,™,›.Ó)ˆð ó $`ˆð �:‰:× Ñ  Ô&¨5¯:©:×+>Ñ+>¸tÔ+DÜ�L‰LÐ9¸5ÔAØØ�:‰:Ô7Ñ7Ü�L‰LÐLÈeÔTØð
	Ø—<‘<”> e¨q¢j±ZÀEÇJÁJÐR\ÑD\ØÜ Ñ!i¨e°mÑ.CÀUÈ\ÑEYÐ-ZÔ!iÐkoÓpˆMØ! MÑ1ˆMØ(×/Ñ/Ó1ÒO°]×5MÑ5MÓ5OˆLð Ð$Ü*¨=¸%ÀÈÓPˆHØÑ#Ø× Ñ  Ö*ÙðMØ× Ñ Ô!9¸%ÀÈÓ!PÖQð �aŠZÜ�L‰LÐJÈEÖRá5;˜F˜8 1 U§Z¡Z LÑ1ÀÇÁˆJØ×Ñœ^¨E°6À*ÐTYÐ\]ÑT]Ô^Ö_ðI$`ðJ ÐøôQ ò Ü�‰Ð?ÀÀsÔKØÕûðûô* ò 	ô �N‰NÐHÈ%ÐQTÔUÝûð		ûô ò MÜ—‘Ð7¸È×LÒLûðMúsT   –F= Â,%G,ÃAG,Ä=HÆ=	G)ÇG$ÇG)Ç$G)Ç,	HÇ5HÈHÈ	IÈ#I É Ic                 óª  ‡— ddl m}  g Šddˆfd„}| j                  «       }t        j	                  d|«        |d|dh d£«       t        «       dz  }t        j	                  d	|«        |d
|d
«       | j                  d«      r<t        j                  «       dz  dz  }t        j	                  d|«        |d|d«       ‰S t        j	                  d«       ‰S )z÷Read directory manifests in full-discovery order (bundled top-level, bundled/platforms, user, opt-in
    project) without loading or mutating anything, so startup probes share the exact precedence/containment
    rules of the real discovery sweep.r   )rW   c                ó„   •— t        |||¬«      }t        j                  d| t        |«      «       ‰j	                  |«       y )N)ra   z  %s: %d manifest(s))rx   r<   r=   Úlenrw   )ÚlabelÚ	directoryr2   ra   ÚfoundrD   s        €r&   Ú_scanz*collect_directory_manifests.<locals>._scan«   s4   ø€ Ü˜y¨&¸ZÔHˆÜ�‰Ð+¨U´C¸³JÔ?Ø×Ñ˜Õó    zScanning bundled plugins: %szbundled (top-level)Úbundled>   úmodel-providersÚmemoryÚcontext_engineÚcron_providersrW   zScanning user plugins: %sÚuserÚHERMES_ENABLE_PROJECT_PLUGINSz.hermeszScanning project plugins: %sÚprojectzHProject plugins disabled (set HERMES_ENABLE_PROJECT_PLUGINS=1 to enable)rg   )
rƒ   rA   r„   r   r2   rA   ra   úOptional[Set[str]]ÚreturnÚNone)	Ú
hermes_clirW   Úget_bundled_plugins_dirr<   r=   r   Ú_env_enabledr   Úcwd)Ú_originr†   Úrepo_pluginsÚuser_dirÚproject_dirrD   s        @r&   Úcollect_directory_manifestsr›   ¤   sÈ   ø€ õ .Ø&(€Iö ð ×2Ñ2Ó4€LÜ
‡L�LÐ/°Ô>Ù	Ð
 ¨yÚ
KôMäÓ  9Ñ,€HÜ
‡L�LÐ,¨hÔ7Ù	ˆ&�(˜FÔ#Ø×ÑÐ;Ô<Ü—h‘h“j 9Ñ,¨yÑ8ˆÜ�‰Ð3°[ÔAÙˆi˜ iÔ0ð Ðô 	�‰Ð_Ô`ØÐr‡   c           	     ó.  — i }| D �]  }t        |«      }|j                  |«      }|�æ|j                  dk(  r×|j                  dv rÉ|j                  rt	        |j                  «      j
                  nd}|j                  rt	        |j                  «      j
                  nd}|r6|r4||k7  r/t        j                  d|j                  |j                  |||«       ŒÑt        j                  d||j                  |j                  |j                  «       |||<   �Œ |S )að  Later sources win on key collision (project > user > bundled): a same-named copy under
    ``~/.hermes/plugins/<name>`` is the documented way to override a bundled plugin, and is logged. A flat
    user/project manifest that claims a bundled key from a *differently named* directory is an impostor, not
    an override (``impostor_dir/plugin.yaml`` with ``name: kanban``): it is skipped with a warning so
    ``hermes plugins enable kanban`` never activates unrelated code under the bundled name.rˆ   >   r�   r�   r-   z§Ignoring %s plugin at %s: its manifest name '%s' is a bundled plugin's key but the directory is named '%s'; rename the directory to '%s' to override the bundled pluginz5Plugin '%s' at %s (%s) shadows the bundled copy at %s)	r   r#   r2   r3   r   r0   r<   rq   Úinfo)rD   Úwinnersr~   r4   ÚshadowedÚown_dirÚbundled_dirs          r&   Úresolve_manifest_winnersr¢   Ä   só   € ð *,€GØó  ˆÜ˜8Ó$ˆØ—;‘;˜sÓ#ˆØÐ H§O¡O°yÒ$@ÀXÇ_Á_ÐXkÑEkØ2:·-²-”d˜8Ÿ=™=Ó)×.Ò.ÀRˆGØ6>·m²mœ$˜xŸ}™}Ó-×2Ò2ÈˆKÙ™;¨7°kÒ+AÜ—‘ðkà—O‘O X§]¡]°C¸À+ôð
 Ü�K‰KÐOÐQTÐV^×VcÑVcØ Ÿ™¨¯©ô8àˆ�‹ð ð  €Nr‡   T)Úfrozenc                  óF   — e Zd ZU dZded<   dZded<   dZded	<   dZd
ed<   y)ÚManifestGatezERouting verdict for one winning manifest (see :func:`gate_manifest`).rA   ÚactionFÚboolr_   NúOptional[str]ÚerrorzOptional[tuple]Úlog)Ú__name__Ú
__module__Ú__qualname__Ú__doc__Ú__annotations__r_   r©   rª   © r‡   r&   r¥   r¥   Þ   s(   … áOàƒKØ€GˆTÓØ€Eˆ=ÓØ€CˆÔr‡   r¥   c                ó  ‡— t        | «      Š‰| j                  h}ddœdˆfd„}|t        z  r#dt        › d�} ||t        j
                  d|«      S ||z  r |dt        j                  d«      S | j                  d	k(  r |d
t        j                  d«      S | j                  dk(  r |dt        j                  dd¬«      S | j                  dk(  r4| j                  dk(  rt        d«      S | j                  dk(  rt        d«      S |�||z  s |d‰› d�t        j                  d«      S | j                  dk7  rQddl
m}  || j                  | j                  «      }|�,d|j                  xs d› �} ||t        j
                  d|«      S t        d«      S )a  Decide how one winning manifest is handled. Gate order matters: legacy relay refusal, explicit disable,
    category-owned kinds (exclusive / model-provider), bundled auto-loads (backend now, platform deferred),
    then ``plugins.enabled`` opt-in (path-derived key or legacy bare name).F)r_   c               ó.   •— t        d|| ||‰g|¢­¬«      S )NÚplaceholder)r_   r©   rª   )r¥   )r©   ÚlevelÚmessager_   ÚargsÚ
lookup_keys        €r&   Ú_placeholderz#gate_manifest.<locals>._placeholderñ   s#   ø€ Ü˜M°7À%ÈeÐU\Ð^hÐMpÐkoÑMpÔqÐqr‡   u?   removed â€” Relay lifecycle is owned by Hermes core; configure z insteadz5Refusing to load removed Hermes Relay plugin '%s'; %szdisabled via configzSkipping disabled plugin '%s'Ú	exclusiveu<   exclusive plugin â€” activate via <category>.provider configz8Skipping '%s' (exclusive, handled by category discovery)zmodel-providerNz?Skipping '%s' (model-provider, handled by providers/ discovery)Trˆ   ÚbackendÚload_nowÚplatformÚdeferz2not enabled in config (run `hermes plugins enable z` to activate)z&Skipping '%s' (not in plugins.enabled)r   )Úinstalled_plugin_removalz(removed from the Hermes plugin catalog: zno reason recordedu@   Refusing to load plugin '%s' â€” %s; run `hermes plugins remove`Úload)
r©   r¨   r´   Úintrµ   rA   r_   r§   r‘   r¥   )r   r0   r   r   ÚloggingÚWARNINGÚDEBUGr5   r2   r¥   Úhermes_cli.plugins_cmd_catalogr¾   r3   Úreason)	r~   rX   r_   Únamesr¸   r©   r¾   Úremovedr·   s	           @r&   Úgate_manifestrÈ   è   s¥  ø€ ô ˜hÓ'€JØ˜Ÿ™Ð'€Eà\a÷ rð Ô'Ò'àMÜ'Ð(¨ð2ð 	ñ ˜E¤7§?¡?Ð4kÐmrÓsÐsØˆxÒÙÐ1´7·=±=ÐBaÓbÐbà‡}�}˜Ò#ÙØJÌGÏMÉMØFó
ð 	
ð ‡}�}Ð(Ò(ÙØ”'—-‘-Ð!bÐlpôrð 	rà‡�˜)Ò#à�=‰=˜IÒ%Ü 
Ó+Ð+ð �=‰=˜JÒ&Ü Ó(Ð(Ø€˜e gšoÙØ@ÀÀÈNÐ[Ô]d×]jÑ]jØ4ó
ð 	
ð ‡�˜)Ò#õ 	LÙ*¨8¯=©=¸(¿-¹-ÓHˆØÐØ>¸w¿~¹~Ò?eÐQeÐ>fÐgˆEÙ ¤w§¡Ð8zØ %ó'ð 'ä˜ÓÐr‡   )r$   r   r   rA   r‘   r    )r‘   zList['PluginManifest'])r>   rA   r‘   rA   )r‘   r\   )r‘   zOptional[set])r3   r   r2   rA   ra   r�   rb   rA   rc   rÀ   r‘   úList[PluginManifest])r‘   rÉ   )rD   rÉ   r‘   zDict[str, PluginManifest])r~   r   rX   zSet[str]r_   r�   r‘   r¥   )2r®   Ú
__future__r   Úimportlib.metadatar8   rÁ   Údataclassesr   Úpathlibr   Útypingr   r   r   r	   r
   Úhermes_constantsr   r[   r   Úhermes_cli.plugin_capabilitiesr   r   rC   Úhermes_cli.plugins_manifestr   r   r   r   r   r   Úhermes_cli.relay_plugin_cutoverr   r   Ú	getLoggerr<   r9   r:   Ú	frozensetrt   r'   rL   rB   r]   r`   rx   r›   r¢   r¥   rÈ   r°   r‡   r&   ú<module>rÕ      s  ðñõ #ã Û Ý !Ý ß 1Õ 1å ,Ý %Ý ?Ý f÷÷ ÷ _à	ˆ×	Ñ	Ð/Ó	0€à+Ð Ø!CÐ ñ
 "+ò ,ó "Ð ó
<óó>óó	ð BFÐUWÐfgñ4Ø
ð4Øð4Ø,>ð4ØORð4Ø`cð4àó4ónó@ñ4 �$Ô÷ ð  ó ð ð6 Øð6 Ø(0ð6 Ø;Mð6 àô6 r‡   