Repository navigation
fix(bot-mode): give each group thread its own member session (#90420, salvage #93580) - #111003
Merged
Merged
Conversation
A group member's hidden plumbing session was keyed by member alone, so
every thread in a room collapsed onto one backend transcript: thread B
resumed thread A's session and answered carrying A's context.
Everything else in the room engine is already thread-aware — the delta is
filtered by `groupThreadOf(e) === thread` and the watermark is
`${thread}::${memberKey}` — so only session identity was missing. Key and
title it per thread, and adopt a room's pre-thread pointer into the first
thread that speaks so an upgraded room's history is continued rather than
orphaned.
Co-authored-by: Wenfengcheng <30426178+Wenfengcheng@users.noreply.github.com>
The co-keyed readers of `room.sessions` had to follow session identity or
they would split from it: the stop interrupt targeted a member's only
session regardless of which thread issued the stop, and the stranded-reply
harvest resumed by bare member key even though its marker already carries
`{before, thread}`.
The clarify mirror moves too — with per-thread sessions one member can be
blocked in two threads at once, and a room-and-member key let thread B's
question silently replace thread A's card. The sweep's owner lookup stays
a MEMBER question, so it reads the member half of the key and keeps the
`::` source-qualifier check on that half alone.
Co-authored-by: Wenfengcheng <30426178+Wenfengcheng@users.noreply.github.com>
The room-level test is the reported defect: two composer sends, two threads, and neither backend transcript may contain the other thread's prompt. Proven red on the unfixed code — both threads resolved to sid-research-1. Also pins same-thread continuity, the pre-thread adoption (first thread continues the old conversation, later threads do not), and that the member half of the key stays source-qualified so a remote `research` and a local `research` never share a session. Co-authored-by: Wenfengcheng <30426178+Wenfengcheng@users.noreply.github.com>
8 tasks done
૮ >ﻌ< ა ci reviewran on c0a8d6d — test(bot-mode): pin thread-scoped session identity
|
OutThisLife
approved these changes
Sep 15, 2026
OutThisLife
left a comment
There was a problem hiding this comment.
Verified in a review worktree on current main: the room-level no-bleed regression is red with main's group-rounds.ts/group-turns.ts and green with this branch; hermes-bots suite 610/610 with #110969 applied alongside, no overlap. Same design as #93580 carried forward on the decomposed modules plus the pre-thread pointer adoption. Approving.
1 of 2 tasks
1 task done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this is
Bot Mode group threads each get their own hidden member session, so a bot answering thread B no longer replies carrying thread A's context.
Supersedes #93580 (@Wenfengcheng) — same premise and same core design, rebuilt against the decomposed Bot Mode modules. That branch targeted
apps/desktop/src/plugins/hermes-bots/plugin.jsand twotests/*.test.mjs, all three of which no longer exist onmain; cherry-picking it gives modify/delete on every file (its merge base is ~9,000 commits back). The idea was right, so it is carried forward here with credit rather than reimplemented from scratch.The defect
ensureGroupChatSession(group, member)resolved a member's hidden plumbing session with no thread component anywhere:groupMemberKey(member)— bare name or source-qualified keyGroup: <roomId>room.sessions[memberKey]Every other layer of the room engine is already thread-aware. The delta is narrowed with
groupThreadOf(e) === threadand the watermark is${thread}::${memberKey}(group-round-members.ts:44-48). So thread B built a correctly thread-scoped prompt and then submitted it into thread A's backend transcript — the model saw A's history and answered out of it.The fix
groupSessionKey(thread, member)→thread:<id>::<memberKey>, mirroring the composite the watermarks already use. Titles becomeGroup: <roomId> · <thread>. The member half stays source-qualified, so a remoteresearchand a localresearchstill never share a session.{before, thread}); the clarify mirror is keyed by thread too, since one member can now be blocked in two threads at once and a room-and-member key let thread B's question replace thread A's card; the hide sweep's owner lookup reads the member half of the key and keeps its::source check on that half alone.Verification
sid-research-1(expected 'sid-research-1' not to be 'sid-research-1'). Green after.main@345cd2b057.npm run typecheck --workspace apps/desktop: pass.npm run build --workspace apps/desktop: pass,assert-dist-builtok.voice-prefs×2,managed-ssh-update) reproduce on cleanmainwith this branch stashed, so they are pre-existing and unrelated.Tests
Four behavior contracts, no snapshots: distinct sessions per thread with same-thread reuse; the room-level no-bleed check on the actual transcripts; pre-thread adoption (first thread continues, later threads do not); source-qualified remote keys.
Supersedes #93580
Closes #90420
Closes #106460