feat(honcho): map bot authors onto their profile peer
The bot-mode dispatcher names another profile as bot:<profile>. The resolver treated that like a human runtime id, so a configured runtimePeerPrefix produced peers like telegram_bot:coder and a profile that already owns an AI peer in the same workspace got a second one. A bot:<profile> author now resolves in this order: a userPeerAliases entry for the full bot id, else the sanitized profile name. A cloned profile's aiPeer defaults to the profile name, so a same-workspace sender lands on its existing AI peer. Prefixes never apply to bot ids. pinUserPeer still collapses bot authors onto the pinned peer, the same as every other author.
This commit is contained in:
@@ -13,6 +13,8 @@ if TYPE_CHECKING:
|
||||
logger = logging.getLogger("plugins.memory.honcho.session")
|
||||
|
||||
_PEER_ID_HASH_ESCALATION_LENGTHS = (8, 12, 16, 24, 32, 64)
|
||||
# Author ids the bot-mode dispatcher assigns to other Hermes profiles (tools/bot_relay.py).
|
||||
BOT_AUTHOR_PREFIX = "bot:"
|
||||
|
||||
|
||||
class SessionPeersMixin:
|
||||
@@ -103,12 +105,18 @@ class SessionPeersMixin:
|
||||
|
||||
def _peer_id_for_runtime_id(self, runtime_id: str) -> str:
|
||||
"""Map one gateway runtime identity onto its Honcho peer ID with the same alias-then-prefix
|
||||
order ``_resolve_user_peer_id`` applies, so an aliased account lands on its peer on any turn."""
|
||||
order ``_resolve_user_peer_id`` applies, so an aliased account lands on its peer on any turn.
|
||||
A ``bot:<profile>`` author maps onto the profile name: a cloned profile's aiPeer defaults to
|
||||
that name, so a sender in the same workspace lands on its existing AI peer. Prefixes never
|
||||
apply to bot ids."""
|
||||
aliases = getattr(self._config, "user_peer_aliases", {}) if self._config else {}
|
||||
if isinstance(aliases, dict):
|
||||
alias = aliases.get(runtime_id)
|
||||
if isinstance(alias, str) and alias.strip():
|
||||
return self._sanitize_id(alias.strip())
|
||||
if runtime_id.startswith(BOT_AUTHOR_PREFIX):
|
||||
profile = runtime_id[len(BOT_AUTHOR_PREFIX):].strip()
|
||||
return self._sanitize_id(profile or runtime_id)
|
||||
prefix = getattr(self._config, "runtime_peer_prefix", "") if self._config else ""
|
||||
prefix = prefix.strip() if isinstance(prefix, str) else ""
|
||||
return self._generated_runtime_peer_id(prefix, runtime_id) if prefix else self._sanitize_id(runtime_id)
|
||||
|
||||
@@ -78,6 +78,31 @@ class TestResolveAuthorPeerId:
|
||||
mgr = _manager(_config(pin_peer_name=True), runtime_id="7654321")
|
||||
assert mgr.resolve_author_peer_id("telegram:group1", "111222") is None
|
||||
|
||||
def test_bot_author_lands_on_its_profile_peer(self):
|
||||
"""A cloned profile's aiPeer defaults to the profile name, so the sender reuses its AI peer."""
|
||||
mgr = _manager(_config(), runtime_id="7654321")
|
||||
assert mgr.resolve_author_peer_id("telegram:dm1", "bot:coder") == "coder"
|
||||
|
||||
def test_bot_author_alias_wins_over_the_profile_name(self):
|
||||
mgr = _manager(_config(user_peer_aliases={"bot:coder": "hermes-coder"}), runtime_id="7654321")
|
||||
assert mgr.resolve_author_peer_id("telegram:dm1", "bot:coder") == "hermes-coder"
|
||||
|
||||
def test_bot_author_ignores_the_runtime_prefix(self):
|
||||
mgr = _manager(_config(runtime_peer_prefix="telegram_"), runtime_id="7654321")
|
||||
assert mgr.resolve_author_peer_id("telegram:dm1", "bot:coder") == "coder"
|
||||
|
||||
def test_bot_author_profile_name_is_sanitized(self):
|
||||
mgr = _manager(_config(), runtime_id="7654321")
|
||||
assert mgr.resolve_author_peer_id("telegram:dm1", "bot:my profile.v2") == "my-profile-v2"
|
||||
|
||||
def test_bot_author_without_a_profile_keeps_the_raw_id(self):
|
||||
mgr = _manager(_config(), runtime_id="7654321")
|
||||
assert mgr.resolve_author_peer_id("telegram:dm1", "bot:") == "bot-"
|
||||
|
||||
def test_pin_peer_name_collapses_bot_authors_too(self):
|
||||
mgr = _manager(_config(pin_peer_name=True), runtime_id="7654321")
|
||||
assert mgr.resolve_author_peer_id("telegram:dm1", "bot:coder") is None
|
||||
|
||||
def test_display_name_never_becomes_a_peer_id(self):
|
||||
"""Display names are attacker-influenceable on most platforms."""
|
||||
mgr = _manager(_config(), runtime_id="7654321")
|
||||
|
||||
Reference in New Issue
Block a user