Skip to content

ask_user tool doesn't work in Slack/gateway sessions #707

@ahmedhesham6

Description

@ahmedhesham6

Bug: ask_user tool is non-functional in Slack/gateway mode

Summary

When an agent running through a Slack channel (or any gateway channel) calls the ask_user tool, the call is broken in all approval modes:

  • Auto-approved (AllowAll/Allowlist): The tool is approved and executed via the MCP server handler, which returns "INTERACTIVE_REQUIRED" error — because the MCP handler explicitly does not support non-interactive execution. The agent receives an error result instead of user answers.
  • Approval-gated (Ask/DenyAll with ask_user not allowlisted): The ask_user call appears as a generic "Allow/Deny" approval prompt with a brief tool preview. The user can only approve or deny the tool — they never see the actual questions and cannot answer them.

In both cases, the ask_user tool is completely non-functional through gateway channels.

Root Cause

The gateway dispatcher (libs/gateway/src/dispatcher.rs) treats ask_user like any other tool — routing it through the tool_calls_proposed → approval → execute pipeline. But ask_user is fundamentally different from other tools:

  1. It requires user input as its result — not just an approve/deny binary decision
  2. Its arguments contain the questions that should be shown to the user
  3. The MCP server handler (libs/mcp/server/src/local_tools.rs:3089) explicitly refuses non-interactive execution, returning an error

In contrast, the TUI interactive mode (cli/src/commands/agent/run/mode_interactive.rs) correctly intercepts ask_user before execution: it auto-approves the tool, shows a popup with the questions, collects answers, and sends the structured AskUserResult back as the tool result.

Expected Behavior

When ask_user is called through Slack, the channel should:

  1. Intercept the tool call before it reaches the approval/execution pipeline
  2. Render the questions as Slack Block Kit interactive elements — radio buttons for single-select, checkboxes for multi-select, with an optional text input for custom answers
  3. Collect user responses via Slack interactive callbacks
  4. Feed the structured AskUserResult back as the tool result (resolving the tool call with the answer content, not just accept/reject)

Implementation Approach

The fix involves changes primarily in the gateway dispatcher and Slack channel:

1. Intercept ask_user in the dispatcher (libs/gateway/src/dispatcher.rs)

When tool_calls_proposed is received, check if any of the proposed tools is ask_user. If so:

  • Auto-resolve all non-ask_user tools normally (per approval policy)
  • For ask_user tools, parse the AskUserRequest from arguments and return a new RunOutcome::AskUserNeeded variant (similar to ApprovalNeeded)
  • The dispatcher handler should call channel.send_ask_user(...) on the Slack channel

2. Add send_ask_user to the Channel trait (libs/gateway/src/channels/mod.rs)

New trait method (with default no-op impl):

async fn send_ask_user(
    &self,
    reply: OutboundReply,
    questions: Vec<AskUserQuestion>,
) -> Result<String> { ... }

3. Implement send_ask_user in Slack channel (libs/gateway/src/channels/slack.rs)

  • Render each question as a Block Kit section with radio buttons (input block type with radio_buttons element) or checkboxes for multi-select
  • Add a plain_text_input element for custom answers when allow_custom is true
  • Use action_id format: q:{question_label}:{option_index} for selection and q:{question_label}:custom for text input
  • Handle interactive callback from Slack → parse responses → build AskUserResult → resolve the tool call via client.resolve_tools() with the answer as content

4. Handle ask_user response callbacks in the dispatcher

When a Slack interactive callback arrives with type=ask_user_response:

  • Parse the selected options per question
  • Build AskUserResult with AskUserAnswer entries
  • Call client.resolve_tools() with ToolDecisionAction::Accept and content = Some(serde_json::to_string(&result))
  • Edit the original message to show the user's answers

5. Add ask_user to safe auto-approve allowlist

Since ask_user is now handled interactively by the channel, it should be in the default allowlist so it doesn't block on approval. The tool is inherently safe — it only asks questions and returns the user's answers.

Files to Modify

File Change
libs/gateway/src/dispatcher.rs Intercept ask_user in consume_run_events, add AskUserNeeded outcome, handle response callbacks
libs/gateway/src/channels/mod.rs Add send_ask_user trait method
libs/gateway/src/channels/slack.rs Implement Block Kit question rendering + callback parsing
libs/gateway/src/channels/telegram.rs Implement inline keyboard question rendering
libs/gateway/src/channels/discord.rs Implement component-based question rendering
libs/gateway/src/types.rs Add InboundMessage metadata type for ask_user responses

Fallback for channels without interactive support

For channels that don't implement send_ask_user (e.g., basic webhooks), the dispatcher should:

  1. Render the questions as formatted text in the channel
  2. Auto-reject the ask_user tool with a message like "This channel does not support interactive questions. Please use a channel that supports buttons/interactive elements."
  3. Or: accept the tool with a default/cancelled result

Related Code References

  • TUI ask_user handler: tui/src/services/handlers/ask_user.rs
  • TUI popup interception: cli/src/commands/agent/run/mode_interactive.rs:145-180
  • MCP handler (returns INTERACTIVE_REQUIRED): libs/mcp/server/src/local_tools.rs:3089-3099
  • Types: libs/shared/src/models/tools/ask_user.rs
  • Gateway approval flow: libs/gateway/src/dispatcher.rs:1410-1490
  • Slack approval buttons: libs/gateway/src/channels/slack.rs:728-750
  • Slack Block Kit: libs/gateway/src/channels/slack.rs:1023+

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions