Debounce rapid messages

Users often send several texts in a row. Debounce per channelId before calling your model:
const pending = new Map<string, ReturnType<typeof setTimeout>>();

app.on("typing", () => { /* optional: extend debounce window */ });

for await (const [space, message] of app.messages) {
  const key = message.group?.chatId ?? space.to;
  clearTimeout(pending.get(key));
  pending.set(
    key,
    setTimeout(() => runAgent(space, message), 400)
  );
}

Do not block the iterator

app.messages is a single consumer. Offload heavy work to a queue — never await a 30s LLM call inside the loop without accepting that you will miss concurrent messages.

Cancellation

If the user sends “never mind” while a reply is generating, track generation tokens per conversation and discard stale completions.