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.
This commit is contained in:
2026-07-21 06:36:09 +00:00
parent e634418a02
commit 6f7497bce8
4 changed files with 17 additions and 6 deletions
+2 -2
View File
@@ -1,12 +1,12 @@
{ {
"name": "@paradigm/hub", "name": "@paradigm/hub",
"version": "0.0.40", "version": "0.0.41",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "@paradigm/hub", "name": "@paradigm/hub",
"version": "0.0.40", "version": "0.0.41",
"dependencies": { "dependencies": {
"@alicloud/credentials": "^2.4.5", "@alicloud/credentials": "^2.4.5",
"@alicloud/docmind-api20220711": "^1.4.15", "@alicloud/docmind-api20220711": "^1.4.15",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@paradigm/hub", "name": "@paradigm/hub",
"version": "0.0.40", "version": "0.0.41",
"private": true, "private": true,
"type": "module", "type": "module",
"engines": { "engines": {
+12 -2
View File
@@ -1,7 +1,8 @@
/** /**
* Sends a "processing" reaction immediately, then streams a single * Sends a "processing" reaction immediately, then streams a single
* interactive card through the full agent run lifecycle: thinking → tool * 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 * shows a collapsible tool-use panel, a collapsible reasoning panel, and
* the markdown answer text. Throttled to ~2.5 patches/sec to avoid * the markdown answer text. Throttled to ~2.5 patches/sec to avoid
* spamming the Feishu API. * spamming the Feishu API.
@@ -680,7 +681,16 @@ export function makeTriggerHandler(deps: TriggerDeps): TriggerHandler {
initializedSkills: [...(result.initializedSkillIds ?? [])], 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) => { .catch(async (e) => {
const removedProcessingReaction = await removeProcessingReaction(); const removedProcessingReaction = await removeProcessingReaction();
+2 -1
View File
@@ -52,7 +52,7 @@ describe("Feishu reactions", () => {
await expect(removeReaction(rt, "message-1", "reaction-1")).resolves.toBe(false); 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<RunResult>(); const run = deferred<RunResult>();
const rt = mockRuntime(); const rt = mockRuntime();
const runAgent = vi.fn((req: RunRequest) => { const runAgent = vi.fn((req: RunRequest) => {
@@ -71,6 +71,7 @@ describe("Feishu reactions", () => {
expect(rt.reactionRequests).toEqual([ expect(rt.reactionRequests).toEqual([
{ kind: "add", messageId: "message-1", emoji: "Typing", reactionId: "reaction-1" }, { kind: "add", messageId: "message-1", emoji: "Typing", reactionId: "reaction-1" },
{ kind: "remove", messageId: "message-1", reactionId: "reaction-1" }, { kind: "remove", messageId: "message-1", reactionId: "reaction-1" },
{ kind: "add", messageId: "message-1", emoji: "CheckMark", reactionId: "reaction-2" },
]); ]);
}); });
}); });