Feishu: wire inbound message debounce#31548
Conversation
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ae34304e47
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| ...last.message, | ||
| message_type: "text", | ||
| content: JSON.stringify({ text: combinedText }), | ||
| mentions: undefined, |
There was a problem hiding this comment.
Preserve mentions in merged Feishu debounce payloads
Dropping message.mentions on merged flushes breaks mention-dependent routing for group chats: parseFeishuMessageEvent computes ctx.mentionedBot from event.message.mentions, and handleFeishuMessage will skip replying when requireMention is enabled (default true for groups). In a common burst like @bot ... followed quickly by another text, the merged synthetic event now has no mentions, so the turn is treated as “not mentioned” and only recorded to history instead of being answered.
Useful? React with 👍 / 👎.
Greptile SummaryThis PR successfully wires Feishu's inbound message handler into the standard debounce mechanism, bringing it in line with other integrations like Discord and Slack. Key changes:
Implementation quality:
Confidence Score: 5/5
Last reviewed commit: ae34304 |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 20ce943089
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| const suppressedIds = new Set( | ||
| entries | ||
| .slice(0, -1) | ||
| .map((entry) => entry.message.message_id?.trim()) | ||
| .filter((id): id is string => Boolean(id)), |
There was a problem hiding this comment.
Exclude final message ID from suppressed dedupe recording
If Feishu retries the same text event inside the debounce window, entries can contain duplicate message_id values (for example [m1, m1]). This code records every pre-final ID as suppressed before dispatch, so the first duplicate records m1, and then handleFeishuMessage immediately drops the final m1 as an already-seen duplicate; the net effect is the user message is never processed at all. Filter suppressed IDs so they never include the final entry’s message_id (or otherwise ensure only truly suppressed IDs are pre-recorded).
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 22eceaa6df
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| mention.key?.trim() || | ||
| mention.id.open_id?.trim() || | ||
| mention.id.user_id?.trim() || | ||
| mention.id.union_id?.trim() || | ||
| mention.name?.trim(); |
There was a problem hiding this comment.
Key merged mentions by stable user ID
mergeFeishuDebounceMentions deduplicates on mention.key first, but that value is a per-message placeholder (for example @_user_1) and is not stable across different messages in the same debounce batch. Two different mentions can therefore collide and drop later entries; if the dropped one is the bot mention from a later message, parseFeishuMessageEvent sees mentionedBot=false and group messages can be skipped when requireMention is enabled. Prefer open_id/user_id/union_id as the primary key and only fall back to mention.key last.
Useful? React with 👍 / 👎.
| const combinedText = entries | ||
| .map((entry) => resolveDebounceText(entry)) | ||
| .filter(Boolean) | ||
| .join("\n"); |
There was a problem hiding this comment.
Deduplicate retried message IDs before joining text
The flush path builds combinedText from every buffered entry, but retries can place duplicate events for the same message_id into entries during the debounce window (which this file already anticipates in recordSuppressedMessageIds). In a sequence like [m1, m1, m2], the merged turn will include m1 content twice, so the agent receives corrupted/repeated input. Deduplicate by message.message_id before mapping entries into combinedText.
Useful? React with 👍 / 👎.
a210ff6 to
61fe440
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 61fe440ba3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| return; | ||
| } | ||
| await recordSuppressedMessageIds(entries); | ||
| const dedupedEntries = dedupeFeishuDebounceEntriesByMessageId(entries); |
There was a problem hiding this comment.
Exclude previously processed retries from merged text
dedupeFeishuDebounceEntriesByMessageId only deduplicates IDs within the current flush batch, so if Feishu retries an already-processed message and a new message arrives in the same debounce window (for example, m2 retry + new m3), combinedText still includes stale m2 content. In that case the merged dispatch uses m3 as the final message ID, so handleFeishuMessage dedupe does not remove the stale text and the agent receives duplicated/corrupted input for the new turn.
Useful? React with 👍 / 👎.
61fe440 to
dc8a3be
Compare
dc8a3be to
0fef244
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0fef244bc2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| .map((entry) => resolveDebounceText(entry)) | ||
| .filter(Boolean) | ||
| .join("\n"); | ||
| const mergedMentions = mergeFeishuDebounceMentions(freshEntries); |
There was a problem hiding this comment.
Avoid cross-message mention-forward triggers in debounced flushes
mentions on the synthetic debounced event currently come from all freshEntries, so a burst like @alice ... followed by @bot ... in the same debounce window is reinterpreted as a mention-forward request even though no single message requested that. parseFeishuMessageEvent uses the merged mentions with isMentionForwardRequest, so group replies can incorrectly auto-mention unrelated users. This should not combine non-bot mention targets across messages; otherwise debounce changes message intent and can ping the wrong people.
Useful? React with 👍 / 👎.
0b139c5 to
6985ee8
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 43566e7caf
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
src/secrets/resolve.ts
Outdated
| if (settled) { | ||
| return; | ||
| } | ||
| if (isIgnoredExecStdinError(error)) { |
There was a problem hiding this comment.
Use the existing stdin-error guard in exec resolver
The new child.stdin listener calls isIgnoredExecStdinError(error), but that identifier is not defined in this module (the existing helper is isIgnorableStdinWriteError). In the exact fast-exit case this code is handling (EPIPE/ERR_STREAM_DESTROYED from stdin), this path will throw a ReferenceError instead of gracefully continuing, so exec-backed secret resolution can fail unexpectedly for providers that exit before consuming stdin.
Useful? React with 👍 / 👎.
6396bdd to
82646bd
Compare
|
PR #31548 - Feishu: wire inbound message debounce (#31548) Merged after verification.
|
Verified: - pnpm install --frozen-lockfile - pnpm build - pnpm check (fails on unrelated pre-existing lint in ui/src/ui/views/agents-utils.ts and src/pairing/pairing-store.ts) - pnpm test:macmini (previous run passed before rebase) Co-authored-by: bertonhan <60309291+bertonhan@users.noreply.github.com> Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
Verified: - pnpm install --frozen-lockfile - pnpm build - pnpm check (fails on unrelated pre-existing lint in ui/src/ui/views/agents-utils.ts and src/pairing/pairing-store.ts) - pnpm test:macmini (previous run passed before rebase) Co-authored-by: bertonhan <60309291+bertonhan@users.noreply.github.com> Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
Verified: - pnpm install --frozen-lockfile - pnpm build - pnpm check (fails on unrelated pre-existing lint in ui/src/ui/views/agents-utils.ts and src/pairing/pairing-store.ts) - pnpm test:macmini (previous run passed before rebase) Co-authored-by: bertonhan <60309291+bertonhan@users.noreply.github.com> Co-authored-by: Tak Hoffman <781889+Takhoffman@users.noreply.github.com>
Summary
messages.inbounddebounce, so rapid consecutive text messages were processed as separate turns.extensions/feishu/src/monitor.account.ts, inboundim.message.receive_v1now usesresolveInboundDebounceMs({ channel: "feishu" }) + createInboundDebouncer(...); debounce is applied to text-only messages, media/attachments flush immediately, and control commands bypass debounce.Change Type (select all)
Scope (select all touched areas)
User-visible / Behavior Changes
messages.inbound.debounceMsandmessages.inbound.byChannel.feishu.Security Impact (required)
No)No)No)No)No)Repro + Verification
Environment
Steps
messages.inbound.byChannel.feishuto a non-zero window (e.g.10000).Expected
Actual
Evidence
Attach at least one:
Trace/log snippet:
./node_modules/.bin/vitest run extensions/feishu/src/monitor.reaction.test.ts # ✓ 11 passedHuman Verification (required)
What you personally verified (not just CI), and how:
Compatibility / Migration
Yes)No)No)Failure Recovery (if this breaks)
messages.inbound.byChannel.feishu: 0(or globalmessages.inbound.debounceMs: 0) to disable debounce behavior.extensions/feishu/src/monitor.account.ts.Risks and Mitigations