community-bot

A Discord member that remembers.

community-bot is an AI member of a four-person Discord server. It answers in the form the moment calls for, keeps a private memory of each person, and revises its own manners at night. Everything runs on Cloudflare, so there is no machine to keep alive.

An exploded isometric view of community-bot A Discord message enters a Gateway Durable Object, passes through a Worker, and is stored by a Conversation Durable Object. The Worker also calls AI Gateway, Jev, and a per-channel TaskAgent with its sandbox. A daily Cron trigger and a nightly alarm are drawn as dashed lines. Discord Gateway DOone WebSocket Workerroutes the event Conversation DOSQLite, memories nightly alarm 03:00 JST AI Gatewaychat Jevtyped decisions TaskAgenttools + sandbox Cron17:00 UTC Cloudflare edge
  1. Discorda member types
  2. Gateway DOone WebSocket
  3. Workerroutes the event
  4. Conversation DOSQLite, memories

Worker also calls

AI GatewayJevTaskAgent

Cron 17:00 UTC
alarm 03:00 JST

Fig. 1. The bot, exploded. A message enters through a Durable Object, is routed by a Worker, and lands in another Durable Object that holds the conversation and its memories. Model calls and the task agent hang off the same Worker.

What it does

It is not an assistant waiting for a prompt. It sits in the channel, follows the conversation, and joins when someone mentions it. A reply can be a single emoji, a short paragraph, or a multi-step task such as researching a question with web tools and a sandbox. A typed decision model picks the shape of the reply before any text is written, and the reaction is sent first as an immediate acknowledgment.

The bot also remembers. Each person has their own memories, written after a conversation and consolidated while the server sleeps. Memories are injected into the next reply as context, never as facts about the channel as a whole.

Three loops

The system is easier to hold in your head as three loops that run at different speeds.

  1. The turn. A message is de-duplicated, appended to the channel log, and classified. If the bot was mentioned, it reacts first, then either answers in text or hands the request to the task agent. A memory extraction runs after the reply, outside the response path.
  2. The style. At 17:00 UTC a Cron Trigger reads the last day of reactions and corrections, proposes a new style layer, and keeps it only if the persona probes do not get worse.
  3. The memory. At 03:00 JST a Durable Object alarm groups the previous day by speaker, merges what is worth keeping, reviews memories that have gone stale, and writes a one-line diary of the day.

The two nightly jobs are deliberately separate. Style revision uses a Cron Trigger because it is a Worker-level job. Memory consolidation uses a Durable Object alarm because it needs the same SQLite database as the messages it reads.

Design choices

  • Decisions are typed; text is generated. Jev returns a choice, a probability, or a score. The language model only writes text for a decision that has already been made.
  • One Durable Object owns the log and the memories. Both daily extraction and nightly consolidation touch the same rows, so they share a transaction boundary instead of coordinating across services.
  • Every memory write leaves a ledger entry. The memory_log table stores the before and after state, the actor, and the reason. A nightly run can be rolled back from it.
  • The core persona is human-owned. Only the style layer can revise itself, and only when the probes say the change is safe.
  • A failed decision never destroys data. If Jev cannot answer, the bot falls back to a written reply, a default reaction, or leaves the memory untouched.

Start here

  • Architecture: the runtime pieces and what each one owns.
  • Agent workflow: what happens between a mention and a reply.
  • Memory: how memories are written, read, consolidated, and rolled back.
  • Commands: what you can type in the server.
  • Deploy and operate: local setup, deployment, and the docs pipeline.