Bot Chat Commands

Built-in chat commands — status, model switching, usage, context compression, queuing — plus Python-registered, file-based, and entry-point custom commands for Telegram, Discord, Slack, and WhatsApp bots.

Diagram

Bot Chat Commands

📋 User Request

⚙️ Bot Chat Commands

✅ Result

python
from praisonaiagents import Agent

agent = Agent(name="assistant", instructions="You are a helpful assistant.")
agent.start("What commands are available?")

The user types a slash command in chat; the bot runs built-in handlers for status, model, usage, and session control.

Diagram

Bot Chat Commands

📝 /command

⚙️ Command Handler

🤖 Agent

✅ Chat Reply

Command Picker

Diagram
graph TB
    Q{What do you need?} --> A[Cancel current answer only]
    Q --> A2[Cancel current answer AND my queued messages]
    Q --> B[Free context space]
    Q --> M[See where we left off]
    Q --> C[Change LLM]
    Q --> D[See cost/tokens]
    Q --> E[Queue a follow-up]
    Q --> F[Author a skill from sources]
    Q --> G[Rewind the last turn]
    Q --> H[List or switch sessions]
    Q --> I[Re-run your last message]
    Q --> J[Toggle reasoning visibility]
    Q --> K[Accept a suggested automation]
    Q --> L[Create from a template]
    Q --> M[Run a project command file]
    Q --> N[Inspect background tasks]
    A --> A1[/stop]
    A2 --> A3[/stop all  ·  /cancel]
    B --> B1[/compress]
    M --> M1[/recap]
    C --> C1[/model]
    D --> D1[/usage]
    E --> E1[/queue]
    F --> F1[/learn]
    G --> G1[/undo]
    H --> H1[/sessions, /resume]
    I --> I1[/retry]
    J --> J1[/reasoning]
    K --> K1[/automations]
    L --> L1[/blueprint]
    N --> N1[/tasks]
    M --> M1[".praisonai/commands/*.md → /name"]

    classDef agent fill:#8B0000,color:#fff
    classDef tool fill:#189AB4,color:#fff
    classDef source fill:#6366F1,color:#fff

    class Q agent
    class A,A2,B,C,D,E,F,G,H,I,J,K,L,N,A1,A3,B1,C1,D1,E1,F1,G1,H1,I1,J1,K1,L1,N1 tool
    class M,M1 source

