%%%------------------------------------------------------------------- %% @doc Sovereign Event Bus - Agent Lifecycle Supervisor %% %% Supervises dynamic agents spawned via spawn_agent/2. %% Each agent runs seb_agent_fsm (4-state corrected FSM). %% %% Agent States (per XML spec): %% 1. active - Processing events normally %% 2. draining - Rejecting new events, processing queue %% 3. checkpointed - Committed offset to L0 kernel %% 4. stopped - Shutdown complete %% %% Drain Timeout: 30 seconds (per XML) %% Offset Commit: Via L0 kernel NIF %% %% @end %%%------------------------------------------------------------------- -module(seb_agent_sup). -behaviour(supervisor). -export([start_link/0]). -export([spawn_agent/2, terminate_agent/1]). -export([init/1]). -export([get_agent_pids/0]). -define(SERVER, ?MODULE). -define(AGENT_RESTART_INTENSITY, 10). -define(AGENT_RESTART_PERIOD, 60). %%%=================================================================== %%% API %%%=================================================================== %% @doc Start the agent supervisor -spec start_link() -> supervisor:startlink_ret(). start_link() -> supervisor:start_link({local, ?SERVER}, ?MODULE, []). %% @doc Spawn a new agent FSM %% %% AgentId: Unique identifier for this agent %% Config: Configuration map with options %% %% Returns: {ok, Pid} | {error, Reason} -spec spawn_agent(binary(), map()) -> {ok, pid()} | {error, term()}. spawn_agent(AgentId, Config) when is_binary(AgentId), is_map(Config) -> ChildSpec = #{ id => AgentId, start => {seb_agent_fsm, start_link, [AgentId, Config]}, restart => temporary, shutdown => 5000, type => worker, modules => [seb_agent_fsm] }, supervisor:start_child(?SERVER, ChildSpec). %% @doc Terminate a specific agent %% %% Initiates drain sequence: %% 1. Agent transitions to draining state %% 2. Processes remaining queue items (< 30s) %% 3. Commits offset to L0 kernel %% 4. Transitions to stopped state %% -spec terminate_agent(binary()) -> ok | {error, not_found}. terminate_agent(AgentId) when is_binary(AgentId) -> case supervisor:terminate_child(?SERVER, AgentId) of ok -> supervisor:delete_child(?SERVER, AgentId); {error, not_found} -> {error, not_found} end. %% @doc Get all active agent PIDs -spec get_agent_pids() -> [pid()]. get_agent_pids() -> case supervisor:which_children(?SERVER) of Children -> [Pid || {_Id, Pid, worker, _Modules} <- Children, is_pid(Pid)]; _ -> [] end. %%%=================================================================== %%% Supervisor Callbacks %%%=================================================================== %% @doc Initialize the agent supervisor %% %% Uses one_for_one strategy: if an agent fails, only that agent restarts. %% Max 10 restarts per 60 seconds per agent. %% -spec init([]) -> {ok, {supervisor:sup_flags(), []}}. init([]) -> SupFlags = #{ strategy => one_for_one, intensity => ?AGENT_RESTART_INTENSITY, period => ?AGENT_RESTART_PERIOD }, {ok, {SupFlags, []}}. %%%=================================================================== %%% Internal Functions %%%=================================================================== % No internal functions at this time