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

# Channels

> Give each agent its own Telegram bot or Slack presence — and approve sensitive actions from your phone without opening the desktop app.

<div className="mxp-hero xc-border">
  <div className="exp-eyebrow mxp-rise">LIVE IN YOUR APPS</div>

  <h2 className="mxp-hero-title mxp-rise" style={{animationDelay:'0.06s'}}>
    Your agents, where <span style={{background:'linear-gradient(90deg,#24A4FF,#9000FF,#E3008E)',WebkitBackgroundClip:'text',backgroundClip:'text',color:'transparent'}}>your team already is.</span>
  </h2>

  <p className="mxp-hero-dek mxp-rise" style={{animationDelay:'0.13s'}}>
    Bind an agent to a Telegram bot or a Slack workspace. Message it from your phone. Approve sensitive actions with a tap. Keep it running 24/7 on a VPS — no desktop required.
  </p>
</div>

<div className="w-full py-10">
  <div className="actionist-tight flex flex-col gap-2 mb-6">
    <span className="actionist-section-eyebrow">See it in action</span>
    <h2 className="text-2xl md:text-3xl font-semibold tracking-tight text-gray-900 dark:text-gray-50">A conversation from start to approval.</h2>

    <p className="text-base text-gray-600 dark:text-gray-400 max-w-3xl">
      Illustrative Telegram thread — user message, agent processing, reply, then an inline approval for a sensitive action. No desktop needed. A phone tap is enough.
    </p>
  </div>

  <div className="xch-conv">
    <div className="xch-conv-bar">
      <div className="xch-conv-avatar">M</div>

      <div className="xch-conv-meta">
        <div className="xch-conv-name">Maya</div>
        <div className="xch-conv-sub">@maya\_actionist\_bot</div>
      </div>

      <div className="xch-conv-status">online</div>
    </div>

    <div className="xch-conv-body">
      <div className="xch-bubble-user xch-s1">
        Send the Q3 proposal to the Northwind team.
        <div className="xch-ts">09:41</div>
      </div>

      <div className="xch-bubble-event xch-s2">seen · reading context</div>

      <div className="xch-bubble-agent xch-s3">
        Got it — 47 recipients on the Northwind distribution list. Before I send, I need your sign-off on this one.
        <div className="xch-ts">09:41</div>
      </div>

      <div className="xch-approval xch-s4">
        <div className="xch-approval-msg">
          <strong>Send email to Northwind team</strong><br />
          47 recipients · 1 attachment (Q3\_Proposal.pdf)
        </div>

        <div className="xch-approval-btns">
          <button className="xch-btn-approve">Approve</button>
          <button className="xch-btn-deny">Deny</button>
        </div>
      </div>

      <div className="xch-bubble-user xch-s5">
        Approve
        <div className="xch-ts">09:42</div>
      </div>

      <div className="xch-bubble-event xch-s6">tool · send\_email · 47 recipients</div>

      <div className="xch-bubble-agent xch-s7">
        Done — proposal sent to all 47 contacts. Delivery receipts look clean.<span className="mxp-caret" />
        <div className="xch-ts">09:42</div>
      </div>
    </div>
  </div>

  <div className="exp-surface-grid" style={{marginTop:'20px'}}>
    <div className="exp-surface-tile">
      <strong>Approve from anywhere.</strong> The inline keyboard appears right in the chat thread. No desktop, no dashboard — a tap is all it takes.
    </div>

    <div className="exp-surface-tile">
      <strong>Context is preserved.</strong> Approval or denial goes back to the agent immediately. The workflow continues or aborts without losing the conversation thread.
    </div>

    <div className="exp-surface-tile">
      <strong>Same on Slack.</strong> The approval request uses Block Kit action buttons. Same one-tap flow, same agent-side handling.
    </div>
  </div>
</div>

<hr className="xc-divider" />

<div className="w-full py-10 xc-rise">
  <div className="actionist-tight flex flex-col gap-2 mb-6">
    <span className="actionist-section-eyebrow">In practice</span>
    <h2 className="text-2xl md:text-3xl font-semibold tracking-tight text-gray-900 dark:text-gray-50">A travel day, approved from a phone.</h2>

    <p className="text-base text-gray-600 dark:text-gray-400 max-w-3xl">
      Dario runs his sales agent on Telegram. On a travel day he handled every approval and query between flights — no laptop, no dashboard.
    </p>
  </div>

  <div className="xc-receipt">
    <div className="xc-receipt-head">APPROVED FROM MY PHONE</div>
    <div className="xc-receipt-sub">Dario · Sales — illustrative travel-day log</div>

    <div className="xc-receipt-row">
      <span>Morning pipeline briefing (agent DM, Telegram)</span>
      <span>2 min</span>
    </div>

    <div className="xc-receipt-row">
      <span>Approved: follow-up sequence to 12 Northwind contacts</span>
      <span>1 min</span>
    </div>

    <div className="xc-receipt-row">
      <span>Denied: outreach draft flagged as too aggressive</span>
      <span>1 min</span>
    </div>

    <div className="xc-receipt-row">
      <span>Approved: Q3 proposal sent to 6 prospects</span>
      <span>1 min</span>
    </div>

    <div className="xc-receipt-row">
      <span>Queried: open deal count and next follow-up dates</span>
      <span>2 min</span>
    </div>

    <div className="xc-receipt-row">
      <span>Approved: meeting summary logged to CRM (4 deals)</span>
      <span>1 min</span>
    </div>

    <div className="xc-receipt-total">
      <span>Total time on phone</span>
      <span>\~8 min</span>
    </div>

    <div className="xc-receipt-foot">ILLUSTRATIVE — BASED ON THE CAPABILITIES DOCUMENTED ON THIS PAGE</div>
  </div>
</div>

<hr className="xc-divider" />

