feat(discord): default history backfill on, expand to per-user + threads

Follow-up to snav's PR #25463 contribution: flip default to on, broaden
scope so backfill fires whenever require_mention gates the bot (not just
shared-session channels).

Why:
- The mention-gate creates a session-transcript gap regardless of whether
  the channel is shared or per-user. In per-user sessions, Alice's session
  is still missing other participants' messages and her own pre-mention
  messages — backfill fills both gaps.
- Threads naturally scope to thread-only history because discord.py's
  channel.history() on a thread returns only that thread's messages.
- DMs still skip — every DM triggers the bot, so the session transcript
  is already complete.

Changes:
- hermes_cli/config.py: discord.history_backfill default → true
- gateway/platforms/discord.py: drop the _is_shared gate, keep _is_dm
  skip and _needed_mention gate; env var DISCORD_HISTORY_BACKFILL
  default → 'true'
- cli-config.yaml.example + website docs: update defaults and prose;
  add the DISCORD_HISTORY_BACKFILL / _LIMIT env var rows that were
  documented in the PR description but missing from the env-var table
- tests/gateway/test_discord_free_response.py:
  - flip test_discord_per_user_channel_does_not_backfill →
    test_discord_per_user_channel_backfills_too (new behavior)
  - add test_discord_dm_does_not_backfill (DM skip is invariant)
  - give FakeThread a no-op history() so existing thread tests don't hit
    a fake discord.Forbidden when backfill now fires on threads too

Tests: 160/160 in target files; 400/400 across all tests/gateway/ -k discord.
This commit is contained in:
teknium1 2026-05-14 15:49:01 -07:00 committed by Teknium
parent e84fe483bc
commit 4abfb6bc24
5 changed files with 80 additions and 20 deletions

View file

@ -3613,7 +3613,7 @@ class DiscordAdapter(BasePlatformAdapter):
if isinstance(configured, str):
return configured.lower() not in ("false", "0", "no", "off")
return bool(configured)
return os.getenv("DISCORD_HISTORY_BACKFILL", "false").lower() in ("true", "1", "yes")
return os.getenv("DISCORD_HISTORY_BACKFILL", "true").lower() in ("true", "1", "yes")
def _discord_history_backfill_limit(self) -> int:
"""Return the max number of messages to scan backwards for context.
@ -4644,8 +4644,8 @@ class DiscordAdapter(BasePlatformAdapter):
# ── History backfill ─────────────────────────────────────────
# When require_mention is active, the bot only processes messages
# that @mention it. This means channel messages between bot turns
# are invisible to the session transcript. To recover that context,
# that @mention it. Messages in the channel between bot turns are
# invisible to the session transcript. To recover that context,
# fetch recent channel history and prepend it to the user message.
#
# The fetch window is: everything after the bot's last message in
@ -4653,9 +4653,14 @@ class DiscordAdapter(BasePlatformAdapter):
# cold start (no prior bot message found), fetch the last N messages
# and stop at the first self-message encountered.
#
# This only runs for shared sessions (group_sessions_per_user=False
# or shared threads) where multiple users contribute context the bot
# would otherwise miss.
# Threads naturally scope to thread-only history (channel.history()
# on a thread returns only that thread's messages). DMs are skipped
# because every DM message triggers the bot — there's no mention gap
# to fill; the session transcript already has everything.
#
# Per-user sessions also benefit: Alice's session is missing the
# other-channel-participants' context, and her own messages from
# before she mentioned the bot. Backfill fills that gap.
#
# Messages that arrive while the bot is processing (between trigger
# and response) are not captured — this is an accepted simplification
@ -4663,17 +4668,13 @@ class DiscordAdapter(BasePlatformAdapter):
_channel_context = None
_is_dm = isinstance(message.channel, discord.DMChannel)
if not _is_dm:
_is_shared = (
(is_thread and not self.config.extra.get("thread_sessions_per_user", False))
or (not is_thread and not self.config.extra.get("group_sessions_per_user", True))
)
_needed_mention = (
require_mention
and not is_free_channel
and not in_bot_thread
)
_backfill_enabled = self._discord_history_backfill()
if _is_shared and _needed_mention and _backfill_enabled:
if _needed_mention and _backfill_enabled:
_backfill_text = await self._fetch_channel_context(
message.channel, before=message,
)