chore(honcho): replace example Telegram UID with placeholder

This commit is contained in:
Erosika 2026-06-11 15:06:07 -04:00
parent 2708c33c75
commit 1544813bfe
6 changed files with 78 additions and 78 deletions

View file

@ -138,8 +138,8 @@ In gateway deployments (Telegram, Discord, Slack, etc.) each user arrives with a
| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `pinUserPeer` | bool | `false` | When `true`, every gateway runtime user collapses to `peerName`. Single-operator deployments where you want all your platforms (and any other users) to share one peer |
| `userPeerAliases` | object | `{}` | Map of runtime IDs to peer IDs (`{"86701400": "alice"}`). Many-to-one is the intended pattern — alias all your runtime IDs to one peer name. One-to-many is not supported; one runtime ID resolves to exactly one peer |
| `runtimePeerPrefix` | string | `""` | Prepended to unknown runtime IDs to namespace them (e.g. `"telegram_"``telegram_86701400`). Used only when no alias matches. Prevents collisions between platforms whose runtime IDs share the same shape |
| `userPeerAliases` | object | `{}` | Map of runtime IDs to peer IDs (`{"7654321": "alice"}`). Many-to-one is the intended pattern — alias all your runtime IDs to one peer name. One-to-many is not supported; one runtime ID resolves to exactly one peer |
| `runtimePeerPrefix` | string | `""` | Prepended to unknown runtime IDs to namespace them (e.g. `"telegram_"``telegram_7654321`). Used only when no alias matches. Prevents collisions between platforms whose runtime IDs share the same shape |
> **Deprecated:** `pinPeerName` is a legacy alias for `pinUserPeer`, still read for back-compat (`pinUserPeer` wins where both are set). `hermes honcho setup` migrates it onto `pinUserPeer` on touch and never writes it.

View file

