mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-13 05:16:29 +02:00
* feat(lark): reply inside the originating thread (话题) instead of the group When a user @-mentions the bot inside a Lark topic/thread, the bot now replies back into that thread rather than posting a fresh message at the chat level. Behavior is automatic and scoped: only triggers that were themselves inside a thread get a threaded reply, so normal group/p2p chats are unchanged. The outbound path is event-driven and decoupled from the inbound message, so the trigger message_id + thread_id are persisted on lark_chat_session_binding (migration 122) at ingest time. The patcher then routes the agent reply (text / markdown card / error card) and the OutcomeReplier notices (/issue confirmation, offline/archived) through Lark's reply endpoint with reply_in_thread=true when a thread is present, falling back to a chat-level send if the threaded reply fails. Co-authored-by: Cursor <cursoragent@cursor.com> * fix(lark): classify thread-reply failures before chat-level fallback Only retry a threaded reply at the chat level when Lark returns an explicit "this message/topic cannot receive a threaded reply" error (recalled trigger, topic gone, topics disabled, aggregated message, etc.). Transport errors, 5xx, timeouts, rate limits, and ambiguous failures are now logged and returned as failures instead of being retried, so we never duplicate a reply or leak a thread-only reply into the main group chat. The three reply-capable send methods now return a structured *APIError carrying the Lark business code, and isThreadReplyUnsupported drives the fallback via an allowlist. sendWithThreadFallback is promoted to a package-level function so the immediate OutcomeReplier sends (/issue confirmation, offline/archived notices) share the same classified fallback path instead of silently swallowing thread-reply failures. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: kun <kuen@micous.com> Co-authored-by: Cursor <cursoragent@cursor.com>
24 lines
1.3 KiB
SQL
24 lines
1.3 KiB
SQL
-- Thread-aware outbound: remember the most recent inbound trigger
|
|
-- message (and the Lark topic / thread it belongs to, if any) per chat
|
|
-- binding so the decoupled outbound patcher can thread its reply back
|
|
-- into the originating 话题 (thread) instead of always posting a fresh
|
|
-- message at the chat level.
|
|
--
|
|
-- The outbound side is event-driven and disconnected from the inbound
|
|
-- message: it only knows the chat_session → lark_chat_session_binding
|
|
-- (i.e. the lark_chat_id). Persisting the latest trigger here is what
|
|
-- lets EventChatDone resolve a reply target without plumbing the
|
|
-- message id through the whole task lifecycle.
|
|
--
|
|
-- Both columns are nullable:
|
|
-- * last_lark_message_id is the message_id the reply quotes / replies
|
|
-- to (Lark's reply endpoint keys off it).
|
|
-- * last_lark_thread_id is the message's thread_id; Lark only sends a
|
|
-- thread_id for messages that are part of a topic, so a NULL/empty
|
|
-- value means "the last trigger was a normal chat message" and the
|
|
-- outbound keeps the existing chat-level send behavior. Only when a
|
|
-- thread_id is present does the patcher switch to a thread reply.
|
|
ALTER TABLE lark_chat_session_binding
|
|
ADD COLUMN last_lark_message_id TEXT,
|
|
ADD COLUMN last_lark_thread_id TEXT;
|