<div className="w-full py-10 xc-rise">
  <div className="actionist-tight flex flex-col gap-2 mb-6">
    <span className="actionist-section-eyebrow">The idea</span>
    <h2 className="text-2xl md:text-3xl font-semibold tracking-tight text-gray-900 dark:text-gray-50">A channel is where an agent lives as a bot.</h2>

    <p className="text-base text-gray-600 dark:text-gray-400 max-w-3xl">
      Once an agent has a channel, it answers messages, reports back, and surfaces approval requests in that messaging app — without you ever switching to the Actionist desktop.
    </p>
  </div>

  <Columns cols={2}>
    <Card title="Telegram" icon="send">
      Each agent gets its own dedicated bot created through @BotFather. Users DM the bot directly; the agent replies in the same thread. Runs on the grammY SDK via Actionist's Gateway service.
    </Card>

    <Card title="Slack" icon="https://cdn.simpleicons.org/slack">
      Connect one Slack app per agent, or share a single company-level Slack app across all your agents and route each by its unique <code>@mention-name</code>. Built on Bolt SDK with Socket Mode — no public HTTPS endpoint needed.
    </Card>
  </Columns>

  <Note>
    Only Telegram and Slack are supported channels today. WhatsApp, email, and Discord are not available on the current release.
  </Note>
</div>

<div className="w-full py-10 xc-rise">
  <div className="actionist-tight flex flex-col gap-2 mb-6">
    <span className="actionist-section-eyebrow">Architecture</span>
    <h2 className="text-2xl md:text-3xl font-semibold tracking-tight text-gray-900 dark:text-gray-50">One bot per agent, or one Slack app for all.</h2>

    <p className="text-base text-gray-600 dark:text-gray-400 max-w-3xl">
      Telegram always gives each agent its own bot. Slack offers a choice: a dedicated bot per agent, or a shared company Slack app where users reach each agent by typing its <code>@mention-name</code> in any channel.
    </p>
  </div>

  <div className="xch-routing-diagram">
    <div className="xch-rd-col">
      <div className="xch-rd-label">Dedicated bot</div>

      <div className="xch-rd-row">
        <div className="xch-rd-node user">You</div>
        <div className="xch-rd-arrow"><span>DM</span></div>
        <div className="xch-rd-node bot">@maya\_bot</div>

        <div className="xch-rd-arrow">
          <span />
        </div>

        <div className="xch-rd-node agent">Maya agent</div>
      </div>
    </div>

    <div className="xch-rd-divider" />

    <div className="xch-rd-col">
      <div className="xch-rd-label">Shared Slack app</div>

      <div className="xch-rd-shared">
        <div className="xch-rd-senders">
          <div className="xch-rd-node user">Team member</div>
          <div className="xch-rd-inline-arrow"><span>@support help me</span></div>
          <div className="xch-rd-node user">Team member</div>
          <div className="xch-rd-inline-arrow"><span>@finance approve Q3</span></div>
        </div>

        <div className="xch-rd-node bot xch-rd-hub">Company Slack app</div>

        <div className="xch-rd-routes">
          <div className="xch-rd-inline-arrow"><span>mention routing</span></div>
          <div className="xch-rd-node agent">Support agent</div>
          <div className="xch-rd-inline-arrow"><span>mention routing</span></div>
          <div className="xch-rd-node agent">Finance agent</div>
        </div>
      </div>
    </div>
  </div>

  <p className="text-base text-gray-600 dark:text-gray-400 mt-6">
    With a shared Slack app, direct messages to the bot still route automatically — no mention required. Only messages in shared channels need the <code>@mention-name</code> prefix; bare channel messages are silently dropped.
  </p>
</div>

<hr className="xc-divider" />

