> ## Documentation Index
> Fetch the complete documentation index at: https://praison.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Long-Running Bots

> Run bots 24/7 without memory leaks or silent corruption

Run Telegram, Discord, Slack, and other bots for weeks — bounded lock caches and agent-scoped locks keep memory stable with no extra configuration.

```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
from praisonaiagents import Agent
from praisonai.bots import Bot

agent = Agent(name="assistant", instructions="Be helpful")
bot = Bot("telegram", agent=agent)
bot.run()
```

The user messages your bot channel; session locks stay bounded while the same agent handles concurrent chats.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    U[👤 User] --> B[🤖 Bot]
    B --> M[BotSessionManager]
    M --> AL[Agent locks<br/>WeakKeyDictionary]
    M --> PL[Per-user locks<br/>LRU 10k / 1h TTL]

    classDef agent fill:#8B0000,stroke:#7C90A0,color:#fff
    classDef process fill:#189AB4,stroke:#7C90A0,color:#fff
    classDef config fill:#6366F1,stroke:#7C90A0,color:#fff

    class U agent
    class B,M agent
    class AL,PL process
```

## How It Works

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
sequenceDiagram
    participant User
    participant Agent
    participant Feature as Long-Running Bots

    User->>Agent: Request
    Agent->>Feature: Process request
    Feature-->>Agent: Result    Agent-->>User: Response
```

## Quick Start

<Steps>
  <Step title="Simple Usage">
    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    from praisonaiagents import Agent
    from praisonai.bots import Bot

    agent = Agent(name="assistant", instructions="Be helpful")
    bot = Bot("telegram", agent=agent)
    bot.run()  # Bounded locks are active by default
    ```

    Nothing extra is required — debounce, session, and run-control paths share the same bounded per-user lock cache.
  </Step>

  <Step title="With Configuration">
    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    from praisonaiagents import Agent
    from praisonaiagents import BotConfig
    from praisonai.bots import Bot

    agent = Agent(name="assistant", instructions="Be helpful")
    config = BotConfig(session_ttl=86400)  # Reap idle sessions after 24h

    bot = Bot("telegram", agent=agent, config=config)
    bot.run()
    ```
  </Step>
</Steps>

## What's Bounded by Default

| Resource            | Default limit              | When it cleans up                                        |
| ------------------- | -------------------------- | -------------------------------------------------------- |
| Per-user lock cache | 10,000 entries, 1 hour TTL | Idle, unlocked entries evict automatically               |
| Agent locks         | One per agent instance     | Removed when the agent is garbage-collected              |
| Session histories   | Opt-in via `session_ttl`   | See [Session Persistence](/features/session-persistence) |

<Note>
  Recreating agents per request is safe. Agent locks no longer reuse stale `id(agent)` keys, so unrelated users cannot share locks or swap histories.
</Note>

## Operational Knobs

For mid-run cancellation and stale session cleanup, see [Bot Run Control](/features/bot-run-control) — `SessionRunControl.cleanup_stale_sessions(max_age_seconds=3600)` removes abandoned run-control state.

## Multi-Agent Safety

Earlier releases keyed agent locks on `id(agent)`. CPython may reuse that integer after garbage collection, so long-running gateways that recreated agents per request could silently mix up two users' histories. Agent locks now follow agent lifetime via `WeakKeyDictionary`; per-user locks stay bounded even if you never call cleanup helpers.

<Info>
  **Released in PR #1972** — Upgrade to pick up the fix. See [Session Persistence → Bounded lock caches](/features/session-persistence#bounded-lock-caches) for details.
</Info>

## Best Practices

<AccordionGroup>
  <Accordion title="Reuse one agent instance per bot">
    Create the agent once at startup — agent locks follow instance lifetime via `WeakKeyDictionary`, so recreating agents per request is safe but wasteful.
  </Accordion>

  <Accordion title="Set session_ttl for busy channels">
    Default lock caches evict after 1 hour; set `BotConfig(session_ttl=…)` when conversations stay idle longer than your support SLA.
  </Accordion>

  <Accordion title="Run cleanup_stale_sessions periodically">
    Call `SessionRunControl.cleanup_stale_sessions` on a schedule in long-lived gateways to drop abandoned run-control state.
  </Accordion>

  <Accordion title="Upgrade after PR #1972">
    Ensure you are on a release that includes bounded agent locks — earlier builds could mix histories when `id(agent)` was reused.
  </Accordion>
</AccordionGroup>

## Auto-reconnect (default)

`Bot(...).run()` now supervises its own inbound connection — if the socket drops, `ChannelSupervisor` reconnects with capped exponential backoff and restarts an unhealthy channel, matching the resilience `BotOS` and the gateway already provide.

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph LR
    subgraph "Bot(...).run()"
        Start[🤖 Bot start] --> Supervisor[🛡 ChannelSupervisor]
        Supervisor --> Adapter[🔌 Adapter inbound loop]
        Adapter -->|drop| Backoff[⏱ capped backoff]
        Backoff --> Supervisor
        Adapter -->|healthy| Serving[✅ serving messages]
    end
    classDef input fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef process fill:#F59E0B,stroke:#7C90A0,color:#fff
    classDef result fill:#10B981,stroke:#7C90A0,color:#fff
    class Start,Adapter input
    class Supervisor,Backoff process
    class Serving result
```

<Steps>
  <Step title="Default supervision on Slack">
    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    from praisonaiagents import Agent
    from praisonai_bot import Bot

    agent = Agent(name="Support", instructions="Answer support questions")
    Bot("slack", agent=agent).run()   # auto-reconnects on drop
    ```
  </Step>

  <Step title="Same code, every platform">
    The identical pattern works for Discord, Email, Linear, and WhatsApp — no extra flags:

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    from praisonaiagents import Agent
    from praisonai_bot import Bot

    agent = Agent(name="Support", instructions="Answer support questions")
    Bot("discord", agent=agent).run()   # supervised too
    ```
  </Step>

  <Step title="Opt out under an external supervisor">
    Set `enable_supervision=False` when embedding, testing, or running under systemd/k8s:

    ```python theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
    from praisonaiagents import Agent
    from praisonai_bot import Bot

    agent = Agent(name="Support", instructions="Answer support questions")
    Bot("slack", agent=agent, enable_supervision=False).run()
    ```
  </Step>
</Steps>

### Which platforms are supervised by default?

| Platform                                           | Supervised by default? | Why                                                                        |
| -------------------------------------------------- | ---------------------- | -------------------------------------------------------------------------- |
| Slack, Discord, WhatsApp, Email, Linear, AgentMail | ✅ Yes                  | Standard adapters — supervision wraps the inbound run loop.                |
| Telegram                                           | ❌ No — self-managed    | Telegram already runs its own reconnect loop; wrapping would double-drive. |
| Relay transport                                    | ❌ No — external        | Out-of-process reconnect + scale-to-zero dormancy.                         |

### When to opt out (`enable_supervision=False`)

```mermaid theme={"theme":{"light":"vitesse-light","dark":"vitesse-dark"}}
graph TB
    Q[Who owns restarts?] --> A[Bot run alone in a process]
    Q --> B[systemd / k8s / external supervisor]
    Q --> C[Under a BotOS / gateway]
    Q --> D[Unit test with a fake adapter]
    A --> R1[Leave enable_supervision=True — default]
    B --> R2[enable_supervision=False]
    C --> R3[enable_supervision=False — BotOS supervises]
    D --> R2

    classDef decision fill:#6366F1,stroke:#7C90A0,color:#fff
    classDef default fill:#10B981,stroke:#7C90A0,color:#fff
    classDef optout fill:#F59E0B,stroke:#7C90A0,color:#fff
    class Q,A,B,C,D decision
    class R1 default
    class R2,R3 optout
```

<AccordionGroup>
  <Accordion title="Don't stack supervisors">
    If a `Bot` runs inside `BotOS` or under systemd/Kubernetes, set `enable_supervision=False`. Two supervisors on one connection means double reconnect attempts and double backoff — let the outer supervisor own restarts.
  </Accordion>

  <Accordion title="Email / Linear / WhatsApp / AgentMail — `Bot.run()` no longer returns immediately">
    Adapters whose `start()` returns immediately (Email IMAP poll, Linear/WhatsApp/AgentMail webhook servers) now stay supervised until they actually stop. Scripts that relied on `.run()` returning early should switch to `await bot.start()` and manage lifecycle explicitly, or spawn the run in a background task.
  </Accordion>
</AccordionGroup>

## Related

<CardGroup cols={2}>
  <Card title="Session Persistence" icon="floppy-disk" href="/docs/features/session-persistence">
    Bot session storage and bounded lock behaviour
  </Card>

  <Card title="Messaging Bots" icon="message-circle" href="/docs/features/messaging-bots">
    Platform setup, debounce, and chunking
  </Card>

  <Card title="Inbound DLQ" icon="inbox" href="/docs/features/inbound-dlq">
    Persist failed messages for replay
  </Card>

  <Card title="Cross-Platform Mirror" icon="repeat" href="/docs/features/cross-platform-mirror">
    One conversation across every channel
  </Card>
</CardGroup>
