feat(chat): emoji reactions on Chat messages, both directions - #1082
Merged
Conversation
Users react to agent posts from a picker on each post; the reaction shows as a chip and is injected into the agent's pane in a DISPATCH CHAT REACTION envelope that names the post by id, kind, position among the agent's posts, and a bounded quote of its opening. Agents react to the user's posts with the new dispatch_chat_react MCP tool (display-only). - agent_chat_reactions table (0051): one row per message/author/emoji, delivery state for user reactions, recovery sweep on startup. - Reactions ride on the feed's chat rows and republish via chat.entry. - Web: animated picker and chips (framer-motion), optimistic toggles, tap a chip to remove; reaction UI lives in chat-reactions.tsx. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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.
Summary
Emoji reactions in the Chat tab, in both directions.
User → agent. Hover (or, on touch, always see) the smiley action on an agent post to open a picker of 12 emoji. The reaction shows as a chip under the post and is injected into the agent's pane — through the same quiet-gated delivery as a user message — so the chip carries delivery state (dimmed while pending, ⚠ when undelivered). Tap your chip to take it back; removal is display-only.
Agent → user. Agents react to the user's posts with the new
dispatch_chat_reactMCP tool ({ messageId, emoji, remove? }). These render as neutral chips the user can't remove and are never injected anywhere.Reaction envelope
Identifies the post without pasting it back:
The latest post gets ~100 chars of quote ("your latest question"); older posts up to 300 plus how far back they are. Quoted text passes through
escapeEnvelopeMarkers.Implementation
0051_agent-chat-reactions— one row per (message, author, emoji), FK cascade to the message,deliveredfor user reactions, partial index for the startup recovery sweep. (0050 is the latest migration applied in production.)ChatService.addReaction/removeReaction(author-aware), sharedinjectDetachedsettlement chain for messages and reactions,POST/DELETE /agents/:id/chat/messages/:messageId/reactions, reactions aggregated onto feed chat rows and republished viachat.entry.chat-reactions.tsx(picker + chip row); framer-motion pop-in/out with sibling slide, row collapses only after the last chip's exit; picker grows open with staggered emoji; reduced-motion respected; picker greys out (not hidden) when the agent can't receive; optimistic toggles that never clobber a newer stream entry.docs/03-api-spec.mdChat section.Review
architecture-review(#1341): 1 item (split reaction UI out ofchat-entries.tsx) — fixed and verified.frontend-ux-review(#1342): 4 items (disabled vs hidden picker, error attribution + in-flight chips, last-chip exit animation, touch affordance) — fixed and verified. The hover/touch × was later removed at the user's request (Slack-style tap-to-remove).Test plan
pnpm run checkpnpm run finalize:webpnpm run test(server + web + extension)pnpm run test:e2e(198 passed, 12 skipped)🤖 Generated with Claude Code