<div className="w-full py-10 xc-rise">
  <div className="actionist-tight flex flex-col gap-2 mb-6">
    <span className="actionist-section-eyebrow">Setup</span>
    <h2 className="text-2xl md:text-3xl font-semibold tracking-tight text-gray-900 dark:text-gray-50">Three steps per platform.</h2>

    <p className="text-base text-gray-600 dark:text-gray-400 max-w-3xl">
      Open <strong>Agents</strong> in the sidebar, select the agent, and click the <strong>Channels</strong> tab. Pick your platform and follow the wizard. The connection-target badge at the top tells you whether this agent connects from <strong>Local Desktop</strong> or <strong>Paired VPS</strong>.
    </p>
  </div>

  <div className="xch-tabs exp-tool-shell xc-try">
    <div className="mxp-srch-chips">
      <label htmlFor="xch-tg"><input className="mxp-r" type="radio" name="xch-plat" id="xch-tg" defaultChecked />Telegram</label>
      <label htmlFor="xch-sl"><input className="mxp-r" type="radio" name="xch-plat" id="xch-sl" />Slack</label>
    </div>

    {/* ---- Telegram wizard ---- */}

    <div className="xch-panel-tg">
      <div className="xch-wizard">
        <div className="xch-wizard-step">
          <div className="xch-step-num">1</div>

          <div className="xch-step-body">
            <div className="xch-step-title">Talk to @BotFather to create a bot</div>

            <div className="xch-step-desc">
              Open Telegram and start a chat with <code>@BotFather</code>. Send <code>/newbot</code>, pick a display name, and choose a unique handle ending in <code>\_bot</code>. BotFather replies with a token in the format <code>123456:ABC-DEF…</code>.<br /><br />
              Back in Actionist, pick a saved credential from the combobox (recommended) or paste the token into the <strong>Bot token</strong> field. If you pick a vault credential, a shield icon confirms: <em>"Resolved from your vault at gateway connect. Never stored locally."</em>
            </div>

            <div className="xch-mock-field">
              <span className="xch-vault-hint">Bot token</span>
              <span style={{flex:1,opacity:0.5,overflow:'hidden',textOverflow:'ellipsis',whiteSpace:'nowrap'}}>••••••••••••••••••••••••••</span>
              <span className="xch-shield">vault</span>
            </div>

            <Tip>
              Pick a vault credential rather than pasting. Rotating the credential later updates every agent that uses it — no need to re-open each agent's Channels tab.
            </Tip>
          </div>
        </div>

        <div className="xch-wizard-step">
          <div className="xch-step-num">2</div>

          <div className="xch-step-body">
            <div className="xch-step-title">Restrict who can DM the bot (optional)</div>

            <div className="xch-step-desc">
              Enter comma-separated Telegram user IDs or <code>@usernames</code> in the allowlist field (placeholder: <code>123456789, @username</code>). Leave the field empty to allow anyone who finds the bot to message it.
            </div>

            <div className="xch-mock-field" style={{marginTop:'10px'}}>
              <span className="xch-vault-hint">Allowlist</span>
              <span style={{flex:1,opacity:0.35}}>123456789, @username</span>
            </div>

            <Warning>
              An empty allowlist is a security risk if you share the bot's username publicly. Add your own numeric Telegram user ID before sharing the bot handle with anyone else.
            </Warning>
          </div>
        </div>

        <div className="xch-wizard-step">
          <div className="xch-step-num">3</div>

          <div className="xch-step-body">
            <div className="xch-step-title">Test the connection</div>

            <div className="xch-step-desc">
              Click <strong>Connect</strong>. The button is disabled until a token or credential is present. Actionist calls <code>getMe</code> with the token to verify it resolves to a real bot.
              On success: status changes to <strong>Connected</strong> and shows the bot's username. On failure: <strong>Connection failed</strong> with the rejection detail from Telegram.
              On every subsequent app launch, Actionist reconnects automatically (up to 8 attempts, 1.5 s apart). Bot commands <code>/start</code>, <code>/help</code>, <code>/reset</code>, and <code>/stop</code> are registered at the same time.
            </div>

            <div className="xch-mock-connected">Connected as @maya\_actionist\_bot</div>
          </div>
        </div>
      </div>

      <div className="mt-6">
        {/* TODO(image): telegram-wizard-3-steps — IMAGE SPEC: Channels tab with the Telegram wizard fully expanded: Step 1 showing the credential combobox with a vault-bound credential selected and the shield + vault hint text visible beneath it; Step 2 showing the allowlist input with placeholder text; Step 3 showing the Connect button. A green 'Connected · token saved' status indicator is visible at the top of the Telegram card. — replace this CSS mock with the real screenshot */}

        <div className="mxp-window" style={{margin:'8px 0'}}>
          <div className="mxp-window-bar xch-mock-win-bar">
            <div className="xch-mock-win-dots">
              <span />

              <span />

              <span />
            </div>

            <div className="mxp-window-title">Channels — Telegram</div>
            <div className="xch-mock-win-badge connected">Connected · token saved</div>
          </div>

          <div className="xch-mock-win-body">
            <div className="xch-mock-step-row">
              <div className="xch-mock-step-badge">1</div>

              <div className="xch-mock-step-content">
                <div className="xch-mock-step-label">Talk to @BotFather to create a bot</div>

                <div className="xch-mock-field" style={{marginTop:'6px'}}>
                  <span className="xch-vault-hint">Bot token</span>
                  <span style={{flex:1,opacity:0.45}}>••••••••••••••••••••••</span>
                  <span className="xch-shield">vault</span>
                </div>

                <div className="xch-mock-vault-hint-row">Resolved from your vault at gateway connect. Never stored locally.</div>
              </div>
            </div>

            <div className="xch-mock-step-row">
              <div className="xch-mock-step-badge">2</div>

              <div className="xch-mock-step-content">
                <div className="xch-mock-step-label">Restrict who can DM the bot (optional)</div>

                <div className="xch-mock-field" style={{marginTop:'6px'}}>
                  <span className="xch-vault-hint">Allowlist</span>
                  <span style={{flex:1,opacity:0.3}}>123456789, @username</span>
                </div>
              </div>
            </div>

            <div className="xch-mock-step-row">
              <div className="xch-mock-step-badge">3</div>

              <div className="xch-mock-step-content">
                <div className="xch-mock-step-label">Test the connection</div>
                <div className="xch-mock-connected" style={{marginTop:'6px'}}>Connected as @maya\_actionist\_bot</div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>

    {/* ---- Slack wizard ---- */}

    <div className="xch-panel-sl">
      <div className="xch-wizard">
        <div className="xch-wizard-step">
          <div className="xch-step-num">1</div>

          <div className="xch-step-body">
            <div className="xch-step-title">Create a Slack app</div>

            <div className="xch-step-desc">
              Go to <code>api.slack.com/apps</code> and create a new app. You need to:

              <ol style={{margin:'8px 0 0 16px',padding:0,fontSize:'12.5px',lineHeight:1.7}}>
                <li>Enable <strong>Socket Mode</strong> under Settings — this maintains a persistent outbound WebSocket so no public HTTPS endpoint is needed.</li>
                <li>Grant these bot scopes under <strong>OAuth and Permissions</strong>: <code>chat:write</code>, <code>im:history</code>, <code>channels:history</code>, <code>groups:history</code>.</li>
                <li>Copy both tokens: the <strong>Bot token</strong> (starts <code>xoxb-</code>) and the <strong>App-level token</strong> (starts <code>xapp-</code>).</li>
              </ol>

              <br />

              Back in Actionist, enter or pick both tokens. Both must come from the same source — either both pasted or both picked from the vault. Mixing is rejected with: <em>"Both the bot token and the app-level token are required — paste both, or pick saved credentials for both."</em>
            </div>

            <div className="xch-mock-field" style={{marginTop:'10px'}}>
              <span className="xch-vault-hint">Bot token (xoxb-)</span>
              <span style={{flex:1,opacity:0.5}}>••••••••••••••••</span>
              <span className="xch-shield">vault</span>
            </div>

            <div className="xch-mock-field" style={{marginTop:'6px'}}>
              <span className="xch-vault-hint">App-level token (xapp-)</span>
              <span style={{flex:1,opacity:0.5}}>••••••••••••••••</span>
              <span className="xch-shield">vault</span>
            </div>
          </div>
        </div>

        <div className="xch-wizard-step">
          <div className="xch-step-num">2</div>

          <div className="xch-step-body">
            <div className="xch-step-title">Set a mention name (for shared channels)</div>

            <div className="xch-step-desc">
              Enter a short identifier for this agent. Actionist auto-sanitizes to lowercase <code>\[a-z0-9\_-]</code> as you type.<br /><br />
              This is how users route messages to this agent in a shared Slack channel: they type <code>@mention-name do something</code>. The mention name must be unique across every agent sharing the same Slack app — a duplicate produces a validation error on save.
            </div>

            <div className="xch-mock-field" style={{marginTop:'10px'}}>
              <span className="xch-vault-hint">Mention name</span>
              <span style={{flex:1,fontWeight:600}}>support</span>
            </div>

            <Info>
              Users messaging the bot directly in a DM do not need to use the mention name — direct messages route automatically without any prefix.
            </Info>
          </div>
        </div>

        <div className="xch-wizard-step">
          <div className="xch-step-num">3</div>

          <div className="xch-step-body">
            <div className="xch-step-title">Validate the tokens</div>

            <div className="xch-step-desc">
              Click <strong>Connect</strong>. Actionist calls <code>auth.test</code> with the bot token to confirm it is reachable.
              On success: status shows <strong>Connected to \[workspace name]</strong>. On failure: <strong>Connection failed</strong> — confirm that Socket Mode is enabled and all four scopes are granted.
            </div>

            <div className="xch-mock-connected">Connected to Acme Corp workspace</div>
          </div>
        </div>
      </div>

      <div className="mt-6">
        {/* TODO(image): slack-wizard-mention-name — IMAGE SPEC: Channels tab showing the Slack wizard at Step 2 'Set a mention name (for shared channels)', with the mention-name input filled in (e.g. 'support'). Both bot token and app-level token fields above show vault-bound credentials with shield icons. Two agent names are visible in the surrounding UI to suggest shared-app routing. — replace this CSS mock with the real screenshot */}

        <div className="mxp-window" style={{margin:'8px 0'}}>
          <div className="mxp-window-bar xch-mock-win-bar">
            <div className="xch-mock-win-dots">
              <span />

              <span />

              <span />
            </div>

            <div className="mxp-window-title">Channels — Slack</div>
            <div className="xch-mock-win-badge connected">Connected to Acme Corp workspace</div>
          </div>

          <div className="xch-mock-win-body">
            <div className="xch-mock-step-row">
              <div className="xch-mock-step-badge">1</div>

              <div className="xch-mock-step-content">
                <div className="xch-mock-step-label">Create a Slack app</div>

                <div className="xch-mock-field" style={{marginTop:'6px'}}>
                  <span className="xch-vault-hint">Bot token (xoxb-)</span>
                  <span style={{flex:1,opacity:0.45}}>••••••••••••••</span>
                  <span className="xch-shield">vault</span>
                </div>

                <div className="xch-mock-field" style={{marginTop:'4px'}}>
                  <span className="xch-vault-hint">App-level token (xapp-)</span>
                  <span style={{flex:1,opacity:0.45}}>••••••••••••••</span>
                  <span className="xch-shield">vault</span>
                </div>
              </div>
            </div>

            <div className="xch-mock-step-row xch-mock-step-active">
              <div className="xch-mock-step-badge active">2</div>

              <div className="xch-mock-step-content">
                <div className="xch-mock-step-label">Set a mention name (for shared channels)</div>

                <div className="xch-mock-field xch-mock-field-active" style={{marginTop:'6px'}}>
                  <span className="xch-vault-hint">Mention name</span>
                  <span style={{flex:1,fontWeight:700,color:'rgb(var(--actionist-purple))'}}>support</span>
                </div>

                <div className="xch-mock-agent-pills">
                  <span className="xch-mock-agent-pill">support</span>
                  <span className="xch-mock-agent-pill muted">finance</span>
                </div>
              </div>
            </div>

            <div className="xch-mock-step-row">
              <div className="xch-mock-step-badge">3</div>

              <div className="xch-mock-step-content">
                <div className="xch-mock-step-label">Validate the tokens</div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</div>

