Skip to content

fix(channels): preserve mention-only messages - #54

Open
yunfanye wants to merge 1 commit into
mainfrom
codex/fix-bare-mentions
Open

yunfanye wants to merge 1 commit into
mainfrom
codex/fix-bare-mentions

Conversation

@yunfanye

Copy link
Copy Markdown
Contributor

What this PR does

Mention-only messages lose their only content when a channel adapter strips the bot mention. Feishu replaced that event with hello, which changed the stored user input. Discord left it empty, so the shared inbox dropped it.

Both adapters now preserve the visible mention as @<bot name> when no prose remains. Messages with prose still reach the agent without the bot mention.

Follow-up to #37.

Design & Invariants

Channel adapters absorb provider-specific mention syntax before routing. A stripped bot mention remains meaningful when it is the whole message. Empty non-mention events and Feishu media-only events stay empty.

One shared helper owns the fallback so the providers cannot drift.

Test plan

  • pnpm exec biome check on all changed files
  • pnpm --filter @rome/core typecheck
  • Focused Feishu, Discord, and shared-helper tests (36 tests)
  • pnpm test:unit (5,720 tests)
  • pnpm typecheck — the local checkout fails in untouched mobile and UI files after the dependency update.
  • pnpm dev:all — the existing rome-rome-1 container already owns host port 80.

@zoolsher zoolsher 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.

Code Review: 💬 COMMENT

This PR fixes mention-only messages being lost by channel adapters. When a bot mention is stripped and nothing else remains, both Feishu and Discord now preserve the message as the visible @<bot name> via a shared preserveMentionOnlyText helper, replacing Feishu's prior "hello" substitution and Discord's empty-string result (which the shared inbox dropped). The change is cleanly factored: one helper owns the fallback so the two providers can't drift, and it comes with focused unit tests for the helper and both adapters. I verified the full changed files (mention-only.ts, feishu.ts, discord.ts), traced the call sites, and confirmed no other code depended on the old "hello" behavior.\n\nThe design is sound and the invariants (empty non-mention events and Feishu media-only events stay empty) hold for Feishu because of the explicit rawContentType === "text" && mentionedBot guard plus the if (!text) return gate. My one substantive concern is on the Discord side: the mentionedBot signal is derived from message.mentions.users.has(bot.id), which in discord.js also returns true for reply-pinged users, so a plain reply to the bot (no typed @mention) counts as a mention. The remaining notes are cosmetic/SSOT nits.

Verdict: COMMENT — The core fix is correct and well-tested; the only real concern is a Discord reply-ping edge case that is non-blocking, plus minor consistency nits.

3 finding(s) posted as inline comments below.

Severity Category File Title
P2 correctness packages/core/src/channels/discord.ts Discord mentionedBot includes reply-pings, injecting "@rome" into empty/media-only replies
P3 consistency packages/core/src/channels/mention-only.ts Fallback renders "@name" but resolveMentions renders in-prose mentions without "@"
P3 maintainability packages/core/src/channels/mention-only.ts Hardcoded "Rome" default duplicates the bot-name fallback in webchat.ts

Automated review by RomeOS Code Review · commit c1b6d87

const text = normalizeDiscordMessageText(
message.content,
bot,
bot ? message.mentions.users.has(bot.id) : false,

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.

[P2] correctnessDiscord mentionedBot includes reply-pings, injecting "@rome" into empty/media-only replies

message.mentions.users.has(bot.id) is true not only when the user types <@botId> but also whenever the message is a reply that pings the bot (discord.js includes the replied-to user in mentions.users). For a normal prose reply this is harmless (text is preserved as-is), but for an empty or media-only reply to the bot the text becomes "@Rome" instead of staying empty — injecting synthetic user input and diverging from the PR's stated invariant that media-only events stay empty (which Feishu honors). Consider using discord.js's own helper that excludes the reply ping: message.mentions.has(bot.id, { ignoreRepliedUser: true }), so mentionedBot reflects an actual typed mention.

botName?: string,
): string {
if (text || !mentionedBot) return text;
return `@${botName?.trim() || "Rome"}`;

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.

[P3] consistencyFallback renders "@name" but resolveMentions renders in-prose mentions without "@"

The fallback produces @${botName}, but Feishu's resolveMentions replaces mention keys with the bare display name (no leading @, see feishu.ts:544 and its test "@_user_1 hi" -> "Bob hi"). So a mention-only message surfaces to the agent as "@Rome" while the same bot mention embedded in prose surfaces as "Rome". Not a bug, but the two representations of the same fact are inconsistent; worth aligning the format intentionally.

botName?: string,
): string {
if (text || !mentionedBot) return text;
return `@${botName?.trim() || "Rome"}`;

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.

[P3] maintainabilityHardcoded "Rome" default duplicates the bot-name fallback in webchat.ts

The literal "Rome" default here duplicates the same fallback in webchat.ts prettyAgentName (line 14). If the default bot display name ever changes, these can drift. Consider sourcing the fallback from a single shared constant/helper rather than repeating the string per adapter.

@yunfanye yunfanye left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Code Review: 🛑 REQUEST_CHANGES

This PR cleanly centralizes the mention-only fallback and correctly fixes the ordinary Discord and Feishu text paths without changing downstream routing contracts. However, the Feishu gate is narrower than the stated invariant: supported rich-text post messages can undergo the same mention stripping and remain silently dropped.

Verdict: REQUEST_CHANGES — The Feishu implementation still loses mention-only messages when they arrive as supported rich-text posts.

1 finding(s) posted as inline comments below.

Severity Category File Title
P1 correctness packages/core/src/channels/feishu.ts Preserve mention-only rich-text posts too

Automated review by RomeOS Code Review · commit c1b6d87

const text = resolvedText || (m.rawContentType === "text" && m.mentionedBot ? "hello" : "");
const text = preserveMentionOnlyText(
resolvedText,
m.rawContentType === "text" && m.mentionedBot,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

[P1] correctnessPreserve mention-only rich-text posts too

Feishu post events are also text-bearing, and the channel normalizer strips the bot mention while retaining mentionedBot (SDK docs). A rich-text post containing only the bot mention therefore reaches this code with empty resolvedText, but this predicate evaluates false and the following if (!text) return still drops it. Treat at least post as mention-preservable while continuing to exclude media-only types, and add a post mention-only regression test.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants