hermes_cli.auth mirrors provider-plugin profiles into PROVIDER_REGISTRY
once, at import time, by iterating list_providers(). providers'
_discover_providers() sets its _discovered guard before importing the
plugin directories, so when a plugin's own imports pull hermes_cli.auth in
mid-discovery (e.g. a user plugin importing agent.credential_pool), the
import-time mirror sees a partial profile list and every plugin discovered
afterwards is dropped from the auth registry. resolve_provider() then
rejects those providers with "Unknown provider" even though
get_provider_profile() and resolve_provider_full() know them.
- auth.py: factor the mirror into idempotent sync_plugin_provider_registry()
(returns the number of newly mirrored profiles) and re-sync on a miss via
_registry_lookup() at the resolve_provider() gate, is_known_auth_provider(),
the get_auth_status() dispatch and the credential/status resolvers.
- providers/__init__.py: call back into hermes_cli.auth (through
sys.modules, never importing it, never raising) when discovery finishes
and on any post-discovery register_provider(), so direct
PROVIDER_REGISTRY readers stay correct too. Registrations *during*
discovery are deliberately not mirrored one by one.
- tests: subprocess end-to-end regression (sorted-first plugin imports
hermes_cli.auth, sorted-last plain plugin must still resolve) plus
in-process contract tests: partial snapshot reconciled at discovery end,
post-discovery registration mirrored, sync idempotent / never clobbers,
providers-side hook never imports hermes_cli.auth. All red on main.
Fixes#102123. Absorbs the in-process tests and never-raise hook shape
from #106361 (Finn763). Related: #21685, #69576, #94231.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AwbHcHDBh9vFRHSDj2W2FY