Class: AgentRuntime::FSM

Inherits:
Object
  • Object
show all
Defined in:
lib/agent_runtime/fsm.rb

Overview

Formal Finite State Machine for agentic workflows.

Implements the canonical agentic workflow FSM with 8 states:

  • INTAKE: Normalize input, initialize state

  • PLAN: Single-shot planning using /generate

  • DECIDE: Make bounded decision (continue vs stop)

  • EXECUTE: LLM proposes next actions using /chat (looping state)

  • OBSERVE: Execute tools, inject real-world results

  • LOOP_CHECK: Control continuation

  • FINALIZE: Produce terminal output (terminal state)

  • HALT: Abort safely (terminal state)

Valid state transitions:

  • INTAKE → PLAN

  • PLAN → DECIDE | HALT

  • DECIDE → EXECUTE | FINALIZE | HALT

  • EXECUTE → OBSERVE | FINALIZE | HALT

  • OBSERVE → LOOP_CHECK

  • LOOP_CHECK → EXECUTE | FINALIZE | HALT

  • FINALIZE → (terminal)

  • HALT → (terminal)

Examples:

Initialize and use FSM

fsm = FSM.new(max_iterations: 100)
fsm.transition_to(FSM::STATES[:PLAN], reason: "Starting")
fsm.plan?  # => true

See Also:

Constant Summary collapse

STATES =

State constants mapping state names to integer values.

{
  INTAKE: 0,
  PLAN: 1,
  DECIDE: 2,
  EXECUTE: 3,
  OBSERVE: 4,
  LOOP_CHECK: 5,
  FINALIZE: 6,
  HALT: 7
}.freeze
TERMINAL_STATES =

Terminal states that cannot transition to other states.

[STATES[:FINALIZE], STATES[:HALT]].freeze
VALID_TRANSITIONS =

Valid state transitions based on FSM specification.

Maps each state to an array of valid next states.

{
  STATES[:INTAKE] => [STATES[:PLAN]],
  STATES[:PLAN] => [STATES[:DECIDE], STATES[:HALT]],
  STATES[:DECIDE] => [STATES[:EXECUTE], STATES[:FINALIZE], STATES[:HALT]],
  STATES[:EXECUTE] => [STATES[:OBSERVE], STATES[:FINALIZE], STATES[:HALT]],
  STATES[:OBSERVE] => [STATES[:LOOP_CHECK]],
  STATES[:LOOP_CHECK] => [STATES[:EXECUTE], STATES[:FINALIZE], STATES[:HALT]],
  STATES[:FINALIZE] => [],
  STATES[:HALT] => []
}.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(max_iterations: 50) ⇒ FSM

Initialize a new FSM instance.



71
72
73
74
75
76
# File 'lib/agent_runtime/fsm.rb', line 71

def initialize(max_iterations: 50)
  @state = STATES[:INTAKE]
  @max_iterations = max_iterations
  @iteration_count = 0
  @history = []
end

Instance Attribute Details

#historyObject (readonly)

Returns the value of attribute history.



84
# File 'lib/agent_runtime/fsm.rb', line 84

attr_reader :state, :iteration_count, :history

#iteration_countInteger (readonly)



84
# File 'lib/agent_runtime/fsm.rb', line 84

attr_reader :state, :iteration_count, :history

#stateInteger (readonly)



84
85
86
# File 'lib/agent_runtime/fsm.rb', line 84

def state
  @state
end

Instance Method Details

#decide?Boolean

Check if current state is DECIDE.



103
104
105
# File 'lib/agent_runtime/fsm.rb', line 103

def decide?
  @state == STATES[:DECIDE]
end

#execute?Boolean

Check if current state is EXECUTE.



110
111
112
# File 'lib/agent_runtime/fsm.rb', line 110

def execute?
  @state == STATES[:EXECUTE]
end

#finalize?Boolean

Check if current state is FINALIZE.



131
132
133
# File 'lib/agent_runtime/fsm.rb', line 131

def finalize?
  @state == STATES[:FINALIZE]
end

#halt?Boolean

Check if current state is HALT.



138
139
140
# File 'lib/agent_runtime/fsm.rb', line 138

def halt?
  @state == STATES[:HALT]
end

#increment_iterationvoid

This method returns an undefined value.

Increment the iteration count.

Raises:



170
171
172
173
# File 'lib/agent_runtime/fsm.rb', line 170

def increment_iteration
  @iteration_count += 1
  raise MaxIterationsExceeded, "Max iterations (#{@max_iterations}) exceeded" if @iteration_count > @max_iterations
end

#intake?Boolean

Check if current state is INTAKE.



89
90
91
# File 'lib/agent_runtime/fsm.rb', line 89

def intake?
  @state == STATES[:INTAKE]
end

#loop_check?Boolean

Check if current state is LOOP_CHECK.



124
125
126
# File 'lib/agent_runtime/fsm.rb', line 124

def loop_check?
  @state == STATES[:LOOP_CHECK]
end

#observe?Boolean

Check if current state is OBSERVE.



117
118
119
# File 'lib/agent_runtime/fsm.rb', line 117

def observe?
  @state == STATES[:OBSERVE]
end

#plan?Boolean

Check if current state is PLAN.



96
97
98
# File 'lib/agent_runtime/fsm.rb', line 96

def plan?
  @state == STATES[:PLAN]
end

#resetvoid

This method returns an undefined value.

Reset the FSM to initial state.

Resets state to INTAKE, clears iteration count and history.



180
181
182
183
184
# File 'lib/agent_runtime/fsm.rb', line 180

def reset
  @state = STATES[:INTAKE]
  @iteration_count = 0
  @history = []
end

#state_nameSymbol, String

Get the name of the current state.



189
190
191
# File 'lib/agent_runtime/fsm.rb', line 189

def state_name
  STATES.key(@state) || "UNKNOWN"
end

#state_name_for(state_value) ⇒ Symbol, String

Get the name for a state value.



209
210
211
# File 'lib/agent_runtime/fsm.rb', line 209

def state_name_for(state_value)
  STATES.key(state_value) || "UNKNOWN"
end

#terminal?Boolean

Check if current state is terminal (FINALIZE or HALT).



145
146
147
# File 'lib/agent_runtime/fsm.rb', line 145

def terminal?
  TERMINAL_STATES.include?(@state)
end

#transition_to(new_state, reason: nil) ⇒ void

This method returns an undefined value.

Transition to a new state.

Validates the transition and records it in history.

Examples:

fsm.transition_to(FSM::STATES[:PLAN], reason: "Input normalized")

Raises:



160
161
162
163
164
# File 'lib/agent_runtime/fsm.rb', line 160

def transition_to(new_state, reason: nil)
  validate_transition(@state, new_state)
  @history << { from: @state, to: new_state, reason: reason, iteration: @iteration_count }
  @state = new_state
end

#validate_transition(from, to) ⇒ void

This method returns an undefined value.

Validate a state transition.

Raises:



199
200
201
202
203
# File 'lib/agent_runtime/fsm.rb', line 199

def validate_transition(from, to)
  return if VALID_TRANSITIONS[from]&.include?(to)

  raise ExecutionError, "Invalid transition from #{state_name_for(from)} to #{state_name_for(to)}"
end