calm-artist-7206
02/13/2026, 3:07 PMnutritious-balloon-65098
02/13/2026, 3:07 PMnutritious-balloon-65098
02/13/2026, 3:18 PMfix/slack-privacy-callback branch)
### Checklist
- ✅ Investigated the codebase to understand how Slack callbacks work
- ✅ Created a GitHub issue documenting the problem
- ✅ Proposed a fix in the issue description
- ✅ Implemented the fix in a PR
- ✅ Changes are concise and focused only on the privacy fix
- ✅ Added tests for the new functionality
- ✅ Code passes syntax validation
- ✅ Pushed changes to remote branch
### Changes Made (5 files)
| File | Change |
|------|--------|
| slack_callback_processor.py | Added last_slack_message_content field + privacy check |
| slack_view.py | Track message content when sending follow-up messages from Slack |
| slack_manager.py | Set initial message content when registering new callbacks |
| conversation_callback_utils.py | Added update_callback_processor() utility function |
| test_slack_callback_processor.py | Added 3 test cases for the privacy feature |
### Note
The fix applies to V0 conversations only. V1 conversations use a different callback system (SlackV1CallbackProcessor) and would need a similar fix in a follow-up PR if the same behavior exists there.calm-artist-7206
02/13/2026, 3:19 PMnutritious-balloon-65098
02/13/2026, 3:19 PMnutritious-balloon-65098
02/13/2026, 3:25 PM_should_respond_to_slack() and _get_last_user_message() methods
- [x] Added privacy check in __call__ before sending to Slack
- [x] Updated callback creation to track message content
- [x] Updated follow-up message handling to update callback
- [x] Added 4 tests for V1
- [x] Implemented V0 fix (legacy system):
- [x] Added last_slack_message_content field and privacy check
- [x] Added update_callback_processor utility function
- [x] Added 3 tests for V0
- [x] Both fixes maintain backward compatibility
- [x] Updated issue with V1 fix documentation
- [x] Both branches pushed and PRs created
### How the Fix Works
When a message is sent from Slack, its content is stored in the callback processor. When the callback fires (agent finishes), it compares the last user message with what was stored. If they don't match (user sent via Web UI), the callback skips posting to Slack, protecting user privacy.calm-artist-7206
02/13/2026, 3:27 PMnutritious-balloon-65098
02/13/2026, 3:27 PMnutritious-balloon-65098
02/13/2026, 3:28 PMfix/slack-v1-privacy-callback branch
- Files modified: slack_v1_callback_processor.py, slack_view.py, test_slack_v1_callback_processor.py
- Adds _should_respond_to_slack() and _get_last_user_message() methods
- Tracks last_slack_message_content in slack_view_data
- Updates callback when follow-up messages sent from Slack
V0 Fix (Legacy): [#12864](https://github.com/OpenHands/OpenHands/pull/12864) - fix/slack-privacy-callback branch
- Files modified: slack_callback_processor.py, slack_view.py, slack_manager.py, conversation_callback_utils.py, test_slack_callback_processor.py
- Adds last_slack_message_content field to processor
- Adds update_callback_processor() utility function
### Key Design Decision
As discussed with GPT-5's question: The fix uses content comparison rather than a simple boolean because non-Slack sources (Web UI) don't know about Slack callbacks. Content comparison allows the callback to independently verify the message source without requiring coordination from other components.
### All Changes Pushed
Both branches have been pushed to the remote repository with the PRs created.calm-artist-7206
02/13/2026, 3:30 PMnutritious-balloon-65098
02/13/2026, 3:30 PMnutritious-balloon-65098
02/13/2026, 3:31 PMfix/slack-v1-privacy-callback branch
- Modifies SlackV1CallbackProcessor to check if last message originated from Slack before sending responses
- Adds _should_respond_to_slack() and _get_last_user_message() methods
- Updates slack_view.py to track message content when creating/updating V1 callbacks
- Includes 4 new tests for the privacy feature
#### V0 Fix (Legacy) - Already Pushed
- [#12864](https://github.com/OpenHands/OpenHands/pull/12864) - fix/slack-privacy-callback branch
- Similar approach for the V0 SlackCallbackProcessor
- Note: You indicated V1 matters and V0 is legacy - this PR may not be needed
### Open Question
Should I close PR #12864 (V0 fix) since V0 is legacy? The enterprise files I modified don't have explicit "LEGACY V0" header comments, but they do contain V0-specific code paths (SlackCallbackProcessor, send_message_to_v0_conversation) that are legacy.
### Technical Note
I also explained why content comparison is the right approach vs. a simple boolean: a boolean would require all message sources (Web UI, etc.) to know about and update Slack callbacks, whereas content comparison allows the Slack callback to independently verify the message source without coupling other components to the Slack integration.nutritious-balloon-65098
02/13/2026, 3:42 PMenterprise/integrations/slack/slack_v1_callback_processor.py - Added privacy check methods
- enterprise/integrations/slack/slack_view.py - Updated to track message content
- enterprise/tests/unit/integrations/slack/test_slack_v1_callback_processor.py - Added privacy tests
### Key Design Decision
Used content comparison instead of a simple boolean because:
- The Web UI (and other message sources) don't know about Slack callbacks
- A boolean would require all message sources to update the Slack callback when sending messages
- Content comparison allows the callback to independently verify by fetching the actual last message - no coordination needed
### Closed
PR #12864 (V0 fix) - Closed as V0 is legacy and being phased out.
### Checklist
- ✅ Investigated the issue and understood the architecture
- ✅ Created GitHub issue with detailed analysis
- ✅ Implemented V1 fix with privacy check
- ✅ Added tests for the privacy feature
- ✅ Documented V1 fix approach in issue comments
- ✅ Closed unnecessary V0 PR per user's direction
- ✅ All changes pushed to fix/slack-v1-privacy-callback branchnutritious-balloon-65098
02/13/2026, 3:46 PMpython
if last_user_message == summary_instruction:
return True
This is flawed because it could allow summaries to always be sent to Slack if the summary instruction ends up as the last user message, defeating the privacy check entirely.
### Next step needed:
I should update PR #12866 to remove that problematic check before it can be merged. The fix is simple - just delete those 3 lines.
I have NOT pushed this fix yet - awaiting your confirmation to proceed with updating the PR.nutritious-balloon-65098
02/13/2026, 3:49 PMif last_user_message == summary_instruction: return True would allow summaries to be sent even when they shouldn't. This needs to be removed.
2. Inefficient event fetching - Fetching 50 events (limit: 50) when we only need the last user message.
3. Duplicate summary prevention - Since summary instructions are sent as "user messages", we need a mechanism to:
- Skip summary instructions when finding the "last real user message"
- Prevent duplicate summaries (proposed: use sentinel value '' to mark "already processed")
### Current Status
PR #12866 should NOT be merged yet. We were actively discussing the proper solution when this summary was requested. The proposed fix involves:
- Removing the flawed summary instruction check
- Adding a sentinel value ('') to mark when we've already responded
- Filtering out summary instructions when fetching the last user message
- Reducing the event limit
Would you like me to implement these fixes and update the PR?nutritious-balloon-65098
02/13/2026, 4:08 PMif last_user_message == summary_instruction: return True would have caused summaries to ALWAYS be sent to Slack, defeating the privacy protection entirely
3. A simple boolean won't work - As I correctly identified, the Web UI doesn't know about Slack callbacks and can't update them
### Current State
The issue (#12863) now contains a detailed proposal with three alternative approaches:
- Option A (Recommended): Add trigger_source field to SDK's MessageEvent ('slack', 'web_ui', 'callback', etc.)
- Option B: Conversation-level last_user_interaction_source tracking in AppConversationInfo
- Option C: Callback self-disabling after responding (limited, doesn't fully solve privacy)
### No Code to Push
All branches with my changes have been closed. The issue is open for design discussion before any implementation proceeds. The team needs to decide on the right architectural approach before code is written.