@ -413,7 +413,7 @@ def _collect_operator_aliases(existing: dict, peer_target: str) -> dict:
print(f"\n Add runtime IDs that should alias to peer '{peer_target}'.")
print(" Leave blank to skip a platform. Existing aliases are preserved.")
for platform_label, alias_hint in (
("Telegram UID", "e.g. 86701400"),
("Telegram UID", "e.g. 7654321"),
("Discord snowflake", "e.g. 491827364"),
("Slack user ID", "e.g. U04ABCDEF"),
("Matrix MXID", "e.g. @you:matrix.org"),

View file

@ -1466,7 +1466,7 @@ class TestAgentConfigSignatureUserId:
from gateway.run import GatewayRunner
runtime = {"provider": "anthropic", "api_key": "k", "base_url": "", "api_mode": "chat_completions"}
sig_a = GatewayRunner._agent_config_signature(
"claude-sonnet-4", runtime, ["hermes-telegram"], "", user_id="86701400"
"claude-sonnet-4", runtime, ["hermes-telegram"], "", user_id="7654321"
)
sig_b = GatewayRunner._agent_config_signature(
"claude-sonnet-4", runtime, ["hermes-telegram"], "", user_id="491827364"
@ -1477,10 +1477,10 @@ class TestAgentConfigSignatureUserId:
from gateway.run import GatewayRunner
runtime = {"provider": "anthropic", "api_key": "k", "base_url": "", "api_mode": "chat_completions"}
sig_1 = GatewayRunner._agent_config_signature(
"claude-sonnet-4", runtime, ["hermes-telegram"], "", user_id="86701400"
"claude-sonnet-4", runtime, ["hermes-telegram"], "", user_id="7654321"
)
sig_2 = GatewayRunner._agent_config_signature(
"claude-sonnet-4", runtime, ["hermes-telegram"], "", user_id="86701400"
"claude-sonnet-4", runtime, ["hermes-telegram"], "", user_id="7654321"
)
assert sig_1 == sig_2
@ -1489,11 +1489,11 @@ class TestAgentConfigSignatureUserId:
runtime = {"provider": "anthropic", "api_key": "k", "base_url": "", "api_mode": "chat_completions"}
sig_a = GatewayRunner._agent_config_signature(
"claude-sonnet-4", runtime, ["hermes-telegram"], "",
user_id="86701400", user_id_alt="@igor_tg",
user_id="7654321", user_id_alt="@igor_tg",
)
sig_b = GatewayRunner._agent_config_signature(
"claude-sonnet-4", runtime, ["hermes-telegram"], "",
user_id="86701400", user_id_alt="@erosika_tg",
user_id="7654321", user_id_alt="@erosika_tg",
)
assert sig_a != sig_b

View file

@ -263,7 +263,7 @@ class TestCloneHonchoForProfile:
"apiKey": "***",
"hosts": {
"hermes": {
"userPeerAliases": {"86701400": "eri", "discord-491827364": "eri"},
"userPeerAliases": {"7654321": "eri", "discord-491827364": "eri"},
"peerName": "eri",
},
},
@ -272,7 +272,7 @@ class TestCloneHonchoForProfile:
ok = honcho_cli.clone_honcho_for_profile("coder")
assert ok is True
new_block = written["cfg"]["hosts"]["hermes_coder"]
assert new_block["userPeerAliases"] == {"86701400": "eri", "discord-491827364": "eri"}
assert new_block["userPeerAliases"] == {"7654321": "eri", "discord-491827364": "eri"}
def test_runtime_peer_prefix_carries_into_cloned_profile(self, monkeypatch, tmp_path):
cfg = {
@ -447,7 +447,7 @@ class TestSetupWizardDeploymentShape:
"hermes", # workspace
"2", # tree: me + other people
"y", # keep my memory pooled? → hybrid
"86701400", # telegram uid
"7654321", # telegram uid
"491827364", # discord snowflake
"", # slack (skip)
"", # matrix (skip)
@ -456,7 +456,7 @@ class TestSetupWizardDeploymentShape:
host = self._run_setup(monkeypatch, tmp_path, answers=answers)
assert host["pinUserPeer"] is False
assert host["userPeerAliases"] == {
"86701400": "eri",
"7654321": "eri",
"491827364": "eri",
}
assert "runtimePeerPrefix" not in host
@ -498,7 +498,7 @@ class TestSetupWizardDeploymentShape:
"hermes", # workspace
"3", # tree: only others — triggers the orphan guard
"y", # pool my own memory instead? → hybrid
"86701400", # telegram uid
"7654321", # telegram uid
"", # discord (skip)
"", # slack (skip)
"", # matrix (skip)
@ -506,7 +506,7 @@ class TestSetupWizardDeploymentShape:
]
host = self._run_setup(monkeypatch, tmp_path, answers=answers, initial_cfg=initial_cfg)
assert host["pinUserPeer"] is False
assert host["userPeerAliases"] == {"86701400": "eri"}
assert host["userPeerAliases"] == {"7654321": "eri"}
def test_unpin_decline_steer_keeps_per_user(self, monkeypatch, tmp_path):
"""Operator can decline the steer ('n') and accept orphaning, ending
@ -575,7 +575,7 @@ class TestSetupWizardDeploymentShape:
"""
initial_cfg = {
"apiKey": "***",
"userPeerAliases": {"86701400": "eri"},
"userPeerAliases": {"7654321": "eri"},
"hosts": {"hermes": {"peerName": "eri"}},
}
answers = ["cloud", "", "eri", "hermetika", "hermes"]
@ -583,7 +583,7 @@ class TestSetupWizardDeploymentShape:
assert host["pinUserPeer"] is False
# Hybrid materialises the root aliases into the host so subsequent
# operator edits live on the host block they're inspecting.
assert host["userPeerAliases"] == {"86701400": "eri"}
assert host["userPeerAliases"] == {"7654321": "eri"}
def test_only_others_does_not_override_root_user_peer_aliases(self, monkeypatch, tmp_path):
"""Explicitly choosing 'only other people' must leave the host

View file

@ -105,7 +105,7 @@ class TestRuntimePeerMappingConfigParsing:
config_file.write_text(json.dumps({
"apiKey": "k",
"userPeerAliases": {
" 86701400 ": " Igor ",
" 7654321 ": " Igor ",
"": "ignored",
"empty-value": " ",
"null-value": None,
@ -115,7 +115,7 @@ class TestRuntimePeerMappingConfigParsing:
config = HonchoClientConfig.from_global_config(config_path=config_file)
assert config.user_peer_aliases == {"86701400": "Igor"}
assert config.user_peer_aliases == {"7654321": "Igor"}
assert config.runtime_peer_prefix == "telegram_"
def test_host_aliases_override_root_aliases_as_whole_map(self, tmp_path):
@ -226,12 +226,12 @@ class TestPeerResolutionOrder:
mgr = HonchoSessionManager(
honcho=MagicMock(),
config=self._config(peer_name="Igor", pin_peer_name=False),
runtime_user_peer_name="86701400", # e.g. Telegram UID
runtime_user_peer_name="7654321", # e.g. Telegram UID
)
_patch_manager_for_resolution_test(mgr)
session = mgr.get_or_create("telegram:86701400")
assert session.user_peer_id == "86701400", (
session = mgr.get_or_create("telegram:7654321")
assert session.user_peer_id == "7654321", (
"pin_peer_name=False is the multi-user default — the gateway's "
"platform-native user ID must win so each user gets their own "
"peer scope. If this regresses, every Telegram/Discord/Slack "
@ -245,14 +245,14 @@ class TestPeerResolutionOrder:
config=self._config(
peer_name="Igor",
pin_peer_name=False,
user_peer_aliases={"86701400": "Igor"},
user_peer_aliases={"7654321": "Igor"},
runtime_peer_prefix="telegram_",
),
runtime_user_peer_name="86701400",
runtime_user_peer_name="7654321",
)
_patch_manager_for_resolution_test(mgr)
session = mgr.get_or_create("telegram:86701400")
session = mgr.get_or_create("telegram:7654321")
assert session.user_peer_id == "Igor"
def test_unknown_runtime_id_uses_prefix(self):
@ -264,12 +264,12 @@ class TestPeerResolutionOrder:
pin_peer_name=False,
runtime_peer_prefix="telegram_",
),
runtime_user_peer_name="86701400",
runtime_user_peer_name="7654321",
)
_patch_manager_for_resolution_test(mgr)
session = mgr.get_or_create("telegram:86701400")
assert session.user_peer_id == "telegram_86701400"
session = mgr.get_or_create("telegram:7654321")
assert session.user_peer_id == "telegram_7654321"
def test_prefixed_runtime_id_hashes_when_sanitization_is_lossy(self):
"""Generated prefixed IDs avoid merges caused by lossy sanitization."""
@ -291,43 +291,43 @@ class TestPeerResolutionOrder:
def test_prefixed_runtime_id_hashes_when_it_collides_with_peer_name(self):
"""Unknown generated peers should not silently merge into peerName."""
raw_peer_id = "telegram_86701400"
raw_peer_id = "telegram_7654321"
expected_hash = hashlib.sha256(raw_peer_id.encode("utf-8")).hexdigest()[:8]
mgr = HonchoSessionManager(
honcho=MagicMock(),
config=self._config(
peer_name="telegram_86701400",
peer_name="telegram_7654321",
pin_peer_name=False,
runtime_peer_prefix="telegram_",
),
runtime_user_peer_name="86701400",
runtime_user_peer_name="7654321",
)
_patch_manager_for_resolution_test(mgr)
session = mgr.get_or_create("telegram:86701400")
assert session.user_peer_id == f"telegram_86701400-{expected_hash}"
session = mgr.get_or_create("telegram:7654321")
assert session.user_peer_id == f"telegram_7654321-{expected_hash}"
def test_prefixed_runtime_id_hashes_when_it_collides_with_alias_target(self):
"""Unknown generated peers should not silently merge into alias targets."""
raw_peer_id = "telegram_86701400"
raw_peer_id = "telegram_7654321"
expected_hash = hashlib.sha256(raw_peer_id.encode("utf-8")).hexdigest()[:8]
mgr = HonchoSessionManager(
honcho=MagicMock(),
config=self._config(
peer_name=None,
pin_peer_name=False,
user_peer_aliases={"known-user": "telegram_86701400"},
user_peer_aliases={"known-user": "telegram_7654321"},
runtime_peer_prefix="telegram_",
),
runtime_user_peer_name="86701400",
runtime_user_peer_name="7654321",
)
_patch_manager_for_resolution_test(mgr)
session = mgr.get_or_create("telegram:86701400")
assert session.user_peer_id == f"telegram_86701400-{expected_hash}"
session = mgr.get_or_create("telegram:7654321")
assert session.user_peer_id == f"telegram_7654321-{expected_hash}"
def test_prefixed_runtime_id_extends_hash_when_short_hash_collides(self):
raw_peer_id = "telegram_86701400"
raw_peer_id = "telegram_7654321"
digest = hashlib.sha256(raw_peer_id.encode("utf-8")).hexdigest()
mgr = HonchoSessionManager(
honcho=MagicMock(),
@ -335,17 +335,17 @@ class TestPeerResolutionOrder:
peer_name=None,
pin_peer_name=False,
user_peer_aliases={
"known-user": "telegram_86701400",
"reserved-user": f"telegram_86701400-{digest[:8]}",
"known-user": "telegram_7654321",
"reserved-user": f"telegram_7654321-{digest[:8]}",
},
runtime_peer_prefix="telegram_",
),
runtime_user_peer_name="86701400",
runtime_user_peer_name="7654321",
)
_patch_manager_for_resolution_test(mgr)
session = mgr.get_or_create("telegram:86701400")
assert session.user_peer_id == f"telegram_86701400-{digest[:12]}"
session = mgr.get_or_create("telegram:7654321")
assert session.user_peer_id == f"telegram_7654321-{digest[:12]}"
def test_alias_value_is_sanitized_after_selection(self):
mgr = HonchoSessionManager(
@ -353,13 +353,13 @@ class TestPeerResolutionOrder:
config=self._config(
peer_name=None,
pin_peer_name=False,
user_peer_aliases={"86701400": "Alice Smith!"},
user_peer_aliases={"7654321": "Alice Smith!"},
),
runtime_user_peer_name="86701400",
runtime_user_peer_name="7654321",
)
_patch_manager_for_resolution_test(mgr)
session = mgr.get_or_create("telegram:86701400")
session = mgr.get_or_create("telegram:7654321")
assert session.user_peer_id == "Alice-Smith-"
def test_alias_keys_match_raw_runtime_id_before_sanitization(self):
@ -391,13 +391,13 @@ class TestPeerResolutionOrder:
runtime_peer_prefix="telegram_",
session_peer_prefix=True,
),
runtime_user_peer_name="86701400",
runtime_user_peer_name="7654321",
)
_patch_manager_for_resolution_test(mgr)
session = mgr.get_or_create("telegram:86701400")
assert session.user_peer_id == "telegram_86701400"
assert session.honcho_session_id == "telegram-86701400"
session = mgr.get_or_create("telegram:7654321")
assert session.user_peer_id == "telegram_7654321"
assert session.honcho_session_id == "telegram-7654321"
def test_config_wins_when_pin_is_true(self):
"""With pin enabled, configured peer_name beats runtime ID."""
@ -406,14 +406,14 @@ class TestPeerResolutionOrder:
config=self._config(
peer_name="Igor",
pin_peer_name=True,
user_peer_aliases={"86701400": "Alias"},
user_peer_aliases={"7654321": "Alias"},
runtime_peer_prefix="telegram_",
),
runtime_user_peer_name="86701400", # Telegram pushes this in
runtime_user_peer_name="7654321", # Telegram pushes this in
)
_patch_manager_for_resolution_test(mgr)
session = mgr.get_or_create("telegram:86701400")
session = mgr.get_or_create("telegram:7654321")
assert session.user_peer_id == "Igor", (
"With pinPeerName=true the user's configured peer_name must "
"beat the platform-native runtime ID so memory stays unified "
@ -429,26 +429,26 @@ class TestPeerResolutionOrder:
config=self._config(
peer_name=None,
pin_peer_name=True,
user_peer_aliases={"86701400": "Igor"},
user_peer_aliases={"7654321": "Igor"},
runtime_peer_prefix="telegram_",
),
runtime_user_peer_name="86701400",
runtime_user_peer_name="7654321",
)
_patch_manager_for_resolution_test(mgr)
session = mgr.get_or_create("telegram:86701400")
session = mgr.get_or_create("telegram:7654321")
assert session.user_peer_id == "Igor"
def test_pin_noop_without_peer_name_or_mapping_preserves_runtime(self):
mgr = HonchoSessionManager(
honcho=MagicMock(),
config=self._config(peer_name=None, pin_peer_name=True),
runtime_user_peer_name="86701400",
runtime_user_peer_name="7654321",
)
_patch_manager_for_resolution_test(mgr)
session = mgr.get_or_create("telegram:86701400")
assert session.user_peer_id == "86701400"
session = mgr.get_or_create("telegram:7654321")
assert session.user_peer_id == "7654321"
def test_alt_runtime_id_can_match_alias_without_changing_raw_fallback(self):
"""Stable alternate IDs can map known users while primary ID fallback stays unchanged."""
@ -526,11 +526,11 @@ class TestPeerResolutionOrder:
mgr = HonchoSessionManager(
honcho=MagicMock(),
config=cfg,
runtime_user_peer_name="86701400",
runtime_user_peer_name="7654321",
)
_patch_manager_for_resolution_test(mgr)
session = mgr.get_or_create("telegram:86701400")
session = mgr.get_or_create("telegram:7654321")
assert session.user_peer_id == "Igor"
assert session.assistant_peer_id == "hermes-assistant"
@ -556,10 +556,10 @@ class TestCrossPlatformMemoryUnification:
mgr_telegram = HonchoSessionManager(
honcho=MagicMock(),
config=self._config_pinned(),
runtime_user_peer_name="86701400",
runtime_user_peer_name="7654321",
)
_patch_manager_for_resolution_test(mgr_telegram)
telegram_session = mgr_telegram.get_or_create("telegram:86701400")
telegram_session = mgr_telegram.get_or_create("telegram:7654321")
# Discord turn (separate manager instance — simulates a fresh
# platform-adapter invocation)
@ -701,20 +701,20 @@ class TestPinTransition:
pinned_mgr = HonchoSessionManager(
honcho=MagicMock(),
config=self._pinned(),
runtime_user_peer_name="86701400",
runtime_user_peer_name="7654321",
)
_patch_manager_for_resolution_test(pinned_mgr)
before = pinned_mgr.get_or_create("telegram:86701400")
before = pinned_mgr.get_or_create("telegram:7654321")
assert before.user_peer_id == "Igor"
unpinned_mgr = HonchoSessionManager(
honcho=MagicMock(),
config=self._unpinned(),
runtime_user_peer_name="86701400",
runtime_user_peer_name="7654321",
)
_patch_manager_for_resolution_test(unpinned_mgr)
after = unpinned_mgr.get_or_create("telegram:86701400")
assert after.user_peer_id == "86701400", (
after = unpinned_mgr.get_or_create("telegram:7654321")
assert after.user_peer_id == "7654321", (
"After flipping pinPeerName off, the same runtime ID must resolve "
"to its own peer — otherwise multi-user mode silently merges users."
)
@ -723,14 +723,14 @@ class TestPinTransition:
mgr = HonchoSessionManager(
honcho=MagicMock(),
config=self._pinned(),
runtime_user_peer_name="86701400",
runtime_user_peer_name="7654321",
)
_patch_manager_for_resolution_test(mgr)
first = mgr.get_or_create("telegram:86701400")
first = mgr.get_or_create("telegram:7654321")
assert first.user_peer_id == "Igor"
mgr._config = self._unpinned()
second = mgr.get_or_create("telegram:86701400")
second = mgr.get_or_create("telegram:7654321")
assert second.user_peer_id == "Igor", (
"The per-key session cache is keyed by session-key, not by "
"resolved peer. In-process flips don't invalidate it — the "
@ -764,7 +764,7 @@ class TestPinTransition:
cfg_path.write_text(json.dumps({
"apiKey": "k",
"peerName": "Igor",
"userPeerAliases": {"86701400": "Igor"},
"userPeerAliases": {"7654321": "Igor"},
}))
sig_with_aliases = GatewayRunner._extract_cache_busting_config({"memory": {"provider": "honcho"}})
@ -839,18 +839,18 @@ class TestProfilePeerUniqueness:
mgr_a = HonchoSessionManager(
honcho=MagicMock(),
config=self._pinned_to("alice"),
runtime_user_peer_name="86701400",
runtime_user_peer_name="7654321",
)
_patch_manager_for_resolution_test(mgr_a)
sess_a = mgr_a.get_or_create("telegram:86701400")
sess_a = mgr_a.get_or_create("telegram:7654321")
mgr_b = HonchoSessionManager(
honcho=MagicMock(),
config=self._pinned_to("bob"),
runtime_user_peer_name="86701400",
runtime_user_peer_name="7654321",
)
_patch_manager_for_resolution_test(mgr_b)
sess_b = mgr_b.get_or_create("telegram:86701400")
sess_b = mgr_b.get_or_create("telegram:7654321")
assert sess_a.user_peer_id == "alice"
assert sess_b.user_peer_id == "bob"

View file

@ -130,8 +130,8 @@ When pointing Hermes at a self-hosted Honcho server, `hermes honcho setup` (and
| `dialecticMaxInputChars` | `10000` | Max chars for dialectic query input to `peer.chat()` |
| `sessionStrategy` | `'per-directory'` | `per-directory`, `per-repo`, `per-session`, or `global` |
| `pinUserPeer` | `false` | Gateway only. When `true`, every platform user collapses to `peerName` |
| `userPeerAliases` | `{}` | Gateway only. Map of runtime IDs to peers (`{"86701400": "alice"}`). Many-to-one |
| `runtimePeerPrefix` | `""` | Gateway only. Namespaces unknown runtime IDs (`telegram_86701400`) when no alias matches |
| `userPeerAliases` | `{}` | Gateway only. Map of runtime IDs to peers (`{"7654321": "alice"}`). Many-to-one |
| `runtimePeerPrefix` | `""` | Gateway only. Namespaces unknown runtime IDs (`telegram_7654321`) when no alias matches |
**Session strategy** controls how Honcho sessions map to your work:
- `per-session` — each `hermes` run gets a fresh session. Clean starts, memory via tools. Recommended for new users.