BB.Jido
Autonomous agents for Beam Bots, built on the Jido agent framework.
Where bb_reactor answers "how do
I execute this workflow?", bb_jido lets you answer "what should I do next
to achieve this goal?" — your robot becomes a goal-directed agent that
observes the world via BB.PubSub and dispatches commands or workflows in
response.
Layered architecture
┌─────────────────────────────────────────────────┐
│ Jido Agent │
│ - Observes via BB.PubSub (bridged to signals) │
│ - Routes signals to actions │
│ - Emits further signals as directives │
├─────────────────────────────────────────────────┤
│ bb_reactor workflows + BB commands │
│ (executed via BB.Jido.Action.Reactor and │
│ BB.Jido.Action.Command) │
└─────────────────────────────────────────────────┘What's in the box
| Module | Purpose |
|---|---|
BB.Jido.Plugin.Robot |
Jido v2 plugin you attach to an agent — adds robot state, the standard actions, default bb.* signal routes, and the PubSub bridge |
BB.Jido.Action.Command |
Run a BB command (apply(robot, command, [goal])) and await its result |
BB.Jido.Action.Reactor |
Run a bb_reactor workflow with the robot bound into context.private.bb_robot |
BB.Jido.Action.WaitForState | Wait for the robot state machine to enter a target state |
BB.Jido.Action.GetJointState | Read current joint positions/velocities |
BB.Jido.Action.SafetyAware |
Mixin that aborts an action with {:safety_not_armed, state} unless the robot is armed |
BB.Jido.PubSubBridge |
GenServer that forwards {:bb, path, %BB.Message{}} messages into an agent as Jido.Signals |
BB.Jido.Signal |
Canonical BB.Message → Jido.Signal mapping (CloudEvents bb.* namespace) |
BB.Jido.Telemetry | Telemetry spans for actions and a per-signal counter |
Installation
The fastest path is via Igniter — it adds the dep, generates a Jido instance module, and wires it into your application supervision tree in one command:
mix igniter.install bb_jido --robot MyApp.Robot
With --robot, it also scaffolds an agent module that attaches
BB.Jido.Plugin.Robot for that robot. Drop the flag to skip the agent
and add it later with mix bb_jido.add_agent.
bb_reactor is not a hard dependency. If you want to use
BB.Jido.Action.Reactor, add it the same way:
mix igniter.install bb_reactorSee the scaffolding how-to for the full list of generators.
Quick start
After mix igniter.install bb_jido --robot MyApp.Robot, you'll have:
lib/my_app/jido.ex—use Jido, otp_app: :my_applib/my_app/robot/agent.ex— agent with the robot plugin attachedlib/my_app/application.ex— children list gains{Jido, [name: MyApp.Jido]}
Start the agent and send it a signal:
{:ok, pid} = Jido.start_agent(MyApp.Jido, MyApp.Robot.Agent, id: "main")
:ok =
Jido.AgentServer.cast(
pid,
Jido.Signal.new!(
"bb.command.execute",
%{robot: MyApp.Robot, command: :home, goal: %{}}
)
)Signals
The PubSub bridge maps BB.PubSub events into Jido signals with a stable
naming scheme:
| Signal type | Source |
|---|---|
bb.state.transition | [:state_machine] topic, %BB.StateMachine.Transition{} payloads |
bb.safety.error | [:safety, :error] topic, %BB.Safety.HardwareError{} payloads |
bb.pubsub.<path> |
Anything else — dotted source path (e.g. bb.pubsub.sensor.joint_state) |
The source URI is /bb/<robot module> for traceability. Payload, path, and
robot are all available under signal.data.
By default the bridge only subscribes to [:state_machine]. Pass :topics
(and optionally :message_types or :throttle_ms) when attaching the
plugin to opt into higher-volume topics:
plugins: [
{BB.Jido.Plugin.Robot,
%{robot: MyRobot,
topics: [[:state_machine], [:sensor, :joint_state]],
throttle_ms: 100}}
]Error taxonomy
Actions return a consistent tagged-error set:
{:error, :safety_disarmed}— command exited because the robot was disarmed{:error, {:command_failed, reason}}— any other command failure{:error, {:reactor_failed, errors}}— reactor returned errors{:error, {:reactor_halted, reason}}— reactor was halted mid-flight{:error, {:safety_not_armed, state}}—BB.Jido.Action.SafetyAwareguard tripped
Documentation
Full documentation is available at HexDocs.