Skip to main content
Flip one YAML flag and any inbound channel bot can run shell commands, with approval routed to the platform of your choice. Before this feature, adding execute_command to an inbound bot meant building the agent, attaching an approval backend, and lifting the default deny — all in Python. Now the operator flips one flag and the gateway wires everything at bot construction.
Behaviour change (PraisonAI ≥ 2026-07-24). Bots now honour auto_approve_shell: true for execute_command end-to-end. On older builds the tool could still prompt because the auto-approve backend was attached to the agent instance but not the global approval registry. Upgrade to fix — see Auto-approve still prompting.

Quick Start

1

Turn it on

Add allow_shell: true to any channel. In a dev or trusted workspace, auto-approve every command:
Start the gateway:
Now @your-bot run ls executes on the server.
2

Require approval for real workloads

Turn off auto-approve and route the prompt to the owner. Every shell call now sends a Slack Block Kit approval message to UOWNER, and only UOWNER can approve:

Configuration Options

Eight new per-channel fields control shell execution and approval routing.
approval_mode is validated at load time. A typo like chanel or webook throws a ValueError instead of silently falling through to the gateway-queue fallback — which would leave shell approvals stuck where the operator never looks. Only channel, gateway, http, and webhook are accepted.
auto_approve_shell accepts string truthy values "1", "true", "yes", "on" (case-insensitive).

Choosing an approval_mode

Pick the backend that matches where you want to answer approval prompts. The routing precedence, exactly as wired at bot construction:

Fail-closed behaviour

When auto_approve_shell: false is set but no approval backend can be wired, shell commands are denied instead of silently auto-approved. apply_bot_smart_defaults() may install an AutoApproveBackend when auto_approve_tools is on. If the shell wiring then finds no SlackApproval / TelegramApproval / DiscordApproval / GatewayApprovalBackend / HTTPApproval / WebhookApproval, leaving that backend in place would auto-approve shell despite the explicit opt-out. The gateway now swaps in a deny-by-default CallbackBackend. The warning line to watch for at startup:
This is a behavioural change. A bot that previously auto-approved shell under auto_approve_shell: false (because smart defaults auto-approved tools) now replies “That command needs approval and was denied.” Add approval_channel, set approval_mode, or wire a custom backend to restore approvals.

With routing rules

Routing rules on a shell-enabled channel keep execute_command — routed agents are shell-enabled too, not just the default channel clone. Previously a routing rule swapped in a raw agent that never saw enable_shell_tools(), so execute_command disappeared for any turn that hit a routing rule — even with allow_shell: true. The gateway now re-applies the channel’s shell setup to routed agents, and runs the full smart-defaults pipeline first.

What allow_shell changes on the agent

Setting the flag runs enable_shell_tools() on the per-channel cloned agent, after smart defaults are applied. The five side effects, in order:
Step 5 closes the gap that made auto_approve_shell: true still prompt. execute_command is decorated with @require_approval, which consults the global approval registry — often with agent_name=None. Attaching a backend only to the agent instance was not enough. enable_shell_tools() now calls set_backend(backend, agent_name=agent.name) after installing the backend, so auto-approval takes effect end-to-end. The related core fix honours mark_approved() even for critical-risk tools like execute_command.

Per-platform routing

Each platform resolves the approval target from a fallback chain, then builds its native backend. approval_users accepts a comma-separated string or a list. An empty allowlist passes allowed_approvers=None to the platform backend, whose native default lets anyone in the channel approve.

Environment Variables

Every fallback the feature reads, in one place.
Webhook without a URL falls back safely. If approval_mode=webhook but neither approval_webhook_url nor APPROVAL_WEBHOOK_URL is set, the gateway logs a warning and falls through to GatewayApprovalBackend. It does not build a broken WebhookApproval("None").

Recipes

Copy-paste-ready configurations for common setups.
Fastest to demo — every shell call runs immediately in a trusted workspace.

HTTP dashboard approvals

Serve a built-in approval page and answer from the browser.

User Flow

A Slack workspace owner wants their support team to type @bot run pytest -q and see results in-thread, but only after they personally approve each command. They edit gateway.yaml on the server, add allow_shell: true, auto_approve_shell: false, and approval_channel: "UOWNER" to their slack: block, then restart the gateway. From that moment, every shell request from any Slack user triggers a Block Kit approval prompt in the owner’s DMs — they tap Approve and the command runs; they tap Deny and the agent replies “That command needs approval and was denied.” No Python was written.

Troubleshooting

Upgrade to a PraisonAI build with the approval-registry sync fix (≥ 2026-07-24). On older builds, auto_approve_shell: true attached AutoApproveBackend to the agent instance only, but execute_command is decorated with @require_approval and consults the global approval registry (often with agent_name=None) — so it kept prompting. The fix mirrors the backend into the registry keyed by agent.name, and honours mark_approved() even for critical-risk tools like execute_command.

Best Practices

Set auto_approve_shell: true only in trusted workspaces. On any channel that untrusted users can reach, keep it false and route approvals to an owner.
Prefer an explicit approval_users allowlist over relying on SLACK_APPROVERS / TELEGRAM_APPROVERS / DISCORD_APPROVERS env vars — it keeps the policy visible in the config and avoids env-var leakage.
WhatsApp, email, and other channels have no in-channel approval UI. Route them to approval_mode: gateway so approvals queue in the control plane instead of falling silently.
Run praisonai gateway lint — the fail-closed approval_mode validator rejects typos at load time, catching chanel/webook before they strand approvals.
When auto_approve_shell: false resolves no approval backend, the gateway logs a warning and denies shell tools. If your bot suddenly refuses commands with “needs approval and was denied”, that log line lists the missing knobs — add approval_channel, set approval_mode, or wire a custom backend.
Adding allow_shell: true to a channel with routing rules grants execute_command to every routed agent on that channel, not just the default one. Keep routing rules narrow on shell-enabled channels, and remove allow_shell on reload to drop the cached shell clones.

Approval Protocol

Approval backend reference for Slack, Telegram, Discord, webhook, and HTTP.

Bot Gateway

Gateway config, channel security, and hot-reload.

Bot Default Tools

How tools get injected on bot agents.

Durable Approvals

Survive gateway restarts across in-flight approvals.