A DM addressed to one agent that @-mentions another was silently
dropping the second agent out of the conversation:
* `dispatchAgentMessage` added the mentioned agent to the wake set,
so CEO's heartbeat fired — but
* `listAgentInboxUnread` only surfaced messages where the current
agent was `to_agent_id` or sat in a matching channel/project/
department. Mentions were invisible, so CEO's next turn saw nothing
new and returned silently. From the operator's chair this looked
exactly like "CEO ignored me; 张工程师 answered instead".
* `findMentionedAgentIds` had a latent bug on top: its unique-alias
filter used `ids.length === 1`, but a single agent whose name
matched its role registered itself twice, tripped the filter, and
every `@CEO` in a scaffold with role === name was a no-op. Once
inbox visibility was fixed the bug became load-bearing.
Root-cause fixes:
* New migration v48 adds `agent_message_mentions(message_id, agent_id,
company_id, created_at)` so mention fan-out is a first-class join.
* `sendAgentMessage` now takes the resolved mention set and writes
the row + mentions in a single transaction — a crash between the
two writes can no longer leave a message visible to the recipient
but invisible to the mentioned agent.
* `dispatchAgentMessage` computes the mention set exactly once via
`resolveMentionedAgentIds` and hands the same list to both the DB
write and `resolveMessageWakeTargets`, so persistence and wake
stay in lockstep.
* `listAgentInboxUnread` and `listAgentMessages` gain an `id IN
(select message_id from agent_message_mentions …)` clause so the
mentioned agent's inbox actually contains the message that woke it.
* `findMentionedAgentIds` now stores per-alias agent IDs in a `Set`
and filters on `ids.size === 1`, dropping the self-duplication
bug while preserving the "refuse ambiguous aliases" semantics.
* Company teardown drops `agent_message_mentions` alongside the
existing `agent_message_reads` cleanup so deletes stay clean.
Tests pin the new behaviour: a DM with a `@CEO` mention reaches
CEO's inbox and both agents get woken, while a DM whose recipient
is `__board__` never attempts to wake the human sentinel.