[front] fix: stop sticky human mention re-inserting in the composer - #32281
[front] fix: stop sticky human mention re-inserting in the composer#32281frankaloia wants to merge 2 commits into
Conversation
In a multi-user conversation, if your last message mentioned only human user(s), the "sticky human mentions" feature re-inserted that mention into the composer on every conversation update — incoming messages from other participants, agent answers, and streaming ticks. Because the insertion effect re-ran whenever stickyMentions (derived from the last user message) got a new reference, and only guarded on the editor being empty, the mention kept popping back and could not be dismissed: deleting it emptied the editor, and the next update re-inserted it (#10353). Gate the prefill on an idempotency key of (conversation sId, source message sId). The mention is inserted at most once per source message, re-arming only when the user sends a new message or switches conversation — preserving the intended stickiness while ignoring unrelated updates and honoring deletion. Plumbs stickyMentionsSourceId (the current user's last message sId) through AgentInputBar -> InputBar -> InputBarContainer and documents the invariant with a @cc contract. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The bare #10353 refs resolve to the wrong issue inside dust-tt/dust; keep the explanatory prose without the number. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| user={context.user} | ||
| onSubmit={context.handleSubmit} | ||
| stickyMentions={autoMentions} | ||
| stickyMentionsSourceId={lastUserMessage?.sId ?? null} |
There was a problem hiding this comment.
The new sticky-human-mentions-prefill-once-per-source contract on InputBarContainerProps.stickyMentionsSourceId conflicts with the pre-existing optimistic-message lifecycle: lastUserMessage changes from placeholder-user-message-* to a server ID for the same send (ConversationViewer.tsx:1296). If acknowledgment occurs while the initial prefill remains, the ref keeps the placeholder key; dismissing the mention and receiving another update then reinserts it without a new message.
| // prefilled, so we insert them once per source rather than on every re-render. Without this, | ||
| // streaming updates and other participants' messages would keep re-inserting the mention into | ||
| // the empty composer, and the user could not dismiss it. | ||
| const appliedStickyMentionKeyRef = useRef<string | null>(null); |
There was a problem hiding this comment.
The new sticky-human-mentions-prefill-once-per-source contract on InputBarContainerProps.stickyMentionsSourceId also conflicts with the pre-existing UserAnswerRequired swap in AgentInputBar, which unmounts the composer when an earlier agent run asks a question. Answering remounts it with this ref reset to null, so a previously dismissed human mention is inserted again for the unchanged conversation and source message.
Summary
Fixes dust-tt/tasks#10353.
In a multi-user conversation, the user's handle kept popping back into the composer after every message/answer and couldn't be dismissed.
Root cause
The "sticky human mentions" feature (#25480). The sticky mention is derived from your own last sent message:
autoMentionsbecomesstickyMentions, and the draft-restore effect inInputBarContainer.tsxinserts them viaeditorService.resetWithMentions(...). That effect re-runs wheneverstickyMentionschanges reference — andstickyMentionsderives fromlastUserMessage, which gets a new object reference on every conversation store update (another participant's message, an agent answer, each streaming tick). Its only guard waseditorService.isEmpty(), so whenever the composer was empty it re-inserted the mention.So
stickyMentions= "the human mentions in the last message I sent," and whether it reappears is entirely a function of what you last sent:@Bstill in it — the default, because the composer pre-fills@B, so unless you delete the chip whatever you type goes out as "@b …". Your new last message still mentions@B→autoMentionsis still[@B]→ after the composer clears it re-inserts@B. This is the vicious loop: the prefill guarantees your next message keeps the mention, which keeps it sticky, which prefills again.@B(deleted the chip, sent a plain message) — your last message has no mention →autoMentionsreturns[]→ nothing inserted. Cleared.Two consequences:
Fix
Gate the insertion on an idempotency key
(conversation.sId, source message sId)held in a ref — insert at most once per source message:sIdis unchanged.autoMentionsreturns[]).Plumbs
stickyMentionsSourceId(the current user's last messagesId) throughAgentInputBar → InputBar → InputBarContainer, documented with a@cc [label:react]contract on the prop.Pattern
The guard is the codebase's standard "run once per id" ref idiom — same shape as
useAutoOpenSidePanel.ts(autoOpenedFilesForRef === agentMessage.sId), the composite-keyinitKeyguards (MCPServerAuthConnection.tsx), and the siblinguseHandleMentions.tsx(prevConversationIdRef) that handles agent sticky mentions.Testing
npx tsgo --noEmit(front): no new errors (pre-existing unrelatedarchiverdecl only).biome check+cc-check format/liston the changed file: clean; the new contract is discoverable.Behavior reasoned through the effect's re-run conditions; there's no automated harness for this tiptap+Virtuoso composer.
Risk
Low, scoped to the sticky human-mention prefill. Agent-mention selection (
useHandleMentions) is untouched. Agent builder passes no source id and only has agent sticky mentions, so it's unaffected.Deploy Plan
Reviewer
r? @ykmsd Eng runner ticket https://github.com/dust-tt/tasks/issues/10353