Skip to content

feat(kernel): decouple auto-routing from the hardcoded "assistant" agent name #6139

Description

@neo-wanderer

Summary

Auto-routing (resolve_assistant_target) only engages when the channel's resolved agent is literally named "assistant".
Any other front-door agent silently bypasses intent classification — even when an operator has explicitly opted into per-channel auto_route via #2150.
The AutoRouteStrategy gate added in #2150 is already the intended on/off switch; the hardcoded name check duplicates it and over-constrains it.

Where (main @ dceae897a — line numbers may drift)

  • crates/librefang-kernel/src/kernel/assistant_routing.rs:190resolve_assistant_target early-returns if entry.name != "assistant" { return Ok(agent_id); }, which runs before the match ctx.auto_route { … } gate at :210. So the strategy gate is unreachable for any agent not named "assistant".
  • assistant_routing.rs:133llm_classify_intent treats a classification result of "assistant" as "handle it yourself". This sentinel-in-output is legitimate and separate from the entry gate above.
  • assistant_routing.rs:394 — the metadata route path also special-cases template != "assistant".

Why it's a problem

  • Routing capability is keyed on a magic string instead of a config flag/capability.
  • To use LLM or metadata dispatch, an operator must rename their front-door agent to "assistant" and demote the real agent to a specialist — an awkward inversion.
  • Setting auto_route = "sticky_ttl" (or "explicit_only") on a channel whose default agent is named anything else is silently inert — no warning, no log; the operator's config simply does nothing.
  • Only one dispatcher concept is possible per deployment (the single "assistant" name).

Minimal repro (synthetic)

  1. Configure a channel with default_agent = "concierge" (any name ≠ "assistant").
  2. Set that channel's auto_route = "sticky_ttl".
  3. Define two routable specialist templates with distinct descriptions.
  4. Send a message that clearly matches one specialist's description.

Expected: classifier routes the message to the matching specialist.
Actual: resolve_assistant_target returns at :190; classification never runs; the message is handled by concierge.

Proposal

Gate purely on AutoRouteStrategy (plus the existing routable-agent allowlist), not on the literal entry name:

  • Drop the entry.name != "assistant" precondition at :190. AutoRouteStrategy::Off (the default for all channels) already provides the bypass that preserves legacy behaviour.
  • Keep "assistant" as a sentinel only in the classifier output (:133) — i.e. "no specialization needed, the current agent handles it". That is distinct from the entry gate and stays.
  • When auto_route != Off and the classifier output resolves back to the current agent, no-op (avoid a self-route).
  • Optional: emit a WARN when auto_route is configured on a channel but routing is structurally unreachable, so misconfiguration is not silent.

Backwards-compatible: the default auto_route = Off preserves today's behaviour for every existing deployment.
The only behavioural change is that an agent named something other than "assistant" can now opt into routing.

Related

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/kernelCore kernel (scheduling, RBAC, workflows)enhancementNew feature or requesthas-prA pull request has been linked to this issue

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions