Skip to content

feat(discord): periodic thread retitle on top of upstream semantic titles - #29983

Open
TheoLong wants to merge 1 commit into
NousResearch:mainfrom
TheoLong:upstream/discord-auto-rename-threads
Open

feat(discord): periodic thread retitle on top of upstream semantic titles#29983
TheoLong wants to merge 1 commit into
NousResearch:mainfrom
TheoLong:upstream/discord-auto-rename-threads

Conversation

@TheoLong

Copy link
Copy Markdown

Problem

Discord threads default to the originating message's first line — rarely a good title for a multi-turn agent session.

Change

Rename the thread to the auto-generated session title after the first exchange, and again periodically as the conversation evolves. New periodic retitle helper added to agent/title_generator.py; wired in via gateway/run.py.

Trade-off / opt-out

I noticed #28… feat(telegram): add disable_topic_auto_rename gateway flag just landed for the Telegram side — users want to opt out of unsolicited renames when they've named topics by hand.

This Discord change is the mirror image and the same opt-out concern applies. Happy to add gateway.platforms.discord.extra.disable_thread_auto_rename (default False, preserving the behaviour in this PR) as a follow-up commit on this branch if maintainers want it baked in before merge.

Risk

Medium. Behaviour change visible to every Discord user. Opt-out flag recommended before merge.

@alt-glitch alt-glitch added type/feature New feature or request P3 Low — cosmetic, nice to have platform/discord Discord bot adapter comp/gateway Gateway runner, session dispatch, delivery labels May 21, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Note: competing open PR #15757 implements Discord thread name syncing. Prior closed attempts: #13915, #16059. Consider adding opt-out flag (gateway.platforms.discord.extra.disable_thread_auto_rename) mirroring the Telegram disable_topic_auto_rename from #28986.

@TheoLong
TheoLong force-pushed the upstream/discord-auto-rename-threads branch from 8bdeed8 to 9b0820b Compare June 8, 2026 06:51
@TheoLong

TheoLong commented Jun 8, 2026

Copy link
Copy Markdown
Author

Rebased on current main (1,824 commits absorbed). Added the opt-out flag requested in the triage commentgateway.platforms.discord.extra.disable_thread_auto_rename, mirroring the Telegram disable_topic_auto_rename pattern from #28986. Default False preserves prior behaviour; operators who manage thread names manually can opt out without disabling the feature globally.

Re competing PR #15757 and prior closes (#13915, #16059): also happy to defer. Bumping for review.

@TheoLong
TheoLong force-pushed the upstream/discord-auto-rename-threads branch from 9b0820b to 09a5fca Compare June 26, 2026 07:29
@TheoLong

Copy link
Copy Markdown
Author

Rebased on current main (630 commits since the last bump). Clean one-commit replay; agent/title_generator.py + gateway/run.py parse clean.

The opt-out flag the triage bot asked for is already in this branch — gateway.platforms.discord.extra.disable_thread_auto_rename, mirroring the Telegram disable_topic_auto_rename pattern from #28986. Default False preserves prior behaviour.

On overlap with what's on main: current main names the CLI→Discord handoff thread at creation (thread_name = f"Hermes — {cli_title}" in gateway/run.py), and #3f3d8a7b2 cleaned up mention-stripping in _auto_create_thread names. Neither covers what this PR does — renaming an existing conversation thread from the evolving session title, plus the periodic retitle as the session's topic drifts. Those two pieces have no equivalent on main, so this stays a real gap rather than a duplicate.

Re competing #15757 and prior closes (#13915, #16059): happy to defer if a maintainer prefers one of those. Bumping for review.

@TheoLong
TheoLong force-pushed the upstream/discord-auto-rename-threads branch from 09a5fca to 32542c7 Compare July 6, 2026 02:49
@TheoLong

TheoLong commented Jul 6, 2026

Copy link
Copy Markdown
Author

Rebased on current main (1,575 commits absorbed since the last bump). Clean one-commit replay; agent/title_generator.py + gateway/run.py parse clean, 40/40 in tests/agent/test_title_generator.py.