<div className="w-full py-10 xc-rise">
  <div className="actionist-tight flex flex-col gap-2 mb-6">
    <span className="actionist-section-eyebrow">Try it · routing</span>
    <h2 className="text-2xl md:text-3xl font-semibold tracking-tight text-gray-900 dark:text-gray-50">Watch mention routing in a shared channel.</h2>

    <p className="text-base text-gray-600 dark:text-gray-400 max-w-3xl">
      A shared Slack app routes messages by <code>@mention-name</code> prefix. Messages without a matching mention are silently dropped — they never reach any agent. Pick a message scenario:
    </p>
  </div>

  <div className="xch-route exp-tool-shell xc-try">
    <div className="mxp-srch-chips">
      <label htmlFor="xch-r1"><input className="mxp-r" type="radio" name="xch-route" id="xch-r1" defaultChecked />Mention routes to support</label>
      <label htmlFor="xch-r2"><input className="mxp-r" type="radio" name="xch-route" id="xch-r2" />Mention routes to finance</label>
      <label htmlFor="xch-r3"><input className="mxp-r" type="radio" name="xch-route" id="xch-r3" />Bare message — dropped</label>
    </div>

    {/* Panel 1: @support mention */}

    <div className="xch-route-panel xch-rp1">
      <div style={{marginBottom:'6px',fontSize:'11px',opacity:0.5,fontFamily:'"JetBrains Mono",monospace',letterSpacing:'0.06em',textTransform:'uppercase'}}>
        \#general — shared channel
      </div>

      <div className="xch-slack-msg">
        <div className="xch-slack-avatar" style={{background:'#486EFF'}}>JL</div>

        <div className="xch-slack-meta">
          <div className="xch-slack-sender">Jamie L <span className="xch-time">10:14 AM</span></div>

          <div className="xch-slack-text">
            <span className="xch-mention">@support</span> can you pull the open tickets for Northwind?
          </div>
        </div>
      </div>

      <div style={{paddingLeft:'58px'}}>
        <span className="xch-route-outcome routed">routed to support agent</span>
      </div>

      <div className="xch-slack-msg" style={{marginTop:'8px'}}>
        <div className="xch-slack-avatar" style={{background:'linear-gradient(135deg,#6C37FF,#C800B3)'}}>S</div>

        <div className="xch-slack-meta">
          <div className="xch-slack-sender">Support <span className="xch-time">10:14 AM</span></div>

          <div className="xch-slack-text">
            On it — pulling open tickets for Northwind now. Found 3 open, 1 escalated.
          </div>
        </div>
      </div>

      <div className="xch-route-explain">
        The message starts with <code>@support</code> — Actionist matches this to the agent whose mention name is <code>support</code> and forwards the full message text. The agent replies in the same thread.
      </div>
    </div>

    {/* Panel 2: @finance mention */}

    <div className="xch-route-panel xch-rp2">
      <div style={{marginBottom:'6px',fontSize:'11px',opacity:0.5,fontFamily:'"JetBrains Mono",monospace',letterSpacing:'0.06em',textTransform:'uppercase'}}>
        \#general — shared channel
      </div>

      <div className="xch-slack-msg">
        <div className="xch-slack-avatar" style={{background:'#AC00D9'}}>PR</div>

        <div className="xch-slack-meta">
          <div className="xch-slack-sender">Priya R <span className="xch-time">11:02 AM</span></div>

          <div className="xch-slack-text">
            <span className="xch-mention">@finance</span> please run the Q3 budget summary
          </div>
        </div>
      </div>

      <div style={{paddingLeft:'58px'}}>
        <span className="xch-route-outcome routed">routed to finance agent</span>
      </div>

      <div className="xch-slack-msg" style={{marginTop:'8px'}}>
        <div className="xch-slack-avatar" style={{background:'linear-gradient(135deg,#486EFF,#24A4FF)'}}>F</div>

        <div className="xch-slack-meta">
          <div className="xch-slack-sender">Finance <span className="xch-time">11:03 AM</span></div>

          <div className="xch-slack-text">
            Q3 budget summary running — will post the report here in about 2 minutes.
          </div>
        </div>
      </div>

      <div className="xch-route-explain">
        <code>@finance</code> matches the second agent's mention name. The same Slack app hosts both agents; the mention prefix determines which one handles the message.
      </div>
    </div>

    {/* Panel 3: bare message dropped */}

    <div className="xch-route-panel xch-rp3">
      <div style={{marginBottom:'6px',fontSize:'11px',opacity:0.5,fontFamily:'"JetBrains Mono",monospace',letterSpacing:'0.06em',textTransform:'uppercase'}}>
        \#general — shared channel
      </div>

      <div className="xch-slack-msg">
        <div className="xch-slack-avatar" style={{background:'#5B6380'}}>TK</div>

        <div className="xch-slack-meta">
          <div className="xch-slack-sender">Tom K <span className="xch-time">11:25 AM</span></div>

          <div className="xch-slack-text">
            Has anyone seen the latest Northwind contract?
          </div>
        </div>
      </div>

      <div style={{paddingLeft:'58px'}}>
        <span className="xch-route-outcome dropped">silently dropped</span>
      </div>

      <div className="xch-route-explain">
        No <code>@mention-name</code> prefix — the message is a general team conversation. Actionist ignores it. Neither agent wakes up; no reply is sent. Direct messages to the bot always route without a mention; only shared-channel messages need the prefix.
      </div>
    </div>
  </div>
