---
name: plungeai-bot-agent
description: "Design and emit ONE PlungeAI (Ocean Studio) bot agent — a scheduled, unattended `type: harness` mission that reports to the user's channels (in-app, email, Slack, Telegram, WhatsApp, Discord) — as CNL YAML plus a bot-config block (cron schedule, delivery targets, optional run-on-my-computer). Use when the Studio Think composer has \"Bot agent\" selected, or when a user asks for a bot, a scheduled agent, a daily/weekly brief, a watcher, a monitor, a digest, or \"something that runs on its own and tells me\". Triggers: \"bot\", \"every morning\", \"schedule\", \"report to Slack\", \"watch and alert\", \"digest\". NOT for interactive agents (plungeai-agentic-agent) or multi-task pipelines (plungeai-workflows)."
---

# Bot agent — a mission that runs on a schedule and reports

A bot is an agentic agent with three extra facts: WHEN it runs, WHERE the result goes, and
whether its hands are the user's own computer. Studio saves the mission under "Bots" and
creates the schedule + delivery from your bot-config block. Unattended runs cannot ask the
user anything: no `ask_user`, no `platform_action`, and every risky tool is either excluded
or `permissions: deny`.

## The conversation (before any YAML)

1. **Job.** One sentence: what the bot produces each run (a brief, an alert, a digest).
2. **Cadence.** Ask for the schedule in words, propose the cron, confirm it. Five fields,
   UTC unless the user gives a timezone (convert and say so).
3. **Where it reports.** Ask which channels: `inapp` (Studio inbox, always safe), `email`
   (owner or listed addresses), `slack` (DM the owner or a channel id), `telegram`,
   `whatsapp`, `discord` (require a paired chat — say the user must pair first if unsure).
   Default `inapp` when the user has no preference.
4. **Hands.** Cloud (default) or "runs on this computer" (`local: true`) when it must read
   local files, control local apps, or use local agents. Local bots pin `agent: harness-agent`.
5. **Fence + capabilities + budget** exactly as for an agentic agent: smallest
   `allowed_tools`, ids only from AVAILABLE CAPABILITIES and live `registry_search`,
   `effort: standard` unless argued, 2–4 `success_criteria`.

Confirm, then emit BOTH blocks.

## Output contract — two fenced blocks, in this order

```yaml
workflow:
  name: "<short bot name>"
  description: "<one line: what it reports and how often>"
  tasks:
    - type: harness
      id: bot
      agent: harness-agent
      goal: "{input}"
      mission: |
        You are <name>, a bot that <job>. Each run you <steps in 2–4 sentences>.
        Output <format: bullets / table / one paragraph>. Keep it under <n> words.
        End with `[SILENT]` (or make your whole result `[SILENT]`) when nothing is worth
        reporting this run — the platform then delivers to no channel.
        When your task is finished, call the task_complete tool with your final result.
      effort: standard
      allowed_tools: [web_search, web_fetch, task_complete]
      success_criteria:
        - <statement the bot verifies before finishing>
```

```bot-config
schedule: "0 7 * * 1-5"
deliver:
  - channel: inapp
  - channel: slack
    chat_id: C0123ABCD
local: false
prompt: "<the per-run input the schedule sends as {input}>"
```

Rules the lint enforces — violating them makes the save fail:
- ONE task, `type: harness`, `agent: harness-agent`, `goal: "{input}"`, mission ends with the
  task_complete sentence.
- `allowed_tools` MUST include `task_complete` and MUST NOT include `ask_user` or
  `platform_action` (nobody is there to answer). `skill_manage` needs
  `permissions: {skill_manage: allow}` to run unattended — leave it out unless asked.
- No `memory_owner` (the platform scopes bot memory itself). No emails or ids in the YAML;
  recipients belong in bot-config `deliver`, not in the mission.
- `local: true` goes in BOTH blocks — on the harness task (the runtime reads it to route tool
  calls to the machine) and in bot-config (so the scheduler skips cleanly when the machine is
  offline) — and only with `agent: harness-agent`; add `local_agents: [ids]` from the user's
  connected local agents when they name them, else leave it out (all connected agents).

## bot-config fields

- `schedule` — 5-field cron, required. Examples: `0 7 * * 1-5` weekdays 07:00, `*/30 * * * *`
  every 30 min, `0 18 * * 5` Fridays 18:00.
- `deliver` — list of `{channel, chat_id?, to?, phone_number_id?}`: `inapp` (no fields),
  `email` (`to` optional = owner), `slack` (`chat_id` C… channel or `to` U… member; none =
  DM the owner), `telegram`/`discord` (`chat_id`), `whatsapp` (`to` number +
  `phone_number_id`). Default `[{channel: inapp}]`.
- `local` — `true` only for run-on-my-computer bots. Default `false`.
- `prompt` — the per-run input. Default empty (the mission's goal alone drives the run).
- `budget_usd_run` / `budget_usd_month` — optional non-negative spend caps. Per-run stops the
  loop once its token cost exceeds the cap; per-month is enforced by the scheduler.
- `permissions` — optional class presets `{ pay|send|write|delete: ask|allow|deny }` persisted
  on the bot. `pay: ask` is the safe default for anything money-adjacent. `allow` lets that
  class run automatically unattended, but **never lowers the money floor** (`buy, pay, transfer,
  send, withdraw, …` always pause for approval, even with `pay: allow`). A team admin can LOCK a
  class from the Studio Permissions dialog, which forces its `allow` back up to `ask`.

## Permissions — approval rules

Per-tool `permissions` on the mission gate individual tools. Per-CLASS presets (bot-config
`permissions`, or the ⋮ → Permissions dialog) gate whole action classes. The order the loop
resolves a call: the money floor (always `ask`), then an admin lock, then the tool-name preset,
then the class preset, then allow. Safe default for a bot that touches money or deletes:
`permissions: { pay: ask, delete: ask }`. An unattended `pay: allow` proceeds automatically for
non-floor money ops; a floor op still pauses to `needs_approval` (the run can sit up to 7 days
awaiting the owner) — the `[SILENT]` output contract must never suppress that pause notice.

## The fence for unattended runs

Allowed vocabulary is the same 24 loop tools as an agentic agent; read `plungeai-agentic-agent`
for the catalog. Bots add these rules: exclude `ask_user`, `platform_action`; prefer read
paths (`web_search web_fetch read_file recall_history knowledge`) plus the agents the job
needs via `call_agent` with a tight `allowed_agents`; anything that sends or pays needs the
user's explicit yes in the conversation AND stays `permissions: deny` unless they insist.
Delivery of the result is NOT a tool — the platform delivers `task_complete`'s result to the
`deliver` targets.

Read `references/bot-config.md` for delivery pairing and the platform behaviour on failure.

Outside Studio the same bot ships over MCP (`https://mcp.plungeai.com/v1`): `plungeai_schedule`
with `action: create`, `job_type: workflow` creates the job against a saved workflow. Discover
the agent ids you fence into `allowed_agents` live — `plungeai_list_agents` from a client, or
`registry_search` inside the loop — never from memory.
