You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
When a Telegram (or any) group has more than one LibreFang agent joined, the current code can't route an @agentB mention (or an alias declared by agentB) to agentB if agentB is not the channel's default_agent. Concrete failure modes verified in code, not speculation:
Aliases wiring is single-agent.crates/librefang-api/src/channel_bridge.rs:1934-1969 only loads the default agent's metadata.routing.aliases/weak_aliases into group_trigger_patterns. Aliases of non-default agents joined to the same channel are never merged.
No @agentname → agent_id mapping.crates/librefang-channels/src/telegram.rs:2940-2943, 2983-3014 only sets was_mentioned=true for the bot's own username. Mentioning a specific agent by name is invisible to the bridge.
DMs and groups without forum topics bypass the registry entirely.crates/librefang-channels/src/thread_ownership.rs:18 comment + bridge.rs:3409-3431 gate. A multi-agent group without active topics has zero stickiness.
End-user impact: in a group with fandangorodelo (default) + ambrogio (joined), saying "oye fandango" routes to fandango (good — #2619). Saying "ambrogio, ayúdame" or @ambrogio … routes to fandango anyway (bad), and the follow-up has no concept of "keep talking to ambrogio" because no claim was ever made for him.
Proposal (high-level — implementation in follow-up PRs)
Optional @handle → agent_id resolution — extend check_mention_entities in adapters (Telegram, Discord, Slack) to scan all mention/text_mention entities, lookup against a per-agent metadata.routing.handles: Vec<String> (new convention; ignore if not declared), and stamp metadata[\"addressed_agent_id\"]. was_mentioned semantics preserved for backward compat.
All changes preserve existing TTL semantics (refresh-on-holder, re-claim on @-mention) from #3414; only ThreadKey shape changes (plus the new metadata field and the resolver consultation point).
Test plan (will live in follow-up PRs)
Patrón TestServer en crates/librefang-api/tests/ y cargo test -p librefang-channels:
@agentB en grupo multi-agente enruta a B (no a default).
Follow-up del mismo peer sin mención sigue con B (per-peer stickiness).
Otro peer en el mismo hilo no es contaminado.
TTL expira → claim cae → resolver re-evalúa.
Alias declarado por agentB (no default) enruta a B.
Grupo sin forum topic ahora respeta claim usando chat_id como thread.
Migración de DMs al registry — flag include_dms=false por defecto.
Sustituir scoring del attention system — reutilizar tal cual.
Notes
Plan completo y citas file:line en .omc/plans/per-conversation-routing.md del fork. Disponible para PR atomic splitting según el feedback que reciba este issue.
Context
Builds on top of merged work in this area:
ThreadOwnershipRegistry(channel + thread, TTL 5min, claim/refresh/re-claim).metadata.routing.aliasesauto-merged intogroup_trigger_patterns.is_vocative_trigger,is_addressed_to_other_participant,SenderContext.group_participants, flagLIBREFANG_GROUP_ADDRESSEE_GUARD).chat_id(SessionId::for_channel(agent, "<channel>:<chat>")).route_assistant_by_metadata()per-agent attention scoring (aliases × pesos 6/2/1 + semantic bonus).And work that was attempted but never landed:
account_id: Option<String>toThreadKeyfor multi-tenant + lazysweep_expiredtask. Verified in currentcrates/librefang-channels/src/thread_ownership.rs:32:ThreadKeystill does not haveaccount_id.The gap (multi-agent groups)
When a Telegram (or any) group has more than one LibreFang agent joined, the current code can't route an
@agentBmention (or an alias declared by agentB) to agentB if agentB is not the channel'sdefault_agent. Concrete failure modes verified in code, not speculation:crates/librefang-api/src/channel_bridge.rs:1934-1969only loads the default agent'smetadata.routing.aliases/weak_aliasesintogroup_trigger_patterns. Aliases of non-default agents joined to the same channel are never merged.@agentname → agent_idmapping.crates/librefang-channels/src/telegram.rs:2940-2943, 2983-3014only setswas_mentioned=truefor the bot's own username. Mentioning a specific agent by name is invisible to the bridge.route_assistant_by_metadatais not consulted from the channel dispatch path.resolve_or_fallback(crates/librefang-channels/src/bridge.rs:2321-2398) walks bindings → direct → user default → channel default → "assistant" → first-available. Per-agent attention scoring exists in kernel but is never called from the channel inbound path. Layer (c) of bug(channels): inbound routing falls to non-deterministic "first available" agent when SidecarChannelConfig has no default + empty channels allowlist accepts all (related #4926) #5294 (non-deterministic "first available") is still alive.ThreadKeyis too coarse for multi-agent + multi-tenant. Today(channel, thread). Withoutaccount_id, two Slack workspaces with samethread_tscollide. Withoutchat_id, two Telegram chats with the same Telegram-side topic id (rare but possible) collide. Withoutpeer_id, two users talking to two different agents in the same topic share one claim. (fix(channels): multi-tenant ThreadKey + sweep_expired wiring (#3414 follow-up) #3419/fix(channels): account_id in ThreadKey + lazy sweep + push bypass note #3420 tried to fix the account_id slice and didn't land.)crates/librefang-channels/src/thread_ownership.rs:18comment +bridge.rs:3409-3431gate. A multi-agent group without active topics has zero stickiness.End-user impact: in a group with
fandangorodelo(default) +ambrogio(joined), saying "oye fandango" routes to fandango (good — #2619). Saying "ambrogio, ayúdame" or@ambrogio …routes to fandango anyway (bad), and the follow-up has no concept of "keep talking to ambrogio" because no claim was ever made for him.Proposal (high-level — implementation in follow-up PRs)
account_idslice of fix(channels): multi-tenant ThreadKey + sweep_expired wiring (#3414 follow-up) #3419/fix(channels): account_id in ThreadKey + lazy sweep + push bypass note #3420 — extendThreadKeyto{ channel, account_id: Option<String>, chat_id: Option<String>, thread: String, peer_id: Option<String> }.threadfalls back tochat_idwhen no forum-topic exists; DMs continue to bypass unless an opt-in flag is set. Restores the multi-tenant safety that feat(channels): thread ownership prevents multi-agent duplicate replies #3414 review-comments asked for.channel_bridge.rs:1934-1969with one over every agent whose manifest declareschannels.contains(channel_id)(or no allowlist, depending on feat(types,kernel,api): per-agent channel allowlist #4961 outcome). Each agent's aliases land ingroup_trigger_patternsfor that channel; the bridge already maps a triggered alias back to the correct agent via feat(channels): trigger agent on routing aliases in group messages (#2292) #2619 wiring once the lookup exists.@handle → agent_idresolution — extendcheck_mention_entitiesin adapters (Telegram, Discord, Slack) to scan allmention/text_mentionentities, lookup against a per-agentmetadata.routing.handles: Vec<String>(new convention; ignore if not declared), and stampmetadata[\"addressed_agent_id\"].was_mentionedsemantics preserved for backward compat.route_assistant_by_metadatafrom the channel dispatch path — add aKernelHandlemethodroute_assistant_by_metadata_for_channel(channel, text) -> Option<AgentId>and call it fromresolve_or_fallbackafter the existing chain and before the non-deterministic first-available fallback (closes layer (c) of bug(channels): inbound routing falls to non-deterministic "first available" agent when SidecarChannelConfig has no default + empty channels allowlist accepts all (related #4926) #5294).[channels.X.overrides] conversation_ownership_ttl_seconds(default 600s),conversation_ownership_include_dms(default false).All changes preserve existing TTL semantics (refresh-on-holder, re-claim on @-mention) from #3414; only
ThreadKeyshape changes (plus the new metadata field and the resolver consultation point).Test plan (will live in follow-up PRs)
Patrón
TestServerencrates/librefang-api/tests/ycargo test -p librefang-channels:@agentBen grupo multi-agente enruta a B (no a default).peersin mención sigue con B (per-peer stickiness).chat_idcomo thread.thread_idenaccount_iddistintos no colisiona.Out of scope (separate issues)
channels = []accept-all semantics) — tracked separately.include_dms=falsepor defecto.Notes
Plan completo y citas file:line en
.omc/plans/per-conversation-routing.mddel fork. Disponible para PR atomic splitting según el feedback que reciba este issue.cc related: #5294 (closed layer a), #4926 (N:M routing), #4961 (channels allowlist).