</div>

<div className="w-full py-10 xc-rise">
  <div className="actionist-tight flex flex-col gap-2 mb-6">
    <span className="actionist-section-eyebrow">Control</span>
    <h2 className="text-2xl md:text-3xl font-semibold tracking-tight text-gray-900 dark:text-gray-50">Manage a running agent from the chat.</h2>

    <p className="text-base text-gray-600 dark:text-gray-400 max-w-3xl">
      Four commands are registered on every Telegram bot at connect time. Send them in the conversation from any device.
    </p>
  </div>

  <div className="xch-cmd-grid">
    <div className="xch-cmd-tile">
      <code>/start</code>
      <div className="xch-cmd-desc">Starts the bot and greets you.</div>
    </div>

    <div className="xch-cmd-tile">
      <code>/help</code>
      <div className="xch-cmd-desc">Lists available commands.</div>
    </div>

    <div className="xch-cmd-tile">
      <code>/reset</code>
      <div className="xch-cmd-desc">Clears the conversation transcript so the agent starts with fresh context. The chat row, agent binding, and Telegram peer link are preserved — only the transcript is rotated. Use this when the agent has drifted after a long session.</div>
    </div>

    <div className="xch-cmd-tile">
      <code>/stop</code>
      <div className="xch-cmd-desc">Aborts any in-flight agent work immediately. Conversation context is preserved — your next message continues from the current state.</div>
    </div>
  </div>

  <Note>
    <code>/reset</code> and <code>/stop</code> are Telegram bot commands registered at connect time. Slack does not have equivalent slash commands.
  </Note>

  <AccordionGroup>
    <Accordion title="Understand the difference between /reset and /stop" icon="refresh-cw" defaultOpen={false}>
      Both are Telegram bot commands, but they do different things:

      * <code>/reset</code> — clears the agent's transcript context. The chat row, agent binding, and Telegram peer link are untouched. Use it when the agent has accumulated context from a long session and is no longer behaving as expected.
      * <code>/stop</code> — aborts any work the agent is actively executing right now. Conversation context is preserved; your next message continues normally.

      Neither command removes the bot connection or the agent config.
    </Accordion>

    <Accordion title="Inline approvals on Telegram and Slack" icon="square-check">
      When an agent reaches a sensitive action requiring your sign-off, it pauses and sends you an interactive approval request — right in the chat.

      * On <strong>Telegram</strong>: the request appears with <strong>Approve</strong> and <strong>Deny</strong> buttons rendered as an inline keyboard below the message.
      * On <strong>Slack</strong>: the same request uses Block Kit action buttons in the message.

      Tap the button to respond. The agent receives your decision immediately and either proceeds or aborts — without you opening the desktop app.

      <Check>
        Inline approvals work from any device that receives your Telegram or Slack notifications. A phone tap is all that's needed.
      </Check>

      <div className="mt-4">
        {/* TODO(image): inline-approval-telegram — IMAGE SPEC: Telegram mobile screenshot showing an agent mid-workflow with an approval request message (e.g. 'Send email to client list — 47 recipients') and two inline keyboard buttons beneath it: 'Approve' and 'Deny'. Show the dark Telegram UI on a phone screen to make the mobile context clear. — replace this CSS mock with the real screenshot */}

        <div className="xch-mock-phone">
          <div className="xch-mock-phone-bar">
            <div className="xch-mock-phone-avatar">M</div>

            <div className="xch-mock-phone-meta">
              <div className="xch-mock-phone-name">Maya</div>
              <div className="xch-mock-phone-sub">@maya\_actionist\_bot · online</div>
            </div>
          </div>

          <div className="xch-mock-phone-body">
            <div className="xch-mock-phone-msg agent">
              I need your sign-off before sending.
              <div className="xch-ts">09:43</div>
            </div>

            <div className="xch-mock-phone-approval">
              <div className="xch-mock-phone-approval-text">
                <strong>Send email to client list</strong><br />
                47 recipients · Q3\_Report.pdf
              </div>

              <div className="xch-mock-phone-btns">
                <button className="xch-mock-phone-btn approve">Approve</button>
                <button className="xch-mock-phone-btn deny">Deny</button>
              </div>
            </div>
          </div>
        </div>
      </div>
    </Accordion>
  </AccordionGroup>
