Studio kinds
plungeai-bot-agent
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, Teams) — 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).
Download zip (opens in a new tab) · View raw SKILL.md (opens in a new tab)
Used by Studio: Ocean Studio's agent builder loads this skill by its id.
A bot is an agentic agent plus three facts: WHEN it runs, WHERE the result goes, and whether its
hands are the user's own computer. Studio saves it under "Bots" and creates the schedule and delivery
from your bot-config block. Unattended runs cannot ask anything: no ask_user, no platform_action,
every risky tool excluded or permissions: deny.
The conversation (before any YAML)
- Job. One sentence: what each run produces (a brief, an alert, a digest).
- Cadence. Ask for the schedule in words, propose the cron, confirm it (five fields); set
timezone(IANA, e.g.Europe/Berlin) in the block to the user's zone, never convert to UTC. - Where it reports.
inapp(Studio inbox, always safe),email,slack,telegram,whatsapp,discord(these three need a paired chat: tell the user to pair first),teams(needs the owner's Microsoft connection). Defaultinapp. - Hands. Cloud (default) or "runs on this computer" (
local: true): local files, local apps, local agents. Both run onagent: one-agent. - Fence, money, spend. Smallest
allowed_tools; ids only from AVAILABLE CAPABILITIES and liveregistry_search;effort: standardunless argued; 2–4success_criteria. Ask what the bot may never do alone (pay, send, delete) and what a run may cost.
Confirm, then emit BOTH blocks.
Output contract — two fenced blocks, in this order
workflow:
name: "<short bot name>"
description: "<one line: what it reports and how often>"
tasks:
- type: harness
id: bot
agent: one-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>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 save lint enforces — violating them fails the save:
- ONE task,
type: harness; a presentallowed_toolsMUST includetask_complete; nomemory_owner. posture,peers,review,max_runtime_sandworkspaceapply only onagent: one-agent; onharness-agent,agentic-agentoruniversal-agentthey fail the save, not ignored.- A
local: truebot must nameagent: one-agent; putlocal: truein BOTH blocks (task and bot-config, so the scheduler skips cleanly when the machine is offline) and addlocal_agents: [ids]only when the user names them.
Also expected: agent: one-agent, goal: "{input}", a mission ending with the task_complete sentence, no
emails or ids in the YAML (recipients go in deliver), no ask_user / platform_action (nobody answers).
skill_manage needs permissions: {skill_manage: allow} unattended: omit it unless asked.
bot-config fields
schedule— 5-field cron, required (0 7 * * 1-5weekdays 07:00,*/30 * * * *). Optionaltimezone(IANA).deliver— list of{channel, chat_id?, to?, phone_number_id?}:inapp(none),email(tooptional = owner),slack(chat_idC… ortoU…; none = DM the owner),telegram/discord(chat_id),whatsapp(to+phone_number_id),teams(chat_id= the Graph chat id; posts as the owner through their Microsoft connection; text capped at 4000 characters).local,prompt(the per-run input; default empty).budget_usd_run,budget_usd_month— USD caps above 0, at most 100000;0is refused, omit the key for no cap.permissions— class presets{send|write|pay|delete: allow|ask|deny|hand_off}stored on the bot.peers,posture,review,max_runtime_s,workspace,max_turns,skills,persona,allowed_tools— the mission keys below, written onto the task.memory_consolidate: weekly— opt-in weekly memory consolidation.
Mission keys (on the harness task, snake_case)
| Key | Value | Effect |
|---|---|---|
posture | read_only | denies write, send, delete, pay, delegate, invoke_workflow, every MCP tool |
peers | [bot name | id | "a2a:<url>"] | the only bots message_bot may reach; none by default |
review | auto | a model reviews each allowed write/send/pay/delete call (one extra call each) |
max_runtime_s | seconds | wall-clock cap; the run stops with timeout |
workspace | bot | run | bot (default for a saved bot) keeps files across runs; run starts clean |
budget_usd_run | USD | stops the loop at the next turn once cost passes it |
permissions | {class or tool: allow|ask|deny|hand_off} | see Permissions |
allowed_tools | list | the fence; opt-in tools below |
skills, persona | ids | capability injection |
max_turns | number | turn cap (effort sets a default) |
An off-contract posture or review is forced to read_only / auto: it never widens a run.
Permissions and budgets
Classes: send, write, pay, delete (reads are always allowed). Presets: allow, ask
(the run pauses needs_approval, up to 7 days), deny, and hand_off: the call never
runs and does not retry; the result ends with [handed off to you — not run: …] so the owner
does it themselves (a leading [SILENT] is dropped). Use hand_off for what a bot should
prepare but never do (pay: hand_off). A call resolves: read-only posture, then deny, then
hand_off, then the money floor (buy, pay, transfer, send, withdraw, … always ask, even
with pay: allow), then the tool-name preset, then the class preset, then allow. An unknown preset
is ask. A team admin can LOCK a class: its allow rises to ask (never lifts a
deny). Locks live on the saved bot, not in YAML. Three denied calls in a row stop an unattended
run (stop_reason: denied). hand_off is one-agent only (other runtimes get deny); the task's permissions:,
bot-config permissions and the permissions verbs all accept it.
Safe default: permissions: { pay: ask, delete: ask }.
Budgets: budget_usd_run caps one run; budget_usd_month (bot-config, Studio, or the verbs
below) is enforced by the scheduler. The lower of the YAML and the stored per-run cap applies,
and a capped run's result says so. Blank or omitted = no cap; the block and Settings refuse 0, and the verbs
clear a cap with null / none — never send 0 (the runtime ignores it).
Always-on abilities (opt-in)
Add to allowed_tools only what the job needs; each needs a saved bot.
schedule—action: create | list | pause | resume | delete | run_now | wake.create {name, cron, timezone?, prompt?, deliver?, ends_at?}; one run at a clock time is cron@oncewithrun_at(ISO-8601 with offset);wake {in, note}re-runs the bot once (min 60 s). The bot writes the cron and says it back in words; 50 live jobs per bot.message_bot—{to, text, wait?};tois inpeers, orgroup:<name>(the sender must be a member of that group;wait: trueis refused for a group). Replies arrive as[MESSAGE from <bot>]at the start of a later run;wait: trueruns the peer now as a plain child (no memory, inbox, workspace). A bot group (2–6 of the owner's bots, made in Studio or via the verbs) fans a message out: membership is the fence, every other member's inbox gets it, the next member wakes, 8 hops max.browser—{steps, url?, session?: 'bot' | 'none'};'bot'keeps cookies. Click, type and key steps count as writes.memory—action: forget {query}purges matching entries.code—{prompt, directory?, session_id?, timeout_s?}hands one turn (max 300 s) to the owner's OceanCode over the Local Tunnel. Classwrite; no daemon connected, no tool.mcp_tool_search— only with MCP servers attached: past 15 tools or 8 KB, schemas are listed by name and a search loads up to 5 for the next turn (never ones the permissions refuse).
Shared bots: inbox, schedule, message_bot, the browser session and workspace are owner-only;
a teammate runs a shared bot without identity.
Channels in
A chat can be bound to a bot (Telegram, WhatsApp, Discord, Teams): /bot <name> in the chat
(Slack: /plunge bind), or the verbs below; pair first (Teams two-way chat also needs
the platform's Teams app registration). Voice notes (Telegram and WhatsApp only, up to 25 MB)
from an authorised sender are transcribed with Whisper and answered like text (as audio where
the channel can); Discord and Teams are text only.
Outside Studio — the same bot over REST, MCP and CLI
REST https://api.plungeai.com (One API 2.3.0, live contract GET /v1/openapi.json), MCP
https://mcp.plungeai.com/v1, CLI ocean-cli.
| Job | REST | MCP | CLI |
|---|---|---|---|
| policy, locks, caps | GET/PUT /v1/workflows/{id}/permissions | plungeai_workflow permissions_get / permissions_set | workflows permissions get|set --class pay=ask --lock pay --budget-run 0.5 --budget-month 20 |
| workspace files | GET …/workspace-files, …/download?path= | files / file | workflows files <id> [--download <path> --out <file>] |
| inbox | GET …/inbox | inbox | workflows inbox <id> |
| chat bindings | GET/POST …/bindings, DELETE …/bindings/{channel}/{sender_id}, POST /v1/channels/pair | bindings / bind / unbind | workflows bindings list|bind --channel telegram --sender <id>|unbind --channel --sender --yes |
| groups | /v1/bot-groups, /{id}, /{id}/thread | groups / group_set / group_delete / group_thread | bot-groups list|create --name --members a,b|update|delete|thread <id> |
| steer or list a run's files | POST /v1/executions/{id}/steer, GET …/files | plungeai_executions steer / files | executions steer <id> <text>, executions files <id> |
| schedule once, wake | POST /v1/schedules (@once + run_at), …/{id}/wake | plungeai_schedule run_at, wake | schedules create NAME --type workflow --target <id> --cron @once --at <iso>, schedules wake <job_id> --note |
A saved bot runs under its stored policy on every door, including the Agent Call door:
POST /v1/execute {"agent": "<bot id>@3", "input": "…"}.
The fence for unattended runs
Same 24 loop tools as an agentic agent (plungeai-agentic-agent) plus the always-on ones above. Leave out
ask_user and 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 deny/hand_off/ask.
Delivery is NOT a tool: the platform sends task_complete's result to the deliver targets
(references/bot-config.md: limits, pairing). Discover agent ids live (plungeai_list_agents,
registry_search), never from memory.