Skip to main content

lib/agent_sea/crew/delegation/best_match.ex

defmodule AgentSea.Crew.Delegation.BestMatch do
  @moduledoc """
  Selects the agent whose role capabilities best fit the task. Prefers agents
  that can fully execute the task, breaking ties by capability score. Pure — uses
  the role on each `agent_ref`, no process calls.
  """

  @behaviour AgentSea.Crew.Delegation

  alias AgentSea.Capability
  alias AgentSea.Crew.Delegation.Result

  @impl true
  def delegate(_task, [], _ctx), do: {:error, :no_agents}

  def delegate(task, agents, _ctx) do
    required = Map.get(task, :required_capabilities, [])

    ranked =
      agents
      |> Enum.map(fn agent -> {agent, Capability.match(role_capabilities(agent), required)} end)
      # can_execute (true sorts after false) then score, both descending
      |> Enum.sort_by(fn {_agent, m} -> {m.can_execute, m.score} end, :desc)

    [{best, match} | rest] = ranked

    {:ok,
     %Result{
       selected_agent: best.name,
       confidence: match.score,
       reason: "best capability match (score #{Float.round(match.score, 2)})",
       alternatives: Enum.map(rest, fn {agent, _m} -> agent.name end)
     }}
  end

  defp role_capabilities(%{role: %AgentSea.Role{capabilities: caps}}), do: caps
  defp role_capabilities(_), do: []
end