</div>

<hr className="xc-divider" />

<div className="w-full py-10 xc-rise">
  <div className="actionist-tight flex flex-col gap-2 mb-6">
    <span className="actionist-section-eyebrow">Availability</span>
    <h2 className="text-2xl md:text-3xl font-semibold tracking-tight text-gray-900 dark:text-gray-50">24/7 uptime requires a VPS.</h2>

    <p className="text-base text-gray-600 dark:text-gray-400 max-w-3xl">
      Agents running on Local Desktop stop responding when your machine sleeps. Pair the agent with a Paired VPS runtime to keep bots online around the clock.
    </p>
  </div>

  <Warning>
    A Local Desktop agent's bots go offline when your computer sleeps or the app closes. For always-on channel bots, assign the agent to a Paired VPS in the <strong>Cloud</strong> tab before connecting channels.
  </Warning>

  <p className="text-base text-gray-600 dark:text-gray-400 mb-4">
    The <strong>Channels</strong> tab shows a <strong>RuntimeScopeBadge</strong> at the top — either <span className="xch-scope-badge local">Local Desktop</span> or <span className="xch-scope-badge vps">Paired VPS</span> — so you always know which runtime owns the connection.
  </p>

  When an agent is homed to a Paired VPS and you open its Channels tab from the desktop app:

  * The tab is <strong>read-only</strong>. The wizard fields are replaced by a "Managed on Paired VPS" card.
  * To edit channel settings, open Actionist on the VPS itself.

  When you click <strong>Connect</strong> from the VPS runtime, the status shows "Saved — connecting on your VPS…" while the gateway connects in the background, then updates to "Connected as @username" (Telegram) or "Connected to \[workspace]" (Slack).

  <div className="mt-6">
    {/* TODO(image): vps-channels-managed-elsewhere — IMAGE SPEC: The Channels tab in read-only state, viewed from the desktop app for a VPS-homed agent. Show the 'Paired VPS' RuntimeScopeBadge at the top of the tab, and the 'Managed on Paired VPS' card replacing the normal wizard fields for both Telegram and Slack. The card shows the title and the body text explaining that channels are connected on the VPS. — replace this CSS mock with the real screenshot */}

    <div className="mxp-window" style={{margin:'8px 0'}}>
      <div className="mxp-window-bar xch-mock-win-bar">
        <div className="xch-mock-win-dots">
          <span />

          <span />

          <span />
        </div>

        <div className="mxp-window-title">Channels</div>
        <div className="xch-mock-win-badge vps">Paired VPS</div>
      </div>

      <div className="xch-mock-win-body">
        <div className="xch-mock-readonly-card">
          <div className="xch-mock-readonly-icon">
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <rect x="3" y="11" width="18" height="11" rx="2" ry="2" />

              <path d="M7 11V7a5 5 0 0 1 10 0v4" />
            </svg>
          </div>

          <div className="xch-mock-readonly-text">
            <strong>Managed on Paired VPS</strong>
            <span>Channel settings for this agent are connected and managed on its Paired VPS. Open Actionist on the VPS to edit these settings.</span>
          </div>
        </div>

        <div className="xch-mock-readonly-card" style={{marginTop:'8px'}}>
          <div className="xch-mock-readonly-icon">
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
              <rect x="3" y="11" width="18" height="11" rx="2" ry="2" />

              <path d="M7 11V7a5 5 0 0 1 10 0v4" />
            </svg>
          </div>

          <div className="xch-mock-readonly-text">
            <strong>Managed on Paired VPS</strong>
            <span>Channel settings for this agent are connected and managed on its Paired VPS. Open Actionist on the VPS to edit these settings.</span>
          </div>
        </div>
      </div>
    </div>
  </div>

  <Card title="Set up cloud runtime" icon="cloud" href="/Folders/cloud" horizontal>
    Pair a VPS to keep agents and their channel bots running when the desktop is offline.
  </Card>
</div>

