fix(session-titles): resolve the canonical Bot Chat by exact title, never a "#N" sibling
`SessionDB.resolve_session_by_title()` prefers a "`<title> #N`" continuation over the exact match. For the canonical Bot Chat that preference is wrong: Bot Mode identifies that session BY its exact title, and this lookup is the only one every DM transport shares (`hermes -p <bot> chat --in ~ -c "Bot Chat"` — used by `message_agent`, `bot_relay` and cron delivery). Once a numbered sibling exists (a Desktop branch of the canonical chat, `title_source='derived'`), every teammate DM resolves to that sibling: a visible, unmanaged session where the `message_agent` gate is off, so the addressed bot cannot answer and the sender gets silence instead of an error. Return the exact match when the requested title IS the canonical Bot Chat title; every other title keeps the "newest continuation wins" behaviour untouched. (cherry picked from commit e92638b4c53c97c48db4d9bbb2d3b6b580d289a7)
This commit is contained in:
@@ -167,6 +167,15 @@ class SessionTitlesMixin:
|
|||||||
def resolve_session_by_title(self, title: str) -> Optional[str]:
|
def resolve_session_by_title(self, title: str) -> Optional[str]:
|
||||||
"""Resolve a title to a session ID, preferring the latest "title #N" continuation."""
|
"""Resolve a title to a session ID, preferring the latest "title #N" continuation."""
|
||||||
exact = self.get_session_by_title(title)
|
exact = self.get_session_by_title(title)
|
||||||
|
# Exception to the "#N continuation" preference: the canonical Bot Chat's identity
|
||||||
|
# IS its exact title (Bot Mode re-resolves it by name on every open, no id pointer).
|
||||||
|
# A "<title> #N" sibling — a Desktop branch or a client-minted numbered row — is NOT
|
||||||
|
# a Bot Mode session: it is visible, unmanaged, and the message_agent gate is off in
|
||||||
|
# it. Every DM transport (``hermes -p <bot> chat --in ~ -c "Bot Chat"``: message_agent,
|
||||||
|
# bot_relay, cron delivery) resolves through here, so letting the numbered row win
|
||||||
|
# silently routes teammates' messages into a chat whose bot cannot answer back.
|
||||||
|
if exact is not None and title == self.CANONICAL_BOT_CHAT_TITLE:
|
||||||
|
return exact["id"]
|
||||||
# Escape LIKE wildcards so "%"/"_" in titles cannot false-match.
|
# Escape LIKE wildcards so "%"/"_" in titles cannot false-match.
|
||||||
numbered = self._read_all(
|
numbered = self._read_all(
|
||||||
"SELECT id, title, started_at FROM sessions "
|
"SELECT id, title, started_at FROM sessions "
|
||||||
|
|||||||
@@ -142,3 +142,35 @@ def test_auto_titler_can_rename_visible_derived_bot_chat(db):
|
|||||||
source=SessionDB.TITLE_SOURCE_LLM,
|
source=SessionDB.TITLE_SOURCE_LLM,
|
||||||
)
|
)
|
||||||
assert db.get_session("visible")["title"] == "Renamed by titler"
|
assert db.get_session("visible")["title"] == "Renamed by titler"
|
||||||
|
|
||||||
|
|
||||||
|
def test_numbered_branch_never_shadows_the_canonical_chat(db):
|
||||||
|
"""Title resolution must land on the canonical Bot Chat, never on a "#N" sibling.
|
||||||
|
|
||||||
|
Every DM transport resolves the target bot by that exact name
|
||||||
|
(``hermes -p <bot> chat --in ~ -c "Bot Chat"``: message_agent, bot_relay, cron
|
||||||
|
delivery). A numbered sibling — a Desktop branch, which is visible and NOT
|
||||||
|
Bot-Mode-managed — would otherwise swallow each teammate's message into a
|
||||||
|
session whose bot has no message_agent and cannot answer.
|
||||||
|
"""
|
||||||
|
canonical = _make_canonical(db, "forever")
|
||||||
|
db.create_session("branch", source="desktop", parent_session_id=canonical)
|
||||||
|
assert db._set_session_title(
|
||||||
|
"branch",
|
||||||
|
f"{SessionDB.CANONICAL_BOT_CHAT_TITLE} #2",
|
||||||
|
source=SessionDB.TITLE_SOURCE_DERIVED,
|
||||||
|
)
|
||||||
|
assert db.set_session_hidden("branch", False)
|
||||||
|
|
||||||
|
assert db.resolve_session_by_title(SessionDB.CANONICAL_BOT_CHAT_TITLE) == canonical
|
||||||
|
|
||||||
|
|
||||||
|
def test_numbered_continuation_still_wins_for_ordinary_titles(db):
|
||||||
|
# Control: the "#N continuation" preference is the point of the lookup — it
|
||||||
|
# stays intact for every non-Bot-Chat title.
|
||||||
|
db.create_session("plain", source="cli")
|
||||||
|
assert db.set_session_title("plain", "My session")
|
||||||
|
db.create_session("plain2", source="cli")
|
||||||
|
assert db.set_session_title("plain2", "My session #2")
|
||||||
|
|
||||||
|
assert db.resolve_session_by_title("My session") == "plain2"
|
||||||
|
|||||||
Reference in New Issue
Block a user