@@ -871,6 +871,35 @@ class TurnRunner:
|
||||
session_id = getattr(agent, "session_id", None)
|
||||
source = ctx.source
|
||||
runner = self._runner
|
||||
# Native Discord only stamps auto-thread markers on the opening parent-channel event. A
|
||||
# title retry arrives as an ordinary in-thread event; when the agent was rebuilt, there
|
||||
# is no first-turn callback left to rename it. Recover only those immutable markers from
|
||||
# this same thread's persisted origin, while keeping the live source's transport owner.
|
||||
if (
|
||||
getattr(source, "platform", None) == Platform.DISCORD
|
||||
and getattr(source, "chat_type", None) == "thread"
|
||||
and getattr(source, "thread_id", None)
|
||||
and getattr(source, "delivered_via_upstream_relay", False) is not True
|
||||
and not runner._is_discord_auto_thread_lane(source)
|
||||
):
|
||||
with suppress(Exception):
|
||||
row = agent._session_db.get_session(session_id)
|
||||
origin = json.loads((row or {}).get("origin_json") or "{}")
|
||||
initial_name = origin.get("auto_thread_initial_name")
|
||||
if (
|
||||
origin.get("platform") == Platform.DISCORD.value
|
||||
and origin.get("chat_type") == "thread"
|
||||
and str(origin.get("thread_id") or "") == str(source.thread_id)
|
||||
and origin.get("auto_thread_created") is True
|
||||
and isinstance(initial_name, str)
|
||||
and initial_name.strip()
|
||||
):
|
||||
from gateway.session_identity import replace_source
|
||||
source = replace_source(
|
||||
source,
|
||||
auto_thread_created=True,
|
||||
auto_thread_initial_name=initial_name,
|
||||
)
|
||||
# Both lanes spend a rate-limited platform call per title, so they use the model's title
|
||||
# only (TitleCallback); renaming twice burns Discord's 2-per-10-min budget on a throwaway.
|
||||
# Relay Discord predicate is shape-only: whether the connector auto-threaded our reply is
|
||||
|
||||
@@ -9,6 +9,7 @@ ten minutes, so the throwaway can be the one that survives.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import types
|
||||
import weakref
|
||||
|
||||
@@ -58,6 +59,65 @@ def test_the_rename_waits_for_the_model_title(lane):
|
||||
assert renames == ["Fix flaky auth test"]
|
||||
|
||||
|
||||
def test_discord_title_retry_recovers_auto_thread_origin():
|
||||
"""A fresh agent on a later thread turn still wires the semantic rename."""
|
||||
scheduled = []
|
||||
current = SessionSource(
|
||||
platform=Platform.DISCORD,
|
||||
chat_id="thread-1",
|
||||
chat_type="thread",
|
||||
thread_id="thread-1",
|
||||
message_id="follow-up",
|
||||
)
|
||||
origin = SessionSource(
|
||||
platform=Platform.DISCORD,
|
||||
chat_id="thread-1",
|
||||
chat_type="thread",
|
||||
thread_id="thread-1",
|
||||
message_id="opening-message",
|
||||
auto_thread_created=True,
|
||||
auto_thread_initial_name="Opening words",
|
||||
)
|
||||
session_db = types.SimpleNamespace(
|
||||
get_session=lambda session_id: {
|
||||
"origin_json": json.dumps(origin.to_dict()),
|
||||
}
|
||||
)
|
||||
runner = types.SimpleNamespace(
|
||||
_is_telegram_topic_lane=lambda source: False,
|
||||
_is_discord_auto_thread_lane=lambda source: (
|
||||
source.platform == Platform.DISCORD
|
||||
and source.chat_type == "thread"
|
||||
and source.auto_thread_created is True
|
||||
and bool(source.thread_id)
|
||||
and bool(source.auto_thread_initial_name)
|
||||
),
|
||||
_is_relay_discord_channel_lane=lambda source: False,
|
||||
_schedule_discord_semantic_thread_rename=(
|
||||
lambda source, session_id, title: scheduled.append(
|
||||
(source, session_id, title)
|
||||
)
|
||||
),
|
||||
)
|
||||
holder = types.SimpleNamespace(
|
||||
_runner=runner,
|
||||
_attach_session_title_callback=TurnRunner._attach_session_title_callback,
|
||||
)
|
||||
agent = types.SimpleNamespace(session_id="sess-1", _session_db=session_db)
|
||||
|
||||
holder._attach_session_title_callback(
|
||||
holder, agent, types.SimpleNamespace(source=current)
|
||||
)
|
||||
agent._on_session_title("Recovered semantic title", "llm")
|
||||
|
||||
recovered, session_id, title = scheduled[0]
|
||||
assert recovered.message_id == "follow-up"
|
||||
assert recovered.auto_thread_created is True
|
||||
assert recovered.auto_thread_initial_name == "Opening words"
|
||||
assert session_id == "sess-1"
|
||||
assert title == "Recovered semantic title"
|
||||
|
||||
|
||||
@pytest.mark.anyio
|
||||
async def test_native_thread_rename_passes_only_the_initial_name_guard():
|
||||
"""The shared rename lane must honor the strict native adapter contract."""
|
||||
|
||||
Reference in New Issue
Block a user