<div className="w-full py-10 xc-rise">
  <div className="actionist-tight flex flex-col gap-2 mb-6">
    <span className="actionist-section-eyebrow">Security</span>
    <h2 className="text-2xl md:text-3xl font-semibold tracking-tight text-gray-900 dark:text-gray-50">Tokens never touch local storage.</h2>

    <p className="text-base text-gray-600 dark:text-gray-400 max-w-3xl">
      When you pick a vault credential instead of pasting a token, Actionist resolves it server-side at connect time. The plaintext token is never written to the agent config or local disk.
    </p>
  </div>

  {/* TODO(image): credential-picker-vault-hint — IMAGE SPEC: The Channels tab credential combobox with a saved credential selected. Below the combobox, a shield icon and the vault hint text: 'Resolved from your vault at gateway connect. Never stored locally.' The label 'Bot token' is visible above the combobox. The rest of Telegram Step 1 is visible for context. — replace this CSS mock with the real screenshot */}

  <div className="mxp-window" style={{margin:'8px 0'}}>
    <div className="mxp-window-bar xch-mock-win-bar">
      <div className="xch-mock-win-dots">
        <span />

        <span />

        <span />
      </div>

      <div className="mxp-window-title">Telegram — Step 1</div>
    </div>

    <div className="xch-mock-win-body" style={{padding:'18px 20px'}}>
      <div className="xch-mock-cred-label">Bot token</div>

      <div className="xch-mock-field xch-mock-field-active">
        <span className="xch-vault-hint">telegram-bot-maya</span>

        <span style={{flex:1}} />

        <span className="xch-shield">vault</span>
      </div>

      <div className="xch-mock-vault-hint-row xch-mock-vault-hint-prominent">
        <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
          <path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z" />
        </svg>

        Resolved from your vault at gateway connect. Never stored locally.
      </div>
    </div>
  </div>

  <div className="exp-surface-grid">
    <div className="exp-surface-tile xch-sec-tile">
      <strong>Allowlist gating</strong>
      <p>Unauthorized Telegram users are rejected before any message reaches the agent.</p>
    </div>

    <div className="exp-surface-tile xch-sec-tile">
      <strong>Socket Mode</strong>
      <p>The Slack app maintains a persistent outbound WebSocket — there is no public HTTPS endpoint to expose or protect.</p>
    </div>

    <div className="exp-surface-tile xch-sec-tile">
      <strong>Runtime-scope isolation</strong>
      <p>A desktop runtime cannot start bots homed to a VPS, and vice versa. The scope check runs at connect time.</p>
    </div>
  </div>

  If the vault entry a channel is bound to is later deleted, the credential field shows a stale-binding chip: <strong>"Bound credential not found — re-bind required."</strong> Clear the stale binding, pick or paste a valid token, and reconnect.

  The footer of the Channels tab names the home runtime (Local Desktop or Paired VPS) and confirms that tokens are vault-bound to this agent only.
</div>

<div className="w-full py-10 xc-rise">
  <div className="actionist-tight flex flex-col gap-2 mb-6">
    <span className="actionist-section-eyebrow">Under the hood</span>
    <h2 className="text-2xl md:text-3xl font-semibold tracking-tight text-gray-900 dark:text-gray-50">Five stops from message to reply.</h2>
  </div>

  <div className="mxp-pipe">
    <div className="mxp-pipe-node"><strong>Messaging app</strong><span>You send a DM or mention in Telegram or Slack.</span></div>

    <div className="mxp-pipe-link" />

    <div className="mxp-pipe-node"><strong>Gateway</strong><span>Actionist's Gateway service receives the webhook or Socket Mode event.</span></div>

    <div className="mxp-pipe-link d2" />

    <div className="mxp-pipe-node"><strong>Allowlist check</strong><span>The sender ID is checked against the bot's allowlist before anything reaches the agent.</span></div>

    <div className="mxp-pipe-link d3" />

    <div className="mxp-pipe-node"><strong>Agent runtime</strong><span>The message is forwarded to the bound agent, which runs its tools, memory, and skills.</span></div>

    <div className="mxp-pipe-link d4" />

    <div className="mxp-pipe-node"><strong>Reply</strong><span>The agent's response (or approval request) is sent back through the same channel.</span></div>
  </div>
</div>

<div className="w-full py-10 xc-rise">
  <div className="actionist-tight flex flex-col gap-2 mb-6">
    <span className="actionist-section-eyebrow">Best practices</span>
    <h2 className="text-2xl md:text-3xl font-semibold tracking-tight text-gray-900 dark:text-gray-50">Tips that prevent the common issues.</h2>
  </div>

  <AccordionGroup>
    <Accordion title="Use vault credentials, not pasted tokens" icon="vault" defaultOpen={true}>
      Pasting a token stores it in the agent config on disk. Picking a vault credential means the token is resolved server-side at connect time and never written locally. When you rotate the credential in the vault, every agent that references it updates automatically — no need to re-open each agent's Channels tab.

      <Card title="Credentials vault" icon="key" href="/Folders/credentials" horizontal>
        How to save, rotate, and manage credentials centrally.
      </Card>
    </Accordion>

    <Accordion title="Lock down the Telegram allowlist before sharing the bot username" icon="shield-check">
      An empty allowlist means anyone who finds the bot's username can send it messages. Add your own numeric Telegram user ID — and any teammates' IDs — immediately after connecting, before you share the handle publicly. Values are comma-separated; both numeric IDs and <code>@usernames</code> are accepted.
    </Accordion>

    <Accordion title="Slack DMs don't need a mention; shared channels do" icon="at-sign">
      When a user messages the Slack bot in a DM, the message routes to the agent automatically. In a shared channel, only messages prefixed with <code>@mention-name</code> are routed — bare channel messages are silently dropped. Make sure your team knows to include the mention when they're not in a DM.
    </Accordion>

    <Accordion title="Keep Slack mention names short, lowercase, and unique" icon="tag">
      The mention name is auto-sanitized to <code>\[a-z0-9\_-]</code> as you type. Duplicate names across agents sharing the same Slack app produce a server-side validation error on save. Keep each name distinct: <code>support</code>, <code>finance</code>, <code>research</code>.
    </Accordion>

    <Accordion title="Understand the difference between /reset and /stop" icon="refresh-cw">
      Both are Telegram bot commands, but they do different things:

      * <code>/reset</code> — clears the agent's transcript context. The chat row, agent binding, and Telegram peer link are untouched. Use it when the agent has accumulated context from a long session and is no longer behaving as expected.
      * <code>/stop</code> — aborts any work the agent is actively executing right now. Conversation context is preserved; your next message continues normally.

      Neither command removes the bot connection or the agent config.
    </Accordion>

    <Accordion title="Resolve TELEGRAM_CONSUMER_CONFLICT (HTTP 409)" icon="triangle-alert">
      This error appears when two runtimes are both polling the same bot token — typically the desktop app and a VPS running simultaneously. Telegram only allows one active poller per token.

      Actionist surfaces the conflict in the Channels tab and retries automatically (30 s, 60 s, 120 s, 300 s, then terminal error). To fix it: disconnect the bot on whichever runtime should not own it, then reconnect on the correct one.
    </Accordion>

    <Accordion title="For 24/7 bots, pair channels with a VPS runtime" icon="cloud">
      Local Desktop agents stop answering when the machine sleeps or the app closes. Assign the agent to a Paired VPS in the <strong>Cloud</strong> tab first, then connect its channels from the VPS runtime. The bot runs headlessly, independently of your laptop.
    </Accordion>
  </AccordionGroup>
</div>

<div className="w-full py-10 xc-rise">
  <div className="actionist-tight flex flex-col gap-2 mb-6">
    <span className="actionist-section-eyebrow">Reference</span>
    <h2 className="text-2xl md:text-3xl font-semibold tracking-tight text-gray-900 dark:text-gray-50">Limits and defaults.</h2>
  </div>

  <div className="mxs-limits xch-limits">
    <div className="mxs-limit xch-limit"><b className="xc-count" style={{"--xc-to":"4000"}} data-suffix=""><span className="xc-real">4,000</span></b><span>Telegram message chunk size (effective characters)</span></div>
    <div className="mxs-limit xch-limit"><b className="xc-count" style={{"--xc-to":"4096"}} data-suffix=""><span className="xc-real">4,096</span></b><span>Telegram message hard limit (platform)</span></div>
    <div className="mxs-limit xch-limit"><b className="xc-count" style={{"--xc-to":"10000"}} data-suffix=""><span className="xc-real">10,000</span></b><span>Text attachment: characters injected</span></div>
    <div className="mxs-limit xch-limit"><b><span>5 MiB</span></b><span>Photo attachment: max size</span></div>
    <div className="mxs-limit xch-limit"><b><span>5 min</span></b><span>Message deduplication window</span></div>
    <div className="mxs-limit xch-limit"><b className="xc-count" style={{"--xc-to":"8"}} data-suffix=""><span className="xc-real">8</span></b><span>Auto-connect startup attempts</span></div>
    <div className="mxs-limit xch-limit"><b><span>1,500 ms</span></b><span>Auto-connect delay between attempts</span></div>
    <div className="mxs-limit xch-limit"><b><span>30 s → 300 s</span></b><span>Telegram conflict retry schedule (then terminal error)</span></div>
  </div>

  | Setting                              | Value                                                                                         |
  | ------------------------------------ | --------------------------------------------------------------------------------------------- |
  | Text attachment: supported types     | `.txt` `.md` `.csv` `.json` `.html` `.xml` `.yaml` `.yml` `.toml` `.ini` `.cfg` `.log` `.env` |
  | Photo attachment: photos per message | First photo only (passed as base64 for vision)                                                |
  | Required Slack bot scopes            | `chat:write`, `im:history`, `channels:history`, `groups:history`                              |
  | Slack connection mode                | Socket Mode (persistent outbound WebSocket, no public endpoint)                               |

  <Info>
    Long Telegram replies split automatically at 4,000 characters, breaking first at paragraph boundaries, then newlines, then spaces. If Markdown causes a parse error, the service falls back to plain text automatically.
  </Info>
</div>

<div className="w-full py-10 xc-rise">
  <div className="actionist-cta-panel xc-border">
    <div className="relative z-10 flex flex-col items-center gap-5 max-w-2xl mx-auto">
      <span className="actionist-eyebrow">Five minutes</span>

      <h2 className="text-2xl md:text-3xl font-semibold tracking-tight text-white leading-tight">
        Put your agents on Telegram and Slack today.
      </h2>

      <p className="text-base md:text-lg text-white/85">
        Open the Dashboard, pick an agent, and follow the three-step wizard. Five minutes to a bot your team can message from anywhere.
      </p>

      <div className="flex flex-wrap items-center justify-center gap-3 pt-2">
        <a className="actionist-cta-btn-primary" href="https://app.actionist.ai" target="_blank" rel="noreferrer">
          Open the Dashboard

          <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round">
            <path d="M5 12h14" />

            <path d="m12 5 7 7-7 7" />
          </svg>
        </a>

        <a className="actionist-cta-btn-secondary" href="mailto:support@actionist.ai">
          Email the team
        </a>
      </div>
    </div>
  </div>
</div>

<div className="w-full pt-2 pb-6 xc-rise">
  <p className="actionist-trust-line">
    Bot tokens vault-bound to this agent only.<br />
    Allowlist gating · Socket Mode · Runtime-scope isolation.
  </p>
</div>

<div className="w-full pb-16 xc-rise">
  <div className="actionist-tight flex flex-col gap-2 mb-6">
    <span className="actionist-section-eyebrow">Keep going</span>
    <h2 className="text-2xl md:text-3xl font-semibold tracking-tight text-gray-900 dark:text-gray-50">Next steps.</h2>
  </div>

  <Columns cols={4}>
    <Card title="Agents" icon="users" href="/Folders/agents">
      The Channels tab lives inside the agent studio. Configure identity, tools, and approval mode alongside channel bindings.
    </Card>

    <Card title="Credentials vault" icon="key" href="/Folders/credentials">
      Save, rotate, and manage bot tokens centrally so every agent stays current automatically.
    </Card>

    <Card title="Cloud runtime" icon="cloud" href="/Folders/cloud">
      Pair a VPS to keep channel bots running 24/7 — even when your laptop is closed.
    </Card>

    <Card title="Schedules" icon="calendar-clock" href="/Folders/schedules">
      Combine a channel with a schedule for agents that report proactively on a recurring cadence.
    </Card>
  </Columns>
</div>