File-based (.praisonai/commands/*.md) and entry-point commands surface alongside built-ins — see File-based & Entry-point Commands.

Every PraisonAI bot comes with built-in chat commands. Just type a command in your chat — no setup required.

Built-in Commands

/status

Shows agent name, model, platform, and uptime.

/new

Resets the conversation session. Starts a fresh chat with the agent.

/stop

Cancels the current agent task. Requires run control to be enabled.

/stop all

Cancels the current task AND drains any messages the user queued behind it. Session-scoped stop.

/cancel

Alias for /stop all. Cancels the current task AND drains queued follow-ups.

/help

Lists all available commands (built-in + custom) with descriptions. Output is filtered to commands the caller is allowed to run.

/whoami

Shows your user ID, role, and the commands you are allowed to run.

/model

Switch the LLM model for this conversation (session-scoped).

/usage

Show token usage and estimated cost for this session.

/compress

Summarise older messages to free context window space.

/recap

Show a read-only “where were we” summary of the session. Never mutates history or triggers compaction.

/queue

Queue a follow-up message to run after the current task.

/tasks

List background tasks and background subagents (/tasks), inspect one (/tasks <id>), or cancel (/tasks cancel <id>). Per-user scoped.

/learn

Distil a grounded skill from sources you describe (repo, docs, PDFs, or this chat). Privileged — admin-only by default when a policy is configured. Authored via skill_manage with the same approval gate as other agent-created skills. See Learn a Skill from Sources and Command Access Control.

/undo

Undo the last turn’s file changes. Requires the agent to track changes (autonomy with track_changes).

/sessions

List the caller’s saved sessions/checkpoints. Scoped to the requesting user — never enumerates other users’ sessions.

/resume

Switch to a saved session: /resume &lt;id&gt;. Use /sessions to list available IDs.

/retry

Re-run your last message through the normal chat path.

/reasoning

Toggle whether extended-thinking output is shown in this conversation (per user).

/automations

List pending automation suggestions with inline Accept/Dismiss buttons. Telegram only today.

/blueprint

Create an automation from a template: /blueprint <name> [slot=value ...]. Telegram only today.

Per-Command Access Control

Restrict which commands each user may run with admin_users and user_allowed_commands on Telegram, Slack, and Discord — admins get full access, regular users only run commands you list. All built-in commands, including the new runtime controls, are gated by this policy.

Command classification is now gated by two layers: content-trust (new — BotMessage.allow_control, so an untrusted producer’s /-prefixed body is never treated as a command) and identity ACL (existing — admin_users / user_allowed_commands). Content-trust fires first. See Control-Trust Provenance.

Command Access Control

Admin users, per-command allowlists, and /whoami

Quick Start

  1. Install PraisonAI
    bash
    pip install praisonai
  2. Set Your Bot Token
    bash
    export TELEGRAM_BOT_TOKEN=your_token_here
  3. Start the Bot
    bash
    praisonai bot telegram --token $TELEGRAM_BOT_TOKEN
  4. Send a Command

    Open your bot chat and type:

    /status

    You’ll see the agent name, model, platform, and uptime.

Platform Examples

Telegram
praisonai bot telegram --token $TELEGRAM_BOT_TOKEN
Discord
praisonai bot discord --token $DISCORD_BOT_TOKEN
Slack
praisonai bot slack --token $SLACK_BOT_TOKEN --app-token $SLACK_APP_TOKEN

Command Details

/status

Shows current bot information:

FieldDescription
AgentName of the AI agent
ModelLLM model being used
Platformtelegram, discord, slack, or whatsapp
UptimeHow long the bot has been running
RunningWhether the bot is currently active

/whoami

Shows permission information for the caller:

FieldDescription
User IDPlatform-native user ID
UsernameDisplay name when available
RoleAdmin or User
Allowed commandsCommands this user may run

Example output:

User Information
User ID: 456
Username: alice
Role: User
Allowed commands: help, status, whoami

A command outside your allowed list — or one that doesn’t exist — falls through to the agent rather than vanishing. See Unknown or Misspelled Commands.

/new

Resets the conversation:

Diagram

/new

Clear History

Fresh Start

User

Bot

AI Agent

  • Clears all previous messages from the agent’s session
  • Replies with a confirmation message
  • Next message starts a completely new conversation
  • Clears any per-user /model override — so a “start fresh” also un-does a bad or unavailable model switch (a stuck /model gpt-9-imaginary no longer persists past /new)

/stop

Cancels the current agent task immediately. Pick the scope that matches what you want stopped.

Diagram
graph TB
    Q{What do you want to stop?} --> A[Just the running answer]
    Q --> B[The running answer AND my queued follow-ups]
    A --> A1[/stop  ·  stop]
    B --> B1[/stop all  ·  /cancel]

    classDef q fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef turn fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef session fill:#F59E0B,stroke:#7C90A0,color:#fff

    class Q q
    class A,A1 turn
    class B,B1 session

Turn-scoped (default):

Diagram

/stop

Request Cancel

Interrupt

Task Cancelled

User

Bot

Run Control

AI Agent

CommandWhat it stopsReply payload
/stop (or stop)Current turn only. Queue preserved.scope: "turn", pending_cancelled: 0
/stop allCurrent turn + drains queued messages.scope: "session", pending_cancelled: N
/cancelAlias for /stop all.Same as /stop all.

/stop:

  • Immediately interrupts the current agent processing
  • Replies with confirmation that the task was cancelled
  • Next message starts a fresh task

/stop all and /cancel additionally:

  • Drain any pending messages the user queued behind the running turn
  • Report the drained count as pending_cancelled: N so nothing is silently dropped

/help

Lists all available commands — both built-in and custom — with their descriptions.

/model

Switches the LLM model for your conversation, or reports which model is currently active.

Diagram

/model gpt-4o

Per-user override

Applied next turn

Reply with new model

User

Bot

Session

AI Agent

Show current model — /model with no argument:

Current model: gpt-4o-mini
Use /model <name> to switch (e.g., /model gpt-4o)

Switch model — /model <name>:

/model gpt-4o
✅ Model switched to gpt-4o for this conversation.

Restart-required guard — when the gateway’s code on disk has changed since the process started (e.g. you ran git pull or pip install -U against the running checkout), /model <name> refuses the switch instead of risking a half-loaded module:

/model gpt-4o
⚠️ Gateway code changed on disk since it started (2aa28d1 → def5678). Restart the gateway to apply updates before switching models.

Restart the gateway/bot process and try again. The check is best-effort: when the fingerprint can’t be determined (no git checkout, unreadable files) the switch proceeds normally.

To disable the guard (e.g. on a sandboxed CI runner where in-place updates are deliberate):

python
session_manager.code_skew_guard = False

/usage

Reports token usage and a rough estimated cost for the session.

FieldDescription
TotalTotal tokens used in the session
PromptTokens spent on prompts (when tracked)
CompletionTokens generated by the model (when tracked)
Est. costApproximate cost using a flat per-token rate — not model-specific

Example output:

📊 Token Usage
Total: 18,204 tokens
Prompt: 12,800 | Completion: 5,404
Est. cost: $0.0036

/compress

Compresses your conversation history to free context-window space.

Diagram

/compress

SummarizeOptimizer

Summary + last 10

Tokens freed

User

Bot

Context Manager

Compacted History

  • Requires at least 10 messages — shorter histories get: "ℹ️ Conversation too short to compress (need at least 10 messages)."
  • Targets ~50% token reduction, always preserving the most recent 10 messages and all system messages
  • Reports messages removed and tokens freed:
✅ Compressed 32 messages, freed ~12,400 tokens.
  • If the context optimizer library is unavailable, falls back to keeping system messages and the last 10 messages
  • On failure the history is left untouched: "❌ Compression failed: {error}"

/recap

Renders a read-only “where were we” summary of the session without changing anything.

Diagram

/recap

read history

📌 summary + recent tail

User

Bot

build_recap

Example output:

📌 Recap — where we were:
[Previous conversation summary]
User asked about deploy runbook; we outlined steps 1–3.
Recent:
• user: what about step 4?
• assistant: step 4 covers rollback…
  • Read-only. Never mutates the transcript and never triggers compaction — safe to run mid-conversation.
  • Reuses a persisted summary when present. If /compress has already produced a compaction summary, /recap shows it verbatim instead of re-summarising.
  • Otherwise it runs the naive summariser over a copy of the history, so the original is untouched and no compaction event is emitted.
  • Always preserves the recent tail — the last few messages are appended verbatim.
  • Output is bounded to ~3500 characters so Telegram deliveries never overflow the 4096-char message limit; when trimming is needed the older summary is truncated, never the recent tail.

Empty history returns:

Nothing to recap yet — this session has no messages.

When the access policy denies the command, the bot replies:

⛔ You are not permitted to run /recap

The same recap is available as a Python primitive when you build a custom surface:

python
from praisonaiagents.compaction import build_recap

history = [
    {"role": "user", "content": "Outline the deploy runbook."},
    {"role": "assistant", "content": "Steps 1–3 cover build, test, ship."},
    {"role": "user", "content": "What about step 4?"},
    {"role": "assistant", "content": "Step 4 covers rollback."},
]

print(build_recap(history))
OptionTypeDefaultDescription
historyList[Dict[str, Any]]—Conversation messages. Not modified.
tailint4Number of recent messages to append verbatim.
max_charsint3500Upper bound on rendered recap length. <= 0 disables the cap.

/queue

Adds a follow-up message that runs after the current task completes.

Diagram
AgentRun ControlBotUserAgentRun ControlBotUserCurrent turn finishes firstalt[No active run][Active run, merged into pending slot][Steered into running turn]/queue follow-up messagesubmit(user_id, text)RUN_NOW“Send normally — nothing to queue behind”MERGED“Added to pending follow-up”Process queued messageSTEERED“Injected as live guidance”

Check pending queue — /queue with no argument:

📝 Pending follow-up: summarise the key findings

Or if nothing is pending:

No follow-up queued. Use /queue <message> to add one while a task is running.

Queue a message — three possible outcomes depending on bot state:

OutcomeWhenBot reply
Nothing to queueNo task running"ℹ️ No task is currently running, so there's nothing to queue behind. Send your message normally to run it now."
MergedTask running, message parked in pending slot"⏳ Added to your pending follow-up — it will run after the current task completes."
SteeredTask running, message injected as live guidance"🧭 Injected into the running task as live guidance."

/undo

Rewinds the last turn’s file changes via Agent.undo().

  • Requires change tracking and the pre-run snapshot — autonomy=AutonomyConfig(track_changes=True, snapshot=True) (see File Snapshot)
  • Returns "✅ Reverted the last turn." when files were restored, "ℹ️ Nothing to undo." when there is nothing to revert
  • Degrades gracefully when undo is unavailable: "❌ This agent does not support undo (change tracking not enabled)."

/sessions

Lists recent saved sessions for the requesting user only. Session lookups are scoped via the per-user storage key so one caller cannot enumerate another user’s sessions.

Example output:

📁 Recent sessions:
• telegram:123456789
• telegram:123456789:project-a

Use /resume <id> to switch to one.

Returns "No saved sessions yet." when nothing is stored.

/resume

Switches to a previously saved session.

/resume telegram:123456789:project-a
✅ Resumed session telegram:123456789:project-a.
  • Usage: /resume <id> — run /sessions first to see IDs
  • Returns "❌ Session {id} not found." when the ID is unknown
  • When the session manager lacks a resume_session primitive, the bot reports honestly rather than claiming a switch succeeded

/retry

Re-runs your most recent user message through the normal session.chat(...) path.

🔁 Retrying your last message:
Summarise the deployment runbook in three bullets.

Returns "ℹ️ Nothing to retry — no previous message found." when the history is empty.

/reasoning

Toggles whether extended-thinking output is shown for this user in this conversation. The preference is stored per user on the session manager.

/reasoning
🧠 Reasoning output will be shown for this conversation.

Run again to hide:

/reasoning
🙈 Reasoning output will be hidden for this conversation.

Defaults to hidden when no preference is set. Adapters consult is_reasoning_visible() when rendering responses.

/tasks

Inspect background tasks from inside the chat — no need to switch to a terminal. /tasks merges two sources into one list: background tasks (from BackgroundRunner) and background subagents spawned from chat (from BackgroundJobManager).

CommandPurpose
/tasksList your background tasks and background subagents
/tasks <id>Show detail for one task or subagent job (incl. result / error)
/tasks cancel <id>Cancel a running background task or subagent job

Example output:

text
/tasks
🧩 Background tasks:
• aa1b2c… research — running (35%)
• ff9d3e… summary  — completed (100%)
• 3f8a91c2 Research the latest Python 3.13 features — running

Use /tasks <id> for detail or /tasks cancel <id>.

/tasks 3f8a91c2
🧩 Task 3f8a91c2 — Research the latest Python 3.13 features
Status: completed
Result: Python 3.13 adds an experimental free-threaded build…

The list shows at most 20 entries: background tasks first, then background-subagent jobs newest-first in the remaining slots. Subagent detail shows Result: or Error: trimmed to the first 200 characters.

/tasks cancel <id> on a background subagent calls BackgroundJobManager.cancel_job(id) and only reports success when the job was actually cancelled.

Handler strings:

SituationReply
No tasks💤 No background tasks.
Cancel needs an idℹ️ Usage: /tasks cancel <id>
Not found (or not owned)❌ Task not found: <id>
Cancel success✅ Cancelled task <id>.
Cancel too late❌ Could not cancel task <id> (already finished?).

/learn

Authors a grounded, reusable SKILL.md from sources you name — a codebase, API docs, PDFs, configs, or the current chat.

/learn deploy steps from this repo and the runbook PDF
  • Calls agent.learn_skill(request) which gathers the named sources with the agent’s existing tools and writes one grounded SKILL.md via skill_manage
  • Admin-only by default when a CommandAccessPolicy is configured (admin_users or user_allowed_commands set) — it belongs to PRIVILEGED_COMMANDS because it reads host sources and alters future agent behaviour
  • Available to all users when no policy is configured (backward-compatible)
Learn Skill

Full guide: sources, grounding rules, async API, bot usage

/automations

Lists pending automation suggestions, one message per suggestion, each with its own ✓ Accept / ✕ Dismiss inline keyboard. Telegram only today.

Diagram
schedule_addSuggestionEngineBotUserschedule_addSuggestionEngineBotUser/automationslist pendingsuggestions💡 header + Accept/Dismiss buttonstap ✓ Accept (sug:accept:<id>)schedule_add(…)✅ Automation scheduled
  • Header when suggestions exist: 💡 You have 1 pending automation suggestion: (pluralised by count)
  • Header when empty: You have no pending automation suggestions.\nUse /blueprint to create one from a template.
  • Each Accept button carries sug:accept:<id>; each Dismiss carries sug:dismiss:<id>
  • The automations permission is re-checked on every button tap. A denied user sees ⛔ You are not permitted to manage automations appended to the message
  • When the scheduler extra isn’t installed: ❌ Automations are not available (scheduler not installed).

/blueprint

Creates an automation directly from a blueprint template. Telegram only today.

Three call shapes:

  1. /blueprint (no args) → lists available blueprints + usage:
📋 Create an automation from a template:

• morning-brief — Daily morning briefing with news and priorities
• important-mail — Check for important emails at a regular interval
• weekly-review — End-of-week summary and review

Usage: /blueprint <name> [slot=value ...]
Example: /blueprint morning-brief hour=8 weekdays=mon-fri
  1. /blueprint <name> → creates a job with blueprint defaults
  2. /blueprint <name> hour=8 weekdays=mon-fri → slot overrides

Slot overrides use key=value grammar. Integer-looking values are coerced to int, so hour, minute, and interval_minutes resolve correctly (_parse_slot_args).

Success and error strings from _automations.create_from_blueprint:

✅ Automation scheduled from 'morning-brief'. Added job 'morning-brief' (schedule cron:0 8 * * mon,tue,wed,thu,fri)
❌ Blueprint 'unknown' not found. Available: morning-brief, important-mail, weekly-review

Unknown or Misspelled Commands

An unknown slash command — a typo like /statu, or a custom command name that isn’t registered on this deployment — is dispatched to the agent as the raw text, the same as any free-form message. The bot never silently drops a command.

Diagram
AgentCommandRegistryBotUserAgentCommandRegistryBotUserFalls through to agent dispatch(matches Slack reference adapter)/statumatch “statu”no matchsession.chat(user_id, “/statu”)replyreply
PlatformBehavior on unknown /command
TelegramDispatched to agent as raw text (PR #4320).
DiscordDispatched to agent as raw text (PR #4320).
SlackDispatched to agent as raw text (reference adapter — unchanged).

Custom Commands

Register your own commands using register_command(). Available on all bot adapters.

python
from praisonai.bots import TelegramBot
from praisonaiagents import Agent

agent = Agent(name="assistant", instructions="Be helpful")
bot = TelegramBot(token="YOUR_TOKEN", agent=agent)

# Register a custom command
async def handle_ping(message):
    return "Pong! Bot is alive."

bot.register_command("ping", handle_ping, description="Check if bot is alive")

import asyncio
asyncio.run(bot.start())
# Now /ping is available alongside /status, /new, /stop, /help

register_command() API

python
bot.register_command(
    name="mycommand",           # Command name (without /)
    handler=my_handler,          # Async or sync callable
    description="What it does",  # Shown in /help
    usage="/mycommand <arg>",    # Optional usage string
    channels=["telegram"],       # Optional: restrict to specific platforms
)
ParameterTypeRequiredDescription
namestrYesCommand name without / prefix
handlerCallableYesFunction to call. Receives a BotMessage argument
descriptionstrNoHuman-readable description (shown in /help)
usagestrNoUsage example string
channelslist[str]NoRestrict to specific platforms (e.g., ["telegram", "slack"]). Empty = all platforms

The description you pass here IS the label users see in the native / menu on Telegram and Discord. See Slash Command Menu.

list_commands()

Get all registered commands:

python
commands = bot.list_commands()
# Returns: [ChatCommandInfo(name="status", ...), ChatCommandInfo(name="new", ...), ...]

# Filter by platform
telegram_commands = bot.list_commands(platform="telegram")

File-based and Entry-point Commands in Bot Chats

One .praisonai/commands/{name}.md file authored once runs in praisonai code and in every bot chat — no duplication, no per-platform wiring.

Quick Start — one file, every chat

Author a single command file:

markdown
---
description: Summarise the current channel
argument-hint: <topic?>
---
Summarise recent messages about $ARGUMENTS.

Start the bot — nothing new to wire. The resolver discovers the file automatically once bot commands are enabled in config:

python
from praisonai.bots import TelegramBot
from praisonaiagents import Agent

agent = Agent(name="assistant", instructions="Be helpful.")
bot = TelegramBot(token="YOUR_TOKEN", agent=agent)

import asyncio
asyncio.run(bot.start())
# /summary is now available in Telegram — same file also works in `praisonai code`.

The user runs it in chat:

User:  /summary Q3 revenue
Bot:   [agent replies with a Q3 revenue summary]

One command source, every surface

Diagram

📄 .praisonai/commands/*.md

🔀 CustomCommandResolver

📦 praisonai.bot_commands entry-points

💻 praisonai code REPL/TUI

💬 Telegram / Slack / Discord

The same .praisonai/commands/*.md files already documented for Custom Agents & Commands now reach bot chats too — the resolver reuses that loader through the sanctioned _code_bridge seam instead of reimplementing it.

Configuration — BotCommandsConfigSchema

Enable and tune the bridge with a commands block on each channel.

YAML

yaml
# bots.yaml
channels:
  telegram:
    token: env:TELEGRAM_BOT_TOKEN
    commands:
      allow_shell: false          # default; opt-in per deployment (see safety)
      expose: [summary, deploy]   # optional allow-list; omit → all project commands
      include_user_scope: false   # default; user ~/.praisonai commands excluded

Python

python
from praisonai_bot.bots._config_schema import (
    ChannelConfigSchema,
    BotCommandsConfigSchema,
)

channel = ChannelConfigSchema(
    platform="telegram",
    token="${TELEGRAM_BOT_TOKEN}",
    commands=BotCommandsConfigSchema(
        allow_shell=False,
        expose=["summary", "deploy"],
        include_user_scope=False,
    ),
)

Env

bash
export TELEGRAM_BOT_TOKEN=your_token_here
# Author .praisonai/commands/summary.md, then start the bot with a config
# whose channel enables commands. With an empty `expose`, every project
# command is surfaced automatically.
praisonai bot serve --config bots.yaml
OptionTypeDefaultDescription
allow_shellboolfalseEnables live ` !cmd substitution in bot chats. Also requires the command's own allow_shell: true` frontmatter — a deployment can never upgrade a command that declared shell off.
exposelist[str][]Optional allow-list of command names surfaced in chat. Empty → every project-scope command is exposed.
include_user_scopeboolfalseWhen true, also loads commands from ~/.praisonai/commands/. Off by default — bots serve the project, not the operator’s home dir.

Safety posture: double opt-in for shell

Shell substitution needs both the deployment and the command to say yes.

Diagram

false
default

true

false

true

Command with !Unsupported markdown: codespan in template

Deployment: allow_shell?

Render segment inert
rest of command still runs

Command frontmatter:
allow_shell?

Execute shell substitution

When either is false, the resolver drops the ! marker so the segment becomes plain backticks — the safe $ARGUMENTS and @file parts still interpolate normally instead of the whole command failing.

Precedence & collision rules

When two sources define the same command name, one wins.

SourcePrecedenceNotes
Built-in commands (/status, /help, …)highest — always winsLive commands (/automations, /blueprint) step aside if you register a same-name handler.
register_command(...) (Python)highExisting path, unchanged.
File-based .praisonai/commands/{name}.mdmediumWins over entry-point on collision. Project scope by default; user scope opt-in.
Entry-point praisonai.bot_commandslowestInstalled packages ship bot commands; file/project commands override.

/help and the native / menu are populated through the shared ChatCommandMixin, so file and entry-point commands appear in both automatically — no per-adapter wiring.

Packaged commands via entry-point

Installed packages add bot commands through the praisonai.bot_commands entry-point group — no forking.

toml
# pyproject.toml
[project.entry-points."praisonai.bot_commands"]
mypkg = "mypkg.bot_commands:commands"
python
# mypkg/bot_commands.py
commands = {
    "greet": {
        "description": "Say hi",
        "template": "Say hello to $ARGUMENTS.",
        "allow_shell": False,   # optional; safe default
    },
}
# Or expose a zero-argument callable that returns the same dict.

The loader accepts three spec shapes per command:

Spec shapeExampleMeaning
Bare string"greet": "Say hello to $ARGUMENTS."Template only; no description, allow_shell off.
Dict{"template": ..., "description": ..., "allow_shell": ...}Full record.
Objectany object with .template / .description / .allow_shell attributesFull record from a class instance.

User interaction flow

  1. A teammate authors .praisonai/commands/deploy.md with description: "Run the deploy checklist" and opens a PR.
  2. After merge, everyone using praisonai code gets /deploy — no restart of code.
  3. On the next bot restart, /deploy also appears in Telegram’s native / menu with the same description, and running it in chat replies with the interpolated template through the normal session.chat path.
  4. Same file. Two surfaces. No duplication.

Security & Allowlists

Built-in commands (/status, /new, /help) and custom commands registered via register_command() go through the same security pipeline as regular text messages. If allowed_users is set and the sender is not in it, the command is silently dropped with no reply. If unknown_user_policy="pair", unknown senders get the pairing flow before the command runs. If group_policy="mention_only", commands in groups still need a mention. observe behaves like mention_only for command dispatch — slash-commands in groups still need a mention; only free-form messages are passively recorded. Under command_only on Telegram, Discord and Slack, a plain @mention without a slash-command does not run the agent — only slash-commands do.

python
from praisonai.bots import TelegramBot
from praisonaiagents import BotConfig
from praisonaiagents import Agent

agent = Agent(name="assistant", instructions="Be helpful")
bot = TelegramBot(
    token="YOUR_TOKEN",
    agent=agent,
    config=BotConfig(allowed_users=["123456789"]),  # Only this user
)

# /help, /status, /new from any other user are now silently ignored
Diagram
AgentSecurity PipelineBotAllowed UserDisallowed UserAgentSecurity PipelineBotAllowed UserDisallowed UserSilently dropped (no response)/helpCheck userUser not allowed/helpCheck userUser allowedProcess commandHelp responseHelp message

For full allowlist setup, see the Bot Security guide. This security hardening was implemented in PR #1835.


Python Usage

Start bots programmatically — built-in commands are always available:

Telegram
from praisonai.bots import TelegramBot
from praisonaiagents import Agent

agent = Agent(name="assistant", instructions="Be helpful")
bot = TelegramBot(token="YOUR_TOKEN", agent=agent)

import asyncio
asyncio.run(bot.start())
# /status, /new, /stop, /model, /usage, /compress, /recap, /queue, /tasks, /help available automatically
# /stop all and /cancel (session-scoped stop) are handled by the gateway when the bot is gateway-routed
Discord
from praisonai.bots import DiscordBot
from praisonaiagents import Agent

agent = Agent(name="assistant", instructions="Be helpful")
bot = DiscordBot(token="YOUR_TOKEN", agent=agent)

import asyncio
asyncio.run(bot.start())
Slack
from praisonai.bots import SlackBot
from praisonaiagents import Agent

agent = Agent(name="assistant", instructions="Be helpful")
bot = SlackBot(token="YOUR_TOKEN", app_token="YOUR_APP_TOKEN", agent=agent)

import asyncio
asyncio.run(bot.start())
WhatsApp
from praisonai.bots import WhatsAppBot
from praisonaiagents import Agent

agent = Agent(name="assistant", instructions="Be helpful")
bot = WhatsAppBot(mode="web", agent=agent)
# /stop, /status, /new, /help registered automatically

import asyncio
asyncio.run(bot.start())

Native / Autocomplete (Telegram + Discord)

Typing / in Telegram or Discord surfaces the bot’s commands with descriptions through each platform’s native menu — no configuration needed.

Diagram
CommandRegistryPraisonAI BotTelegram/DiscordUserCommandRegistryPraisonAI BotTelegram/DiscordUserStartupChatbuild_command_menu_entries(user_id)[(name, description), …]publish_command_menu (set_my_commands / tree.sync)types “/”shows /status, /model, …taps /statusmessage “/status”replyreply

The bot publishes the menu automatically on startup — no user code required:

python
from praisonai.bots import TelegramBot
from praisonaiagents import Agent

agent = Agent(name="Support", instructions="Answer customer questions.")
bot = TelegramBot(token="YOUR_TOKEN", agent=agent)

import asyncio
asyncio.run(bot.start())  # /-menu appears in Telegram automatically

Custom commands flow through the same registry, so they appear alongside the built-ins on the next restart:

python
from praisonai.bots import TelegramBot
from praisonaiagents import Agent

agent = Agent(name="Support", instructions="Answer customer questions.")
bot = TelegramBot(token="YOUR_TOKEN", agent=agent)

async def summary(message):
    return "Latest summary..."

bot.register_command("summary", summary, description="Get the latest summary")

import asyncio
asyncio.run(bot.start())  # /summary appears alongside /status, /model, /usage in the / menu

Design Guarantees

The menu is a discoverability layer that never changes how commands run.

Per-Platform Behaviour

PlatformAPIName rulesDescription limit
Telegramset_my_commands(BotCommand(...))[a-z0-9_]{1,32}≤ 256 chars
DiscordCommandTree.sync() (application commands)[a-z0-9_-]{1,32}≤ 100 chars

Out-of-range names are skipped; over-length descriptions are truncated.

Best Practices

Restrict privileged commands

Configure admin_users and user_allowed_commands before enabling /learn, /stop, or /queue in production. See Command Access Control.

Enable run control for /stop and /queue

/stop and /queue need Bot Run Control with busy_mode for mid-run cancellation and follow-up queuing — without it, users get informational fallbacks only.

Choose the right stop scope

Use /stop (turn-scoped) when the user wants to interrupt only the current answer and let their queued follow-ups run. Use /stop all or /cancel (session-scoped) when the user regrets the whole line of questions — a session stop drains every message they queued behind the running turn, and the reply’s pending_cancelled count tells them exactly how many were dropped.

Compress before long sessions

Run /compress when conversations exceed ~20 turns. It preserves recent context whilst reclaiming window space for research or coding tasks.

Recap first — it's free

/recap never changes your history. Run it first to see the state of the conversation; only reach for /compress when you actually need to free window space.

Document custom commands in /help

Always pass a description to register_command(). Users discover extensions through /help, which filters to commands they are allowed to run.

Share one command file across code and bots

Author .praisonai/commands/{name}.md once and commit it. It runs in praisonai code and — once a channel’s commands block is enabled — in every bot chat, with no per-platform duplication. See File-based & Entry-point Commands.

Keep shell double-opted-in

Live ` !cmd substitution runs only when both the deployment (commands.allow_shell: true) and the command's own allow_shell: true` frontmatter agree. Leave both off unless a specific command genuinely needs shell — the segment degrades to inert backticks otherwise, so the rest of the command still works.

Isolate /automations and /blueprint per user

The scheduler’s suggestion store supports per-user isolation via a principal owner key (see Automation Suggestions → Multi-tenant isolation and Per-User Scheduler Isolation). When your bot passes the resolved chat identity to the store, each user sees and can accept/dismiss only their own suggestions, and one tenant cannot exhaust another’s pending cap or dedup window.

Until the adapter you use threads that identity through (or if you prefer a simpler policy), gate the whole command behind an admin allowlist by adding automations and blueprint to admin_users (not user_allowed_commands) in CommandAccessPolicy.

Related