fix(feishu): thread failure and error notices to the triggering message

Failure notices (turn failures, sub-flow errors from list/card/preset/
model/compact/stop/bind/switch paths, batch-input results, and the
card-queue rate-limit notice) were sent as fresh messages keyed only by
chat_id, so in topic groups they landed in the group's default area
instead of the topic the interaction belongs to.

- #sendFailure accepts options.replyTo; every call site forwards the
  anchor available in its context (command message, card message, or
  watch/list anchor)
- #handleMessageFailure and #finishBatchResult thread to the triggering
  message
- /repair stays private-chat-only by design; proactive delivery keeps
  its existing behavior (no thread context exists there)

- lib/index.js: regenerated host bundle
This commit is contained in:
evanfang0054 2026-08-31 21:25:13 +08:00
parent 6d815e0254
commit 82e7127cb4
2 changed files with 25 additions and 22 deletions

File diff suppressed because one or more lines are too long

View file

@ -774,7 +774,7 @@ export class FeishuHarnessBridge {
await this.#state.markSeen(messageId); await this.#state.markSeen(messageId);
this.#status.lastMessageAt = new Date().toISOString(); this.#status.lastMessageAt = new Date().toISOString();
this.#status.messagesReceived += 1; this.#status.messagesReceived += 1;
if (result?.message) await this.#send(event.message.chat_id, result.message); if (result?.message) await this.#send(event.message.chat_id, result.message, { replyTo: event.message.message_id });
this.#status.lastError = null; this.#status.lastError = null;
}) })
.then(() => this.#finishReaction(messageId, processingReaction, 'DONE')) .then(() => this.#finishReaction(messageId, processingReaction, 'DONE'))
@ -843,7 +843,7 @@ export class FeishuHarnessBridge {
const text = options.appendMessage const text = options.appendMessage
? `${messageFailureText(failure)}\n\n${options.appendMessage}` ? `${messageFailureText(failure)}\n\n${options.appendMessage}`
: messageFailureText(failure); : messageFailureText(failure);
await this.#send(chatId, text).catch(() => undefined); await this.#send(chatId, text, { replyTo: options.replyTo }).catch(() => undefined);
return failure; return failure;
} }
@ -874,6 +874,7 @@ export class FeishuHarnessBridge {
await this.#send( await this.#send(
event.message.chat_id, event.message.chat_id,
failureText, failureText,
{ replyTo: event.message.message_id },
).catch(() => undefined); ).catch(() => undefined);
} }
@ -1630,7 +1631,7 @@ export class FeishuHarnessBridge {
} }
this.#logger.warn?.('[dsh-feishu] card action queue is full; dropping callbacks'); this.#logger.warn?.('[dsh-feishu] card action queue is full; dropping callbacks');
let tracked; let tracked;
tracked = this.#send(entry.chatId, t('操作过于频繁,请稍后再试。')) tracked = this.#send(entry.chatId, t('操作过于频繁,请稍后再试。'), { replyTo: entry.messageId ?? null })
.catch(() => undefined) .catch(() => undefined)
.finally(() => { .finally(() => {
this.#cardActionTasks.delete(tracked); this.#cardActionTasks.delete(tracked);
@ -1711,7 +1712,7 @@ export class FeishuHarnessBridge {
}) })
.catch(async (error) => { .catch(async (error) => {
if (this.#signal?.aborted) return; if (this.#signal?.aborted) return;
await this.#sendFailure(entry.chatId, error, { logLabel: 'card action' }); await this.#sendFailure(entry.chatId, error, { logLabel: 'card action', replyTo: entry.messageId });
}) })
.finally(() => { .finally(() => {
if (this.#cardActionInFlight.get(dedupeKey) === tracked) { if (this.#cardActionInFlight.get(dedupeKey) === tracked) {
@ -2081,7 +2082,7 @@ export class FeishuHarnessBridge {
}, },
); );
} catch (error) { } catch (error) {
await this.#sendFailure(chatId, error, { logLabel: 'session list' }); await this.#sendFailure(chatId, error, { logLabel: 'session list', replyTo });
} }
} }
@ -2098,7 +2099,7 @@ export class FeishuHarnessBridge {
{ key, updateMessageId, replyTo }, { key, updateMessageId, replyTo },
); );
} catch (error) { } catch (error) {
await this.#sendFailure(chatId, error, { logLabel: 'workspace list' }); await this.#sendFailure(chatId, error, { logLabel: 'workspace list', replyTo });
} }
} }
@ -2114,6 +2115,7 @@ export class FeishuHarnessBridge {
} catch (error) { } catch (error) {
await this.#sendFailure(chatId, error, { await this.#sendFailure(chatId, error, {
logLabel: 'session binding', logLabel: 'session binding',
replyTo,
userMessage: t('绑定失败:{message}', { message: safeErrorText(error) }), userMessage: t('绑定失败:{message}', { message: safeErrorText(error) }),
}); });
} }
@ -2127,6 +2129,7 @@ export class FeishuHarnessBridge {
} catch (error) { } catch (error) {
await this.#sendFailure(chatId, error, { await this.#sendFailure(chatId, error, {
logLabel: 'workspace switch', logLabel: 'workspace switch',
replyTo,
userMessage: t('切换失败:{message}', { message: safeErrorText(error) }), userMessage: t('切换失败:{message}', { message: safeErrorText(error) }),
}); });
} }
@ -2350,7 +2353,7 @@ export class FeishuHarnessBridge {
catalog._currentId = settings.agentPreset; catalog._currentId = settings.agentPreset;
await this.#sendCard(chatId, presetCard(catalog), { key, updateMessageId }); await this.#sendCard(chatId, presetCard(catalog), { key, updateMessageId });
} catch (error) { } catch (error) {
await this.#sendFailure(chatId, error, { logLabel: 'preset card' }); await this.#sendFailure(chatId, error, { logLabel: 'preset card', replyTo: updateMessageId });
} }
} }
@ -2375,7 +2378,7 @@ export class FeishuHarnessBridge {
} }
await this.#sendCard(chatId, modelCard(catalog), { key, updateMessageId }); await this.#sendCard(chatId, modelCard(catalog), { key, updateMessageId });
} catch (error) { } catch (error) {
await this.#sendFailure(chatId, error, { logLabel: 'model card' }); await this.#sendFailure(chatId, error, { logLabel: 'model card', replyTo: updateMessageId });
} }
} }
@ -2403,7 +2406,7 @@ export class FeishuHarnessBridge {
} }
await this.#send(chatId, lines.join('\n'), { replyTo }); await this.#send(chatId, lines.join('\n'), { replyTo });
} catch (error) { } catch (error) {
await this.#sendFailure(chatId, error, { logLabel: 'status text' }); await this.#sendFailure(chatId, error, { logLabel: 'status text', replyTo });
} }
} }
@ -2455,7 +2458,7 @@ export class FeishuHarnessBridge {
await this.#sendCard(chatId, statusCard(info), { key, updateMessageId }); await this.#sendCard(chatId, statusCard(info), { key, updateMessageId });
} catch (error) { } catch (error) {
await this.#sendFailure(chatId, error, { logLabel: 'status card' }); await this.#sendFailure(chatId, error, { logLabel: 'status card', replyTo: updateMessageId });
} }
} }
@ -2480,7 +2483,7 @@ export class FeishuHarnessBridge {
); );
await this.#send(chatId, result?.message || t('上下文压缩失败。'), { replyTo }); await this.#send(chatId, result?.message || t('上下文压缩失败。'), { replyTo });
} catch (error) { } catch (error) {
await this.#sendFailure(chatId, error, { logLabel: 'compact' }); await this.#sendFailure(chatId, error, { logLabel: 'compact', replyTo });
} }
} }
@ -2503,7 +2506,7 @@ export class FeishuHarnessBridge {
} }
await this.#send(chatId, result?.message || t('/stop 执行完成。'), { replyTo }); await this.#send(chatId, result?.message || t('/stop 执行完成。'), { replyTo });
} catch (error) { } catch (error) {
await this.#sendFailure(chatId, error, { logLabel: 'stop' }); await this.#sendFailure(chatId, error, { logLabel: 'stop', replyTo });
} }
} }
@ -2543,7 +2546,7 @@ export class FeishuHarnessBridge {
if (reply) await this.#send(chatId, reply, { replyTo }); if (reply) await this.#send(chatId, reply, { replyTo });
} }
} catch (error) { } catch (error) {
await this.#sendFailure(chatId, error, { logLabel: 'preset reset' }); await this.#sendFailure(chatId, error, { logLabel: 'preset reset', replyTo: updateMessageId });
return; return;
} }
try { try {
@ -2566,7 +2569,7 @@ export class FeishuHarnessBridge {
if (reply) await this.#send(chatId, reply, { replyTo }); if (reply) await this.#send(chatId, reply, { replyTo });
} }
} catch (error) { } catch (error) {
await this.#sendFailure(chatId, error, { logLabel: 'preset selection' }); await this.#sendFailure(chatId, error, { logLabel: 'preset selection', replyTo: updateMessageId });
return; return;
} }
try { try {
@ -2598,7 +2601,7 @@ export class FeishuHarnessBridge {
if (reply) await this.#send(chatId, reply, { replyTo }); if (reply) await this.#send(chatId, reply, { replyTo });
} }
} catch (error) { } catch (error) {
await this.#sendFailure(chatId, error, { logLabel: 'model selection' }); await this.#sendFailure(chatId, error, { logLabel: 'model selection', replyTo: updateMessageId });
return; return;
} }
try { try {