Overview
ollim-bot uses a single MCP tool,discord_embed, to send rich embed messages. The agent decides when and what to send — there is no manual embed API. Buttons attached to embeds follow an action pattern that either performs a direct operation (complete a task, delete an event) or opens a conversation with the agent through the inquiry system.
Embeds sent from background forks or interactive forks are tagged with a footer indicating their source context.
The discord_embed tool
The agent calls discord_embed with a title (required) and optional description, color, fields, buttons, and critical flag.
Parameters
| Parameter | Type | Required | Default | Description |
|---|---|---|---|---|
title | string | Yes | — | Embed heading (emoji stripped automatically) |
description | string | No | — | Body text below the title |
color | string | No | "blue" | Embed accent color |
fields | array | No | [] | Structured name/value pairs |
buttons | array | No | [] | Interactive buttons attached below the embed |
critical | boolean | No | false | Bypasses per-session limit, busy check, and ping budget in background forks |
Colors
| Value | Meaning |
|---|---|
blue | Informational (default) |
green | Success / completion |
red | Urgent / error |
yellow | Warning |
purple | Used internally for fork entry embeds |
Fields
Each field is an object withname, value, and optional inline (defaults to true). Fields render as labeled sections within the embed — inline fields appear side-by-side, non-inline fields stack vertically.
Buttons
Each button has alabel, an action string, and an optional style (defaults to "secondary").
| Style | Appearance |
|---|---|
primary | Blurple (Discord accent) |
secondary | Grey |
success | Green |
danger | Red |
Discord limits embeds to 25 buttons. Buttons beyond this limit are silently dropped.
Button actions
Button behavior is determined by theaction string. There are two categories: direct actions that execute immediately, and agent inquiries that route a prompt back through the agent.
- Direct actions
- Agent inquiries
Direct actions perform a single operation and respond with an ephemeral message. No agent involvement. If the Google API returns an error, the error reason is shown as an ephemeral message.
| Action pattern | Handler | What it does |
|---|---|---|
task_done:<task_id> | _handle_task_done | Marks a Google Tasks item complete |
task_del:<task_id> | _handle_task_delete | Deletes a Google Tasks item |
event_del:<event_id> | _handle_event_delete | Deletes a Google Calendar event |
dismiss | _handle_dismiss | Deletes the embed message |
act:<action>:<data>, parsed by the ActionButton dynamic item handler in views.py.
Inquiry flow
When the agent sends a button with anagent: action, the prompt is persisted to disk so it survives bot restarts. Clicking the button sends the stored prompt to the agent. If the button was sent by a background fork, clicking it enters an interactive fork that resumes that fork’s session; otherwise the prompt is processed in the current session.
Agent sends embed with inquiry button
The agent calls
discord_embed with a button whose action starts with agent:. The build_view function extracts the prompt, registers it via inquiries.register(), and gets back an 8-character hex UUID. The button’s custom ID becomes act:agent:{uid}.User clicks the button
Discord dispatches the interaction to
ActionButton, which matches the act:agent:{uid} pattern and calls _handle_agent_inquiry.Inquiry is popped and validated
The handler calls
inquiries.pop(uid) to retrieve and remove the stored prompt. If the inquiry has expired (older than 7 days) or was already consumed, the button responds with an expiration notice.Agent processes the prompt
If the button’s message has a fork session ID (i.e., it was sent by a background fork), the handler enters an interactive fork that resumes that session, sends a fork entry embed, and streams the agent’s response. Otherwise, the prompt is sent directly to the agent in the current session prefixed with
[button]. Either way, the agent streams its response to the channel.~/.ollim-bot/state/inquiries.json with a 7-day TTL. Expired entries are cleaned up on read.
Fork embeds
Forks produce their own embeds at entry and exit, separate from thediscord_embed tool.
Fork entry
When a fork starts (via/fork, the enter_fork MCP tool, or an inquiry button), a purple “Forked Session” embed is sent with three buttons:
| Button | Style | Action |
|---|---|---|
| Save Context | success (green) | Promotes fork context to the main session |
| Report | primary (blurple) | Exits the fork and sends a summary to the main session |
| Exit Fork | danger (red) | Discards the fork cleanly |
fork_save, fork_report, and fork_exit handlers in views.py. They are equivalent to the save_context, report_updates, and exit_fork MCP tools.
Fork exit
When a fork ends, a “Fork Ended” embed is sent with a color indicating the exit strategy:| Exit strategy | Color |
|---|---|
| Save | Green |
| Report | Blue |
| Exit (discard) | Grey |
Persistence
Buttons remain functional across bot restarts through two mechanisms:DynamicItem[Button]: Discord.py reconstructs button handlers from custom IDs using a regex pattern (act:(?P<action>[a-z_]+):(?P<data>.+)). TheActionButtonclass matches this pattern and dispatches to the correct handler.- Inquiry storage: Agent inquiry prompts are written to
~/.ollim-bot/state/inquiries.jsonso the prompt text is available even after a restart. Each entry includes a timestamp for TTL enforcement.
Background fork behavior
Embeds sent from the main session or interactive forks are always delivered. In background forks, embeds are subject to additional constraints:- Ping budget: Each embed consumes one ping from the refill-on-read budget. When the budget is exhausted,
discord_embedreturns an error to the agent. critical: true: Bypasses all three checks (per-session limit, busy state, and budget). Critical pings are tracked in a separatecritical_usedcounter but never blocked.allow_ping: false: When set in the background fork config,discord_embedis disabled entirely —critical: truedoes not override this.- Busy state: When the main agent is actively responding (lock held), non-critical embeds from background forks return errors. The agent should use
report_updatesinstead.
Next steps
Forks
Interactive and background fork lifecycle, exit strategies, and idle timeout.
Ping budget
How the refill-on-read budget controls background fork notifications.
MCP tools
Full reference for all MCP tools including discord_embed.
Slash commands
Discord slash commands for managing forks, permissions, and more.