The opt-out flag the triage bot asked for is already in this branch — gateway.platforms.discord.extra.disable_thread_auto_rename, mirroring the Telegram disable_topic_auto_rename pattern from #28986. Default False preserves prior behaviour.

No overlap with 3b739b990 (strip <think> blocks from title output) which also touches title_generator.py — that scrubs generate_title's raw output, whereas this carry adds the periodic-retitle path (maybe_retitle_session → whole-conversation reassessment via _condense_history, no-op when the durable topic hasn't drifted). Different functions, clean replay.

Re competing PR #15757 (thread name syncing) that automation flagged: happy to defer if a maintainer prefers it; this version additionally handles periodic drift (re-evaluates every N turns), not just the first-turn rename.

@TheoLong
TheoLong force-pushed the upstream/discord-auto-rename-threads branch from 32542c7 to c929160 Compare July 6, 2026 05:49

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for carrying the periodic-retitle portion forward. Current main already landed the first-title Discord rename in 0d9ed9214 / v2026.7.7, but the periodic behavior remains distinct.

Problems

  • gateway/run.py:3498-3511 accepts every Discord thread, and gateway/run.py:13433-13445 then directly edits any differing name. This regresses main's intentional human-name protection: gateway/session.py:185-191 records auto-thread provenance, and tests/gateway/test_discord_slash_commands.py:714-729 verifies a human rename is not overwritten.
  • gateway/run.py:13398 truncates by Python code points. Discord's limit is UTF-16 units; current main fixed this in 1deeaf71a, using UTF-16-aware truncation at gateway/run.py:13657-13669.
  • agent/title_generator.py:288-301 omits strip_think_blocks, unlike the existing title path at agent/title_generator.py:90-106; periodic retitles can therefore persist reasoning markup.

Suggested changes

  • Build periodic retitling on main's guarded DiscordAdapter.rename_thread path, preserving manual renames and auto-thread provenance.
  • Reuse the UTF-16 sanitizer and canonical think-block scrubber.
  • Add gateway coverage for periodic scheduling, human/pre-existing thread preservation, and UTF-16 titles.

This is an automated hermes-sweeper review.

Comment thread gateway/run.py Outdated
cleaned = " ".join(cleaned.split())
if not cleaned:
return cleaned
return cleaned[: self._DISCORD_THREAD_NAME_MAX]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Discord applies its thread-name limit in UTF-16 code units, so this code-point slice can still exceed the API limit for emoji/non-BMP titles. Please reuse main's UTF-16-aware sanitizer (_prefix_within_utf16_limit); this was fixed in 1deeaf71a.

Comment thread gateway/run.py Outdated
edit = getattr(channel, "edit", None)
if not callable(edit):
return
await edit(name=new_name, reason="Hermes auto-title")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This edits any thread whose name differs from the generated title, including a human-renamed or pre-existing thread. Main deliberately preserves the initial auto-thread name in SessionSource and uses DiscordAdapter.rename_thread(..., only_if_current_name=...); please retain equivalent ownership protection here.

Comment thread agent/title_generator.py
timeout=timeout,
main_runtime=main_runtime,
)
title = (response.choices[0].message.content or "").strip()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sanitize reasoning output with the same strip_think_blocks helper used by generate_title before validating the title. Otherwise a periodic retitle from a think-enabled model can persist `` content into the session and Discord thread name.

@teknium1 teknium1 added sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 13, 2026
@TheoLong
TheoLong force-pushed the upstream/discord-auto-rename-threads branch from c929160 to 5e3f10d Compare July 18, 2026 06:44
@TheoLong

Copy link
Copy Markdown
Author

