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" }, ]); }); });