What
Replace the closed while-loop AgentLoop with an event-driven architecture. Everything that enters the context window goes through PendingMessage as a single pipeline:
Event → Subscriber → PendingMessage(kind: background|active) → Drain → Promote → Message
One path for everything: user messages, agent messages, tool calls, tool responses, memories, skills, goals, sub-agent responses. No exceptions, no bypass routes.
Problem
- AgentLoop is a closed while-loop invisible to EventBus — tool calls happen inside, subscribers can't react
- Pending messages hang when agent is idle — waiting for user input to trigger processing
between_rounds is a hack to peek inside the closed loop
- Messages enter context through multiple paths (direct persistence, phantom injection, pending messages)
Key design decisions
- Two kinds of PendingMessage:
active (triggers LLM request — user messages, sub-agent responses, tool responses) and background (appears in context silently — memories from Mneme, skills from Melete)
- AASM state machine on Session:
:idle / :awaiting / :executing — replaces manual locks, determines interrupt strategy
- Mailbox drain pattern: SQLite serialized writes provide concurrency safety without mutex
- Two interrupt types: mailbox interrupt (stop drain loop) and tool execution interrupt (synthetic tool_response)
Prerequisites (done)
References
Full exploration: thoughts/shared/notes/2026-04-03/qa-brainstorm-event-loop-mailbox.md
What
Replace the closed while-loop AgentLoop with an event-driven architecture. Everything that enters the context window goes through PendingMessage as a single pipeline:
One path for everything: user messages, agent messages, tool calls, tool responses, memories, skills, goals, sub-agent responses. No exceptions, no bypass routes.
Problem
between_roundsis a hack to peek inside the closed loopKey design decisions
active(triggers LLM request — user messages, sub-agent responses, tool responses) andbackground(appears in context silently — memories from Mneme, skills from Melete):idle/:awaiting/:executing— replaces manual locks, determines interrupt strategyPrerequisites (done)
Assemble tool pairs by tool_use_id, not message order #419 — Assemble tool pairs by tool_use_id✅References
Full exploration: thoughts/shared/notes/2026-04-03/qa-brainstorm-event-loop-mailbox.md