Rebased on current main (c48d534; 955 commits absorbed). One additive conflict this cycle: main reworked agent/title_generator.py (atomic auto-title write, manual-title-race guard, disabled-config honoring, #50535–50537). This PR's _condense_history / _looks_like_title / regenerate_title are independent symbols — concatenated cleanly, 74 title tests pass with no cascade. The prose-rejection guard (_looks_like_title) remains the distinctive delta.

…tles

Upstream 0d9ed92 ("Add semantic titles for Discord auto-threads") now
does the first-turn Discord thread rename that the original carry
implemented in gateway/run.py, so that half is dropped in favor of
upstream's UTF-16-aware _sanitize_discord_thread_title +
_schedule_discord_semantic_thread_rename.

This carry now contributes ONLY the residual delta upstream still lacks:

1. agent/title_generator.py: maybe_retitle_session — re-evaluates the
   session title every few user turns after the initial auto-title and
   fires the existing title_callback only when the topic genuinely
   drifts. Includes regenerate_title (sticky whole-conversation
   re-assessment) and the _looks_like_title guard, which REJECTS
   prose-shaped model output (>10 words / >80 chars / mid-sentence break)
   instead of truncating it into a junk title.

2. gateway/run.py: the periodic call site reuses upstream's semantic
   rename callback via the shared maybe_auto_title_kwargs, so Discord
   threads get renamed on drift with no duplicate rename plumbing.

The carry's own first-turn helpers (_is_discord_thread_lane,
_schedule_discord_thread_rename, _rename_discord_thread_for_session_title,
_sanitize_discord_thread_name) are removed as dead code superseded by
upstream.

Reconstructed on top of upstream during the 2026-07-10 sync.

(cherry picked from commit e4bc33a)
(cherry picked from commit d495f33)
(cherry picked from commit 45d7d350294bd64a7b458b5548ea00cdd462437f)
@TheoLong
TheoLong force-pushed the upstream/discord-auto-rename-threads branch from 5e3f10d to 25e4cd8 Compare September 2, 2026 20:28
@TheoLong

TheoLong commented Sep 2, 2026

Copy link
Copy Markdown
Author

Rebased onto current main (~3,938 commits absorbed since the last refresh; Jul 23 → Sep 2). One additive conflict in gateway/run.py, resolved by taking upstream's side wholesale (the new _TELEGRAM_LOBBY_REMINDER_COOLDOWN_S / _telegram_topic_cooldown_key block landed in the same region; this patch's side of that hunk was empty).

Scope note — this PR is now only the periodic half. Upstream has since landed semantic thread titles (_schedule_discord_semantic_thread_rename), which supersedes the original first-turn auto-rename. Rather than keep a stale fork of that, this branch drops it and rebuilds the surviving half on top of upstream's infrastructure: a thread whose topic drifts mid-conversation gets retitled to match, reusing upstream's own rename lane rather than adding parallel plumbing.

I re-verified the integration against 808a22ea00d ("gate relay-only thread rename kwargs"), which changed this exact callback. _on_session_title is still 2-arg (title, title_source) and now fires only when title_source == "llm". A periodic retitle is a genuine model-derived title, so the call site passes it explicitly:

title_callback=(lambda t: _retitle_cb(t, "llm")) if _retitle_cb else None

Worth stating plainly because a silent arity/gate mismatch here would make the feature a no-op in production while unit tests — which pass their own callback — stayed green.

A guard worth reviewing on its own merits: _looks_like_title() rejects model output that isn't a title (>10 words, >80 chars, mid-sentence lowercase break, internal newline) and returns None to keep the existing title. This came from a real production failure — the retitle model answered the "should this change?" prompt conversationally and a len>80 → truncate fallback wrote "The title remains accurate. The conversation is still about triaging and exec…" as a literal Discord thread title. Truncation is the wrong default for over-long model output: a real 3-7 word title is never >80 chars, so anything hitting that branch is junk to reject, not salvage. The regression test feeds that exact prose string and asserts None.

tests/agent/test_title_generator.py — 68 tests green on the rebased branch; 155 passed / 0 failed across the full 8-file slice, including tests/gateway/test_session_title_rename_lane.py.

Rate limiting is respected throughout (Discord's channel-rename budget is ~2/10min); the retitle is fire-and-forget on a daemon thread and never blocks a turn.

@TheoLong TheoLong changed the title feat(discord): auto-rename threads from session titles + periodic retitle feat(discord): periodic thread retitle on top of upstream semantic titles Sep 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/sessions Session lifecycle, resume, persistence, history comp/gateway Gateway runner, session dispatch, delivery P3 Low — cosmetic, nice to have platform/discord Discord bot adapter sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-session-state Sweeper risk: may lose/corrupt/mis-associate session or context state type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants