Problem
The Slack typing indicator ("is thinking...") persists indefinitely after the LLM completes a turn. It never clears.
Observed Behavior
From a customer agent thread, after the LLM finishes its response:
- The typing indicator at the bottom stays on permanently
- It only briefly reappears when the user sends a new message (at which point it gets cleared again)
- Slack's
assistant.threads.setStatus is persistent (unlike Discord's auto-expiring typing)
Root Cause
In src/Netclaw.Channels.Slack/SlackThreadBindingActor.cs, the TurnCompleted message handler in HandleOutputAsync() (line ~1121) handles:
- Cursor advancement
- Reminder delivery observers
- Fallback posting
- Pending approval cleanup
- Resetting
_postedThisTurn / _uploadedFileThisTurn flags
But it never calls QueueProcessingIndicatorClearIfActive() before breaking.
Code Evidence
The QueueProcessingIndicatorClearIfActive() method exists and is called in other places:
- Line 139:
PostStop() (graceful shutdown)
- Line 427: When enqueueing a new inbound message (aborts previous turn)
- Line 1024:
ReinitializePipelineAsync()
But it's absent from the TurnCompleted handler — the normal completion path.
Contrast with Discord
Discord uses TriggerTypingAsync() which is a transient pulse (auto-expires after ~10 seconds). The bug is masked because Discord cleans up after itself. Slack's SetStatus() is persistent and requires explicit clearing.
Suggested Fix
Add QueueProcessingIndicatorClearIfActive() before the break in the TurnCompleted case:
case TurnCompleted completed:
// ... existing code ...
_pendingApprovalRequests.Clear();
// Clear the processing indicator when the turn completes
QueueProcessingIndicatorClearIfActive();
break;
Files to Change
src/Netclaw.Channels.Slack/SlackThreadBindingActor.cs (line ~1170)
Problem
The Slack typing indicator ("is thinking...") persists indefinitely after the LLM completes a turn. It never clears.
Observed Behavior
From a customer agent thread, after the LLM finishes its response:
assistant.threads.setStatusis persistent (unlike Discord's auto-expiring typing)Root Cause
In
src/Netclaw.Channels.Slack/SlackThreadBindingActor.cs, theTurnCompletedmessage handler inHandleOutputAsync()(line ~1121) handles:_postedThisTurn/_uploadedFileThisTurnflagsBut it never calls
QueueProcessingIndicatorClearIfActive()before breaking.Code Evidence
The
QueueProcessingIndicatorClearIfActive()method exists and is called in other places:PostStop()(graceful shutdown)ReinitializePipelineAsync()But it's absent from the
TurnCompletedhandler — the normal completion path.Contrast with Discord
Discord uses
TriggerTypingAsync()which is a transient pulse (auto-expires after ~10 seconds). The bug is masked because Discord cleans up after itself. Slack'sSetStatus()is persistent and requires explicit clearing.Suggested Fix
Add
QueueProcessingIndicatorClearIfActive()before thebreakin theTurnCompletedcase:Files to Change
src/Netclaw.Channels.Slack/SlackThreadBindingActor.cs(line ~1170)