From 6f7497bce87f9cc5eb6317dd6808bddf7f0802df Mon Sep 17 00:00:00 2001 From: Hong Jiarong Date: Tue, 21 Jul 2026 06:36:09 +0000 Subject: [PATCH] fix(hub): stamp CheckMark/CrossMark when agent run finishes (v0.0.41) After removing the Typing reaction, add CheckMark on success or CrossMark on failure so teachers can see completion on the source message without opening the card. --- hub/package-lock.json | 4 ++-- hub/package.json | 2 +- hub/src/feishu/trigger.ts | 14 ++++++++++++-- hub/test/unit/feishu-reactions.test.ts | 3 ++- 4 files changed, 17 insertions(+), 6 deletions(-) diff --git a/hub/package-lock.json b/hub/package-lock.json index 225dfb7..4a1b665 100644 --- a/hub/package-lock.json +++ b/hub/package-lock.json @@ -1,12 +1,12 @@ { "name": "@paradigm/hub", - "version": "0.0.40", + "version": "0.0.41", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@paradigm/hub", - "version": "0.0.40", + "version": "0.0.41", "dependencies": { "@alicloud/credentials": "^2.4.5", "@alicloud/docmind-api20220711": "^1.4.15", diff --git a/hub/package.json b/hub/package.json index f52ffb0..f44d64f 100644 --- a/hub/package.json +++ b/hub/package.json @@ -1,6 +1,6 @@ { "name": "@paradigm/hub", - "version": "0.0.40", + "version": "0.0.41", "private": true, "type": "module", "engines": { diff --git a/hub/src/feishu/trigger.ts b/hub/src/feishu/trigger.ts index 4647315..d8cce1e 100644 --- a/hub/src/feishu/trigger.ts +++ b/hub/src/feishu/trigger.ts @@ -1,7 +1,8 @@ /** * Sends a "processing" reaction immediately, then streams a single * interactive card through the full agent run lifecycle: thinking → tool - * calls (with trace panel) → streaming answer text → final card. The card + * calls (with trace panel) → streaming answer text → final card. On finish, + * replaces Typing with CheckMark (success) or CrossMark (failure). The card * shows a collapsible tool-use panel, a collapsible reasoning panel, and * the markdown answer text. Throttled to ~2.5 patches/sec to avoid * spamming the Feishu API. @@ -680,7 +681,16 @@ export function makeTriggerHandler(deps: TriggerDeps): TriggerHandler { initializedSkills: [...(result.initializedSkillIds ?? [])], }, }); - await removeProcessingReaction(); + // Mirror the start "Typing" reaction: drop processing, then stamp a + // terminal emoji so teachers see done/failed without reading the card. + const removedProcessingReaction = await removeProcessingReaction(); + if (removedProcessingReaction) { + await addReaction( + rt, + msg.message_id, + outcome.isError ? "CrossMark" : "CheckMark", + ); + } }) .catch(async (e) => { const removedProcessingReaction = await removeProcessingReaction(); diff --git a/hub/test/unit/feishu-reactions.test.ts b/hub/test/unit/feishu-reactions.test.ts index 4d52fde..72b2f20 100644 --- a/hub/test/unit/feishu-reactions.test.ts +++ b/hub/test/unit/feishu-reactions.test.ts @@ -52,7 +52,7 @@ describe("Feishu reactions", () => { await expect(removeReaction(rt, "message-1", "reaction-1")).resolves.toBe(false); }); - it("adds Typing on start and removes it on success", async () => { + it("adds Typing on start and replaces it with CheckMark on success", async () => { const run = deferred(); const rt = mockRuntime(); const runAgent = vi.fn((req: RunRequest) => { @@ -71,6 +71,7 @@ describe("Feishu reactions", () => { expect(rt.reactionRequests).toEqual([ { kind: "add", messageId: "message-1", emoji: "Typing", reactionId: "reaction-1" }, { kind: "remove", messageId: "message-1", reactionId: "reaction-1" }, + { kind: "add", messageId: "message-1", emoji: "CheckMark", reactionId: "reaction-2" }, ]); }); });