Skip to main content

lib/agent_sea/crew.ex

defmodule AgentSea.Crew do
  @moduledoc """
  A crew of agents that collaborate on a task DAG.

  Starting a crew boots a supervision subtree (a `Task.Supervisor`, a
  `DynamicSupervisor` of agents, and a coordinator). Tasks are added, then
  `kickoff/1` runs them — dispatching ready tasks to agents via the configured
  delegation strategy, running independent tasks concurrently, and unlocking
  dependents as results arrive.

  ## Example

      spec = %AgentSea.Crew.Spec{
        name: :research_crew,
        strategy: AgentSea.Crew.Delegation.BestMatch,
        agents: [researcher_config, writer_config]
      }

      {:ok, _sup} = AgentSea.Crew.start_link(spec)
      AgentSea.Crew.add_task(:research_crew, description: "Research X", required_capabilities: ["research"])
      {:ok, result} = AgentSea.Crew.kickoff(:research_crew)
  """

  defmodule Spec do
    @moduledoc "Declarative crew configuration."

    @enforce_keys [:name, :agents]
    defstruct [
      :name,
      :agents,
      strategy: AgentSea.Crew.Delegation.BestMatch,
      delegation_ctx: %{}
    ]

    @type t :: %__MODULE__{
            name: term(),
            agents: [AgentSea.Agent.Config.t()],
            strategy: module(),
            delegation_ctx: map()
          }
  end

  @doc "Start a crew supervision subtree from a `Spec`."
  def start_link(%Spec{} = spec), do: AgentSea.Crew.Supervisor.start_link(spec)

  @doc false
  def child_spec(%Spec{name: name} = spec) do
    %{id: {:crew, name}, start: {__MODULE__, :start_link, [spec]}, type: :supervisor}
  end

  @doc "Add a task to the crew (only while idle). Accepts `AgentSea.Crew.Task.new/1` attrs."
  defdelegate add_task(crew, attrs), to: AgentSea.Crew.Coordinator

  @doc "Run the crew to completion; returns `{:ok, result}` with results & failures."
  defdelegate kickoff(crew), to: AgentSea.Crew.Coordinator

  @doc "Current crew status: `:idle | :running | :paused | :completed | :aborted`."
  defdelegate status(crew), to: AgentSea.Crew.Coordinator

  @doc "Pause dispatching (in-flight tasks finish). Only while `:running`."
  defdelegate pause(crew), to: AgentSea.Crew.Coordinator

  @doc "Resume dispatching after a pause. Only while `:paused`."
  defdelegate resume(crew), to: AgentSea.Crew.Coordinator

  @doc "Abort: cancel in-flight tasks; the kickoff caller gets `{:error, :aborted}`."
  defdelegate abort(crew), to: AgentSea.Crew.Coordinator
end