fix(feishu): deliver watch completions as replies inside the watch's topic

Watch-completion pushes rebuilt a fresh completion card keyed only by
chat_id, so in topic groups the "task finished" notice landed in the
group's default area instead of the topic where the watch was created.

- #runWatch persists the anchor message id (command message or card)
  on the durable watch entry
- #deliverCompletion sends the completion card as a reply to that
  anchor; entries without an anchor keep the previous behavior
- batch watch_add passes the card message as the anchor

- lib/index.js: regenerated host bundle
This commit is contained in:
evanfang0054 2026-08-31 20:44:50 +08:00
parent fdab438a93
commit 6d815e0254
2 changed files with 11 additions and 3 deletions

File diff suppressed because one or more lines are too long

View file

@ -1797,6 +1797,7 @@ export class FeishuHarnessBridge {
const result = await this.#runWatch(key, chatId, sessionId, {
notify: false,
validatedTarget,
replyTo: messageId,
});
if (result.changed) changed += 1;
else if (!result.ok) failed += 1;
@ -2872,6 +2873,13 @@ export class FeishuHarnessBridge {
chatId,
lastSeq,
...(watchStartedAt !== null ? { watchStartedAt } : {}),
// Remember where the watch was created so completion pushes can be
// delivered as replies inside the same Feishu topic.
...(replyTo
? { replyToMessageId: replyTo }
: existingEntry?.replyToMessageId
? { replyToMessageId: existingEntry.replyToMessageId }
: {}),
});
} catch (error) {
await reply(t('关注失败:{message}', { message: safeErrorText(error) }));
@ -2997,7 +3005,7 @@ export class FeishuHarnessBridge {
await this.#sendCard(
entry.chatId,
completionCard(sessionId, entry.title, reason),
{ key },
{ key, replyTo: entry.replyToMessageId ?? null },
);
const current = this.#state.watchEntry?.(key, sessionId);
if (!current