mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-28 18:19:28 +00:00
fix(slack): preserve thread context for commands via channel_context
Prepending cold-start thread backfill directly onto the message text
moved a recognized command (e.g. a bang-normalized "!queue ...") away
from character zero, so downstream command routing misclassified it as
conversational text and the command silently didn't run.
Route the backfill through MessageEvent.channel_context instead —
gateway.run already prepends channel_context after command dispatch
("[New message]" framing), so commands keep their COMMAND type while
the recovered history stays available to the agent.
Supersedes #68020, which dropped the fetched context entirely for
commands instead of preserving it out-of-band.
Salvaged from #66069 by @LevSky22.
This commit is contained in:
parent
c8089dabcd
commit
fd433e046a
2 changed files with 57 additions and 1 deletions
|
|
@ -3825,6 +3825,12 @@ class SlackAdapter(BasePlatformAdapter):
|
|||
|
||||
# When entering a thread for the first time (no existing session),
|
||||
# fetch thread context so the agent understands the conversation.
|
||||
#
|
||||
# Keep recovered history separate from ``text``. Prepending it here
|
||||
# moves a recognized command away from character zero, so downstream
|
||||
# command routing can misclassify it as conversational text.
|
||||
# ``channel_context`` is prepended only after command dispatch.
|
||||
channel_context = None
|
||||
if is_thread_reply and not self._has_active_session_for_thread(
|
||||
channel_id=channel_id,
|
||||
thread_ts=event_thread_ts,
|
||||
|
|
@ -3838,7 +3844,7 @@ class SlackAdapter(BasePlatformAdapter):
|
|||
team_id=team_id,
|
||||
)
|
||||
if thread_context:
|
||||
text = thread_context + text
|
||||
channel_context = thread_context
|
||||
|
||||
# Determine message type
|
||||
msg_type = MessageType.TEXT
|
||||
|
|
@ -4176,6 +4182,7 @@ class SlackAdapter(BasePlatformAdapter):
|
|||
media_types=media_types,
|
||||
reply_to_message_id=thread_ts if thread_ts != ts else None,
|
||||
channel_prompt=_channel_prompt,
|
||||
channel_context=channel_context,
|
||||
reply_to_text=reply_to_text,
|
||||
auto_skill=_auto_skill,
|
||||
metadata={
|
||||
|
|
|
|||
|
|
@ -1483,6 +1483,55 @@ class TestBangPrefixCommands:
|
|||
# same thread.
|
||||
assert msg_event.source.thread_id == "1111111111.000001"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bang_queue_survives_first_thread_context_backfill(self, adapter):
|
||||
"""Backfill stays out of command text while remaining available."""
|
||||
adapter._has_active_session_for_thread = MagicMock(return_value=False)
|
||||
adapter._fetch_thread_context = AsyncMock(
|
||||
return_value=(
|
||||
"[Slack thread context — earlier messages]\n"
|
||||
"Alice: prior request\n"
|
||||
"[End of thread context]\n\n"
|
||||
)
|
||||
)
|
||||
adapter._fetch_thread_parent_text = AsyncMock(return_value="prior request")
|
||||
|
||||
evt = self._make_event(
|
||||
"!queue follow up after the current task",
|
||||
thread_ts="1111111111.000001",
|
||||
)
|
||||
await adapter._handle_slack_message(evt)
|
||||
|
||||
msg_event = adapter.handle_message.call_args[0][0]
|
||||
assert msg_event.text == "/queue follow up after the current task"
|
||||
assert msg_event.message_type == MessageType.COMMAND
|
||||
assert msg_event.get_command() == "queue"
|
||||
assert msg_event.get_command_args() == "follow up after the current task"
|
||||
assert msg_event.channel_context.startswith("[Slack thread context")
|
||||
assert "prior request" in msg_event.channel_context
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_non_command_thread_backfill_uses_channel_context(self, adapter):
|
||||
"""Normal thread text remains separate without losing its backfill."""
|
||||
adapter._has_active_session_for_thread = MagicMock(return_value=False)
|
||||
adapter._fetch_thread_context = AsyncMock(
|
||||
return_value="[Slack thread context]\nAlice: earlier note\n"
|
||||
)
|
||||
adapter._fetch_thread_parent_text = AsyncMock(return_value="earlier note")
|
||||
|
||||
evt = self._make_event(
|
||||
"follow up",
|
||||
thread_ts="1111111111.000001",
|
||||
)
|
||||
await adapter._handle_slack_message(evt)
|
||||
|
||||
msg_event = adapter.handle_message.call_args[0][0]
|
||||
assert msg_event.text == "follow up"
|
||||
assert msg_event.message_type == MessageType.TEXT
|
||||
assert msg_event.channel_context == (
|
||||
"[Slack thread context]\nAlice: earlier note\n"
|
||||
)
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bang_unknown_token_passes_through_unchanged(self, adapter):
|
||||
"""``!nice work`` is just a casual message — must NOT be rewritten."""
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue