fix(slack): reset silent status reaction timers

This commit is contained in:
Frank Yang
2026-03-29 14:27:08 +08:00
parent b21618389b
commit e5d548f9b0
3 changed files with 33 additions and 6 deletions

View File

@@ -560,7 +560,6 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
};
let dispatchError: unknown;
let didAdvanceStatusReaction = false;
let queuedFinal = false;
let counts: { final?: number; block?: number } = {};
try {
@@ -589,13 +588,11 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
onReasoningEnd: onDraftBoundary,
onReasoningStream: statusReactionsEnabled
? async () => {
didAdvanceStatusReaction = true;
await statusReactions.setThinking();
}
: undefined,
onToolStart: statusReactionsEnabled
? async (payload) => {
didAdvanceStatusReaction = true;
await statusReactions.setTool(payload.name);
}
: undefined,
@@ -646,9 +643,9 @@ export async function dispatchPreparedSlackMessage(prepared: PreparedSlackMessag
} else {
void statusReactions.restoreInitial();
}
} else if (didAdvanceStatusReaction) {
// Silent success should preserve the original ack instead of looking like
// we delivered a visible reply.
} else {
// Silent success should preserve queued state and clear any stall timers
// instead of transitioning to terminal/stall reactions after return.
await statusReactions.restoreInitial();
}
}

View File

@@ -94,6 +94,29 @@ describe("Slack status reaction lifecycle", () => {
expect(active.has(DEFAULT_EMOJIS.error)).toBe(false);
});
it("restoreInitial clears stall timers without re-adding queued emoji", async () => {
const { adapter, active } = createSlackMockAdapter();
const ctrl = createStatusReactionController({
enabled: true,
adapter,
initialEmoji: "eyes",
timing: { debounceMs: 0, stallSoftMs: 10, stallHardMs: 20 },
});
void ctrl.setQueued();
await vi.advanceTimersByTimeAsync(1);
expect(active.has("eyes")).toBe(true);
expect(adapter.setReaction).toHaveBeenCalledTimes(1);
await ctrl.restoreInitial();
await vi.advanceTimersByTimeAsync(30);
expect(adapter.setReaction).toHaveBeenCalledTimes(1);
expect(active.has("eyes")).toBe(true);
expect(active.has(DEFAULT_EMOJIS.stallSoft)).toBe(false);
expect(active.has(DEFAULT_EMOJIS.stallHard)).toBe(false);
});
it("does nothing when disabled", async () => {
const { adapter, active } = createSlackMockAdapter();
const ctrl = createStatusReactionController({

View File

@@ -379,7 +379,14 @@ export function createStatusReactionController(params: {
return;
}
const alreadyInitial = currentEmoji === initialEmoji;
const initialAlreadyPending = pendingEmoji === initialEmoji;
clearAllTimers();
if (alreadyInitial || initialAlreadyPending) {
pendingEmoji = "";
return;
}
await enqueue(async () => {
await applyEmoji(initialEmoji);
pendingEmoji = "";