Skip to content

fix(discord): add home-channel owner to handoff thread (#67702) - #67801

Open
JonthanaHanh wants to merge 1 commit into
NousResearch:mainfrom
JonthanaHanh:fix/discord-handoff-add-user-to-thread
Open

fix(discord): add home-channel owner to handoff thread (#67702)#67801
JonthanaHanh wants to merge 1 commit into
NousResearch:mainfrom
JonthanaHanh:fix/discord-handoff-add-user-to-thread

Conversation

@JonthanaHanh

Copy link
Copy Markdown
Contributor

Problem

/handoff discord creates a Discord thread but never adds the destination user as a member. For private threads (channel type 12), this makes the thread completely invisible to the user in the Discord client. The handoff reports completed but the user has no way to see or enter the continuation thread.

Two cooperating gaps:

  1. create_handoff_thread() has no user_id parameter -- the adapter can't add a member even if it wanted to
  2. _process_handoff() never passes the user who ran /sethome to the adapter

Fix

  1. Store owner identity: Add owner_user_id field to HomeChannel. Populate it in the /sethome handler from source.user_id.
  2. Plumb through handoff: gateway/run.py passes home.owner_user_id to create_handoff_thread().
  3. Add member in Discord: After creating the thread, Discord adapter calls thread.add_user(member) for the owner.
  4. Forward-compat: Update base adapter, Telegram adapter, and Slack adapter signatures to accept owner_user_id (keyword-only, default None).

Files Changed

  • gateway/config.py -- HomeChannel: add owner_user_id field + serialization
  • gateway/slash_commands.py -- /sethome: store source.user_id
  • gateway/platforms/base.py -- base create_handoff_thread signature
  • plugins/platforms/discord/adapter.py -- add user to thread after creation
  • plugins/platforms/telegram/adapter.py -- accept owner_user_id (unused)
  • plugins/platforms/slack/adapter.py -- accept owner_user_id (unused)
  • gateway/run.py -- pass owner_user_id to create_handoff_thread

Fixes #67702

/create_handoff_thread created a Discord thread but never added the
destination user as a member. For private threads this made the thread
completely invisible to the user.

1. Store owner_user_id in HomeChannel when /sethome runs
2. Pass owner_user_id through _process_handoff to create_handoff_thread
3. Discord adapter calls thread.add_user() after creation
4. Update base + Telegram + Slack signatures for forward compatibility

Fixes NousResearch#67702
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/gateway Gateway runner, session dispatch, delivery comp/plugins Plugin system and bundled plugins platform/discord Discord bot adapter sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades needs-decision Awaiting maintainer decision before any implementation labels Jul 20, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Related to #67702 and competing with #65505. This implementation derives the member from the stored /sethome owner, while #65505 accepts an explicit per-handoff CLI user ID; please choose or consolidate the desired identity source.

@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 tracing the Discord handoff path; current main still has the missing-membership premise: gateway/run.py:8815-8817 calls the adapter without an identity, and plugins/platforms/discord/adapter.py:6430-6457 returns a created thread without adding one.

Problems

  • plugins/platforms/discord/adapter.py:6431-6437 logs an add_user() failure but still returns the thread ID. gateway/run.py:8825-8830 then routes the synthetic handoff into that thread, preserving the reported "completed but invisible" failure mode.
  • Current main already persists /sethome identity as HomeChannel.user_id and scope_id (gateway/config.py:434-462, gateway/slash_commands.py:2630-2644, commit 45a408f41adc). The new owner_user_id duplicates that state. The member comment also identifies competing PR #65505's explicit per-handoff identity model; this needs one consolidated choice.

Suggested changes

  • Rework the identity plumbing against the current HomeChannel model after resolving the #65505 design choice.
  • Do not return a private-thread ID when its required member cannot be added.
  • Add direct-create, fallback-create, and membership-failure regression tests.

Automated hermes-sweeper review.

Comment thread gateway/config.py
name: str # Human-readable name for display
thread_id: Optional[str] = None

owner_user_id: Optional[str] = None # user who ran /sethome

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.

Current main already stores the /sethome source identity as HomeChannel.user_id (plus scope_id) and serializes both (gateway/config.py:434-462, commit 45a408f41adc). Please consolidate with that current model or with the explicit-initiator approach in #65505 rather than adding a parallel persisted owner field.

member = await guild.fetch_member(int(owner_user_id))
if member is not None:
await thread.add_user(member)
except Exception as add_exc:

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.

If add_user() fails, returning thread.id still makes _process_handoff() target this private thread and mark the handoff complete. Return None or otherwise fail/fallback here so the original invisible-thread failure is not retained.

@teknium1 teknium1 added sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users labels Jul 30, 2026
@GottZ

GottZ commented Aug 3, 2026

Copy link
Copy Markdown

This was generated by AI during triage.

Summary

One PR directly addresses #67702. #67801 carries the stored /sethome identity into Discord handoff-thread creation and calls add_user(), but its diff still returns the private-thread ID when membership addition fails and introduces owner_user_id alongside the identity fields documented on current main.

Related pull requests

  • fix(discord): add home-channel owner to handoff thread (#67702) #67801 best fix — (+60/-19) — n/a: The diff targets the reported cause by passing a user identity through create_handoff_thread() and adding that Discord member after either creation path; however, an add_user() exception is only logged before the thread ID is returned, preserving the completed-but-invisible failure mode. Consistent with the COMMENTED keep_open review, the salvage path is to reuse current HomeChannel.user_id/scope_id or resolve the identity-source choice with fix(discord): carry CLI handoff initiator into threads #65505, then fail or fall back when required membership cannot be established.

Duplicates

No exact duplicate is established among the listed PRs; #67801 substantially overlaps #65505 on identity plumbing and Discord member addition, but they use different identity sources.

Suggested consolidation

Keep #67801 open with a salvage path: retain its handoff-path plumbing and Discord add_user() integration, but rework it against the existing HomeChannel identity model or explicitly consolidate the design with #65505, and return None or otherwise fail/fallback when member addition fails. This follows the visible keep_open review and preserves #67801 as the recorded best-fix candidate without treating its current diff as complete; no listed PR can presently be closed as a duplicate.

Complex graph

flowchart LR
    classDef open fill:#dbeafe,stroke:#1d4ed8,color:#1e3a8a
    classDef merged fill:#dcfce7,stroke:#15803d,color:#14532d
    classDef closed fill:#e5e7eb,stroke:#6b7280,color:#1f2937
    classDef unverified fill:#f3f4f6,stroke:#9ca3af,color:#374151
    classDef best stroke-width:3px,stroke:#b45309
    classDef target stroke-width:3px,stroke:#4338ca
    I67702(["issue #67702 (open)"])
    P67801["PR #67801 (open)"]
    P67801 -->|best fix| I67702
    class I67702 open
    class P67801 open
    class P67801 best
    class P67801 target
    click I67702 "https://github.com/NousResearch/hermes-agent/issues/67702"
    click P67801 "https://github.com/NousResearch/hermes-agent/pull/67801"
Loading

Graph: solid arrow = fixes / best fix, dashed arrow = partial or unverified (see edge label); boxed group = PRs duplicating each other; amber border = best fix; indigo border = target; gray node = closed (state tag in the node label).

Cross-PR triage: Reviewed 1 pull request and 1 issue in this complex. Each diff was read against this issue; Assessment working set: 9 kB of PR diffs, 7 kB of issue/PR text, 2 kB of discussion (5 comments), 2 verify verdicts. verdicts reflect diff content, not PR titles. Part of an automated triage batch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery comp/plugins Plugin system and bundled plugins needs-decision Awaiting maintainer decision before any implementation P2 Medium — degraded but workaround exists platform/discord Discord bot adapter sweeper:blast-contained Sweeper blast radius: contained — one narrow path / opt-in / few users sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages sweeper:risk-security-boundary Sweeper risk: may affect sandboxing, auth, credentials, or sensitive data type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: /handoff discord creates private thread with only the bot as member — destination user never added, thread invisible

4 participants