The pieces
The event log
The event log
Everything that happens is an event in a durable log, with per-subscriber
offsets. A subscriber that was down does not lose what arrived while it was
down; one that crashes mid-handle sees the event again. Delivery is
at-least-once, and anything that fails repeatedly is dead-lettered rather
than retried forever in silence.A new subscriber starts at the HEAD of the log, not at zero. Starting at
zero once re-delivered 1,461 historical WhatsApp messages on a restart.
The agent
The agent
One agent per configured identity, with a mailbox per conversation —
ordered within a chat, concurrent across chats. It has a main model, a
cheaper memory model for retrieval, and a summary model for compaction.
Tools
Tools
The universal currency. Everything an integration offers, everything the
agent can do, and everything a sandboxed workflow can reach is a tool with a
name and a JSON schema. The agent calls them, workflows call them through
one host function, and the Broker gates them by name.
The Broker
The Broker
Decides what an extension may do and meters what it did. Default deny: a
subject with no grants is refused everything. See
the capability model.
Memory
Memory
GitLoom when configured, SQLite when not. One store, never both — two stores
that can disagree is a worse failure than one that can be down, because the
disagreement is silent.
Loops
Loops
Three tiers, in order of how much they can do: recipes (a YAML file),
signed WASM workflows (sandboxed code with a manifest), and compiled-in
loops (first-party, full authority). Durable runs, retries and step
checkpointing come from the loop runtime, so no tier reimplements them.
The Clock
The Clock
Durable timers. “Continue on Thursday” is a row, not a goroutine, so it
survives a restart.
How a message becomes an action
1
A channel receives it
WhatsApp via a wacli webhook, Slack over its socket, Telegram by polling.
Each channel decides in Go — not by asking a model — whether KARMAX was
addressed, because that decides whether it speaks at all.
2
It becomes an event
comms.message, appended to the durable log.3
It reaches a mailbox
Routed to the agent’s mailbox for that conversation. Messages in one chat
are handled in order; different chats run concurrently.
4
The agent takes a turn
With dynamic context: the time, your profile, open review questions, active
coding sessions, available channels, and memory retrieved for this message.
5
It acts
Its own tools for what it can do directly;
claude_code.call for work that
needs a shell, files or research. Anything long runs in the background and
comes back later as a delegation.completed event.The rules the code follows
Tools are the currency. Adding an integration adds tools. It does not add a host function, an ABI, or a special case in the sandbox. This is why WhatsApp went from six bespoke host functions to zero. Untrusted content is fenced or defanged at the boundary. Anything somebody else wrote — a WhatsApp message, a GitHub comment — is neutralised where it enters, not where it is used. A loop author cannot forget. A failure is reported where somebody will see it. A tool that fails returns that failure to the model, which can adapt. A credential that dies shows up inkarmax integrations before something depends on it.
Nothing that matters is only in memory. Runs, timers, offsets, credentials
and loop state are rows. A restart resumes rather than forgets.