phronomy 0.14.0 → 0.15.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +65 -0
- data/README.md +236 -57
- data/benchmark/bench_agent_invoke.rb +2 -3
- data/docs/decisions/004-invoke-timeout-is-not-cancellation.md +14 -67
- data/docs/decisions/011-delegate-transport-policy-to-adapters.md +82 -0
- data/examples/workflows/agent_event_mapping.rb +104 -0
- data/examples/workflows/generic_task_event_mapping.rb +58 -0
- data/lib/phronomy/agent/agent_invocation.rb +385 -0
- data/lib/phronomy/agent/agent_invocation_registry.rb +75 -0
- data/lib/phronomy/agent/agent_invocation_session_builder.rb +448 -0
- data/lib/phronomy/agent/approval_evaluation_request.rb +102 -0
- data/lib/phronomy/agent/async_event_api.rb +471 -0
- data/lib/phronomy/agent/base.rb +500 -411
- data/lib/phronomy/agent/context/capability/base.rb +51 -119
- data/lib/phronomy/agent/llm_operation_result.rb +23 -0
- data/lib/phronomy/agent/phase_machine_builder.rb +75 -137
- data/lib/phronomy/agent/tool_approval_request.rb +121 -0
- data/lib/phronomy/agent/tool_call_intercepted.rb +11 -15
- data/lib/phronomy/agent/tool_executor.rb +47 -69
- data/lib/phronomy/agent/tool_invocation.rb +634 -0
- data/lib/phronomy/agent/tool_invocation_session_builder.rb +378 -0
- data/lib/phronomy/agent.rb +21 -9
- data/lib/phronomy/configuration.rb +42 -6
- data/lib/phronomy/engine/event_loop.rb +269 -112
- data/lib/phronomy/engine/fsm_session.rb +180 -142
- data/lib/phronomy/engine/task.rb +5 -10
- data/lib/phronomy/event.rb +8 -8
- data/lib/phronomy/generator_verifier.rb +253 -142
- data/lib/phronomy/invalid_async_entry_action_error.rb +9 -0
- data/lib/phronomy/invalid_async_transition_action_error.rb +11 -0
- data/lib/phronomy/invalid_async_workflow_action_error.rb +9 -0
- data/lib/phronomy/invocation_context.rb +5 -19
- data/lib/phronomy/llm_adapter/base.rb +25 -34
- data/lib/phronomy/metrics.rb +2 -0
- data/lib/phronomy/multi_agent/parallel_tool_chat.rb +54 -89
- data/lib/phronomy/stream_callback_error.rb +35 -0
- data/lib/phronomy/tools/mcp.rb +25 -0
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/workflow/phase_machine_builder.rb +129 -186
- data/lib/phronomy/workflow.rb +122 -261
- data/lib/phronomy/workflow_context.rb +54 -102
- data/lib/phronomy/workflow_runner.rb +238 -300
- data/lib/phronomy.rb +6 -4
- data/scripts/check_readme_runnable.rb +4 -1
- metadata +18 -7
- data/lib/phronomy/agent/concerns/retryable.rb +0 -103
- data/lib/phronomy/agent/context/capability/scope_policy.rb +0 -54
- data/lib/phronomy/agent/invocation_context.rb +0 -171
- data/lib/phronomy/agent/invocation_session.rb +0 -352
- data/lib/phronomy/agent/suspended_session_registry.rb +0 -54
|
@@ -3,39 +3,33 @@
|
|
|
3
3
|
module Phronomy
|
|
4
4
|
# Event-driven execution wrapper for a single FSM session.
|
|
5
5
|
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
# Created by a runner and registered with EventLoop. All public methods
|
|
10
|
-
# are called from the EventLoop thread — FSMSession is NOT thread-safe and must
|
|
11
|
-
# not be accessed concurrently from multiple threads.
|
|
12
|
-
#
|
|
6
|
+
# All public methods are called from the Runtime-owned EventLoop thread.
|
|
7
|
+
# FSMSession owns FSM execution only; it does not own external Task handles,
|
|
8
|
+
# activity tokens, callback correlation, or domain-specific stale-event policy.
|
|
13
9
|
class FSMSession
|
|
14
10
|
FINISH = WorkflowRunner::FINISH
|
|
15
11
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
declared_states:, wait_state_names:, external_events:, phase_machine_class:,
|
|
35
|
-
recursion_limit:, event_loop:, timer_queue_provider:, action_timeouts: {},
|
|
36
|
-
resume_event: nil, resume_phase: nil)
|
|
12
|
+
attr_reader :id, :context
|
|
13
|
+
|
|
14
|
+
def initialize(
|
|
15
|
+
id:,
|
|
16
|
+
context:,
|
|
17
|
+
entry_point:,
|
|
18
|
+
entry_actions:,
|
|
19
|
+
auto_state_set:,
|
|
20
|
+
declared_states:,
|
|
21
|
+
wait_state_names:,
|
|
22
|
+
external_events:,
|
|
23
|
+
phase_machine_class:,
|
|
24
|
+
recursion_limit:,
|
|
25
|
+
event_loop:,
|
|
26
|
+
resume_event: nil,
|
|
27
|
+
resume_phase: nil,
|
|
28
|
+
stable_observer: nil
|
|
29
|
+
)
|
|
37
30
|
@id = id
|
|
38
31
|
@ctx = context
|
|
32
|
+
@context = context
|
|
39
33
|
@entry_point = entry_point
|
|
40
34
|
@entry_actions = entry_actions
|
|
41
35
|
@auto_state_set = auto_state_set
|
|
@@ -44,155 +38,145 @@ module Phronomy
|
|
|
44
38
|
@external_events = external_events
|
|
45
39
|
@phase_machine_class = phase_machine_class
|
|
46
40
|
@recursion_limit = recursion_limit
|
|
47
|
-
@action_timeouts = action_timeouts
|
|
48
41
|
@event_loop = event_loop
|
|
49
|
-
@timer_queue_provider = timer_queue_provider
|
|
50
42
|
@resume_event = resume_event
|
|
51
43
|
@resume_phase = resume_phase
|
|
44
|
+
@stable_observer = stable_observer
|
|
52
45
|
@step = 0
|
|
53
46
|
@done = false
|
|
54
47
|
@current_state = nil
|
|
55
48
|
@tracker = nil
|
|
56
49
|
end
|
|
57
50
|
|
|
58
|
-
# Begins workflow execution. Called by EventLoop on :start event.
|
|
59
51
|
def start
|
|
60
52
|
if @resume_event
|
|
61
|
-
# Resume from wait state: position tracker at the wait state, then fire the
|
|
62
|
-
# external event. state_machines fires before_transition (exit) and
|
|
63
|
-
# after_transition (entry) callbacks, so both actions execute here.
|
|
64
53
|
@current_state = @resume_phase
|
|
65
54
|
@tracker = build_tracker(@current_state)
|
|
66
55
|
@tracker.context = @ctx
|
|
67
|
-
|
|
68
|
-
|
|
56
|
+
fire_and_advance!(
|
|
57
|
+
Phronomy::Event.new(
|
|
58
|
+
type: @resume_event,
|
|
59
|
+
target_id: @id,
|
|
60
|
+
payload: nil
|
|
61
|
+
)
|
|
62
|
+
)
|
|
69
63
|
else
|
|
70
|
-
# Fresh start: state_machines does not fire callbacks on initialization,
|
|
71
|
-
# so we invoke the entry action for the initial state manually.
|
|
72
64
|
@current_state = @entry_point
|
|
73
65
|
@tracker = build_tracker(@current_state)
|
|
74
66
|
@tracker.context = @ctx
|
|
75
|
-
|
|
76
|
-
(@entry_actions[@current_state] || []).each do |c|
|
|
77
|
-
result = c.call(@ctx)
|
|
78
|
-
if result.is_a?(Phronomy::Task)
|
|
79
|
-
# Awaitable action: resume via on_complete without blocking EventLoop.
|
|
80
|
-
@tracker.async_pending = true
|
|
81
|
-
session_id = @id
|
|
82
|
-
current_state_name = @current_state
|
|
83
|
-
timeout_secs = @action_timeouts[current_state_name]
|
|
84
|
-
if timeout_secs
|
|
85
|
-
@timer_queue_provider.call.schedule(seconds: timeout_secs) do
|
|
86
|
-
next if result.done?
|
|
87
|
-
|
|
88
|
-
@event_loop.post(
|
|
89
|
-
Event.new(
|
|
90
|
-
type: :error,
|
|
91
|
-
target_id: Phronomy::EventLoop::SYSTEM_CHANNEL_ID,
|
|
92
|
-
payload: {session_id: session_id, result: Phronomy::ActionTimeoutError.new(
|
|
93
|
-
"Action in state #{current_state_name.inspect} timed out after #{timeout_secs}s"
|
|
94
|
-
)}
|
|
95
|
-
)
|
|
96
|
-
)
|
|
97
|
-
end
|
|
98
|
-
end
|
|
99
|
-
result.on_complete do |task_result, error|
|
|
100
|
-
if error
|
|
101
|
-
@event_loop.post(Event.new(type: :error, target_id: Phronomy::EventLoop::SYSTEM_CHANNEL_ID, payload: {session_id: session_id, result: error}))
|
|
102
|
-
next
|
|
103
|
-
end
|
|
104
|
-
if _fsm_context?(task_result)
|
|
105
|
-
@event_loop.post(Event.new(type: :action_completed, target_id: session_id, payload: task_result))
|
|
106
|
-
else
|
|
107
|
-
@event_loop.post(Event.new(type: :state_completed, target_id: session_id, payload: nil))
|
|
108
|
-
end
|
|
109
|
-
end
|
|
110
|
-
break # Only one async action at a time per state
|
|
111
|
-
elsif _fsm_context?(result)
|
|
112
|
-
@ctx = result
|
|
113
|
-
end
|
|
114
|
-
end
|
|
67
|
+
run_initial_entry_actions!
|
|
115
68
|
@tracker.context = @ctx
|
|
116
|
-
advance_or_halt
|
|
69
|
+
advance_or_halt
|
|
117
70
|
end
|
|
118
|
-
rescue =>
|
|
119
|
-
finish_with_error(
|
|
71
|
+
rescue => error
|
|
72
|
+
finish_with_error(error)
|
|
120
73
|
end
|
|
121
74
|
|
|
122
|
-
# Processes an event dispatched from EventLoop.
|
|
123
|
-
# Called for :state_completed, :action_completed, and all user-defined external events.
|
|
124
|
-
#
|
|
125
|
-
# @param event [Phronomy::Event]
|
|
126
|
-
# @api private
|
|
127
75
|
def handle(event)
|
|
128
76
|
return if @done
|
|
129
77
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
advance_or_halt
|
|
78
|
+
context_disposition = apply_context_event(event)
|
|
79
|
+
return if context_disposition == :consume
|
|
80
|
+
|
|
81
|
+
if context_disposition &&
|
|
82
|
+
!has_external_event_from?(@current_state, event.type)
|
|
136
83
|
return
|
|
137
84
|
end
|
|
138
85
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
fire_and_advance!(event.type)
|
|
144
|
-
rescue => e
|
|
145
|
-
finish_with_error(e)
|
|
86
|
+
fire_and_advance!(event)
|
|
87
|
+
rescue => error
|
|
88
|
+
finish_with_error(error)
|
|
146
89
|
end
|
|
147
90
|
|
|
148
91
|
private
|
|
149
92
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
93
|
+
def run_initial_entry_actions!
|
|
94
|
+
Array(@entry_actions[@current_state]).each do |callable|
|
|
95
|
+
result = callable.call(@ctx)
|
|
96
|
+
apply_synchronous_action_result!(result, @current_state)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def apply_synchronous_action_result!(result, state_name)
|
|
101
|
+
if result.is_a?(Phronomy::Task)
|
|
102
|
+
raise Phronomy::InvalidAsyncEntryActionError,
|
|
103
|
+
"Entry action for state #{state_name.inspect} returned Phronomy::Task. " \
|
|
104
|
+
"Start the asynchronous operation, register its callback/listener, " \
|
|
105
|
+
"and return the WorkflowContext or nil."
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
if _fsm_context?(result)
|
|
109
|
+
@ctx = result
|
|
110
|
+
@context = result
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def apply_context_event(event)
|
|
115
|
+
return false unless @ctx.respond_to?(:handle_fsm_event)
|
|
116
|
+
|
|
117
|
+
result = @ctx.handle_fsm_event(event)
|
|
118
|
+
return :consume if result == :consume
|
|
119
|
+
|
|
120
|
+
if _fsm_context?(result)
|
|
121
|
+
@ctx = result
|
|
122
|
+
@context = result
|
|
123
|
+
@tracker.context = @ctx
|
|
124
|
+
true
|
|
125
|
+
else
|
|
126
|
+
!!result
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def fire_and_advance!(event)
|
|
153
131
|
if @step >= @recursion_limit
|
|
154
132
|
raise Phronomy::RecursionLimitError,
|
|
155
133
|
"Recursion limit (#{@recursion_limit}) exceeded"
|
|
156
134
|
end
|
|
157
135
|
|
|
158
|
-
|
|
136
|
+
@tracker.context = @ctx
|
|
137
|
+
clear_selected_transition!
|
|
138
|
+
@tracker.current_event = event if @tracker.respond_to?(:current_event=)
|
|
139
|
+
transitioned = fire_event!(@tracker, event.type, @current_state)
|
|
140
|
+
return unless transitioned
|
|
141
|
+
|
|
159
142
|
@ctx = @tracker.context
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
@current_state = (next_phase == @current_state) ? FINISH : next_phase
|
|
143
|
+
@context = @ctx
|
|
144
|
+
@current_state = @tracker.phase.to_sym
|
|
163
145
|
@step += 1
|
|
146
|
+
advance_or_halt
|
|
147
|
+
ensure
|
|
148
|
+
@tracker.current_event = nil if @tracker&.respond_to?(:current_event=)
|
|
149
|
+
clear_selected_transition!
|
|
150
|
+
end
|
|
164
151
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
# background thread will post :action_completed or :state_completed.
|
|
168
|
-
if @tracker.async_pending
|
|
169
|
-
@tracker.async_pending = false
|
|
170
|
-
return
|
|
171
|
-
end
|
|
152
|
+
def clear_selected_transition!
|
|
153
|
+
return unless @tracker
|
|
172
154
|
|
|
173
|
-
|
|
155
|
+
if @tracker.respond_to?(:selected_transition_action=)
|
|
156
|
+
@tracker.selected_transition_action = nil
|
|
157
|
+
end
|
|
158
|
+
if @tracker.respond_to?(:selected_transition_metadata=)
|
|
159
|
+
@tracker.selected_transition_metadata = nil
|
|
160
|
+
end
|
|
174
161
|
end
|
|
175
162
|
|
|
176
|
-
# Determines the next action after the FSM has entered @current_state.
|
|
177
163
|
def advance_or_halt
|
|
178
164
|
return finish! if @current_state == FINISH
|
|
179
165
|
|
|
166
|
+
notify_stable_state!
|
|
167
|
+
|
|
180
168
|
if @wait_state_names.include?(@current_state)
|
|
181
|
-
|
|
169
|
+
halt!
|
|
170
|
+
return
|
|
182
171
|
end
|
|
183
172
|
|
|
184
173
|
if @auto_state_set.key?(@current_state)
|
|
185
|
-
|
|
174
|
+
post_session_event(:state_completed)
|
|
186
175
|
return
|
|
187
176
|
end
|
|
188
177
|
|
|
189
|
-
if has_external_event_from?(@current_state)
|
|
190
|
-
# Async IO pattern: the entry action spawned an IO thread that will post
|
|
191
|
-
# an external event back. Stay registered; do nothing here.
|
|
192
|
-
return
|
|
193
|
-
end
|
|
178
|
+
return if has_external_event_from?(@current_state)
|
|
194
179
|
|
|
195
|
-
# No transition declared — validate the state is known, then treat as terminal.
|
|
196
180
|
unless @declared_states.include?(@current_state)
|
|
197
181
|
raise ArgumentError, "State #{@current_state.inspect} is not defined"
|
|
198
182
|
end
|
|
@@ -200,52 +184,106 @@ module Phronomy
|
|
|
200
184
|
finish!
|
|
201
185
|
end
|
|
202
186
|
|
|
187
|
+
def notify_stable_state!
|
|
188
|
+
return unless @stable_observer
|
|
189
|
+
|
|
190
|
+
@stable_observer.call(
|
|
191
|
+
{
|
|
192
|
+
state: @current_state,
|
|
193
|
+
context: @ctx
|
|
194
|
+
}
|
|
195
|
+
)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def post_session_event(type, payload = nil)
|
|
199
|
+
event = Phronomy::Event.new(
|
|
200
|
+
type: type,
|
|
201
|
+
target_id: @id,
|
|
202
|
+
payload: payload
|
|
203
|
+
)
|
|
204
|
+
accepted =
|
|
205
|
+
if @event_loop.respond_to?(:post_to_session)
|
|
206
|
+
@event_loop.post_to_session(event)
|
|
207
|
+
else
|
|
208
|
+
@event_loop.post(event)
|
|
209
|
+
end
|
|
210
|
+
return if accepted
|
|
211
|
+
|
|
212
|
+
raise Phronomy::RuntimeShutdownError,
|
|
213
|
+
"EventLoop rejected #{type.inspect} for FSMSession #{@id}"
|
|
214
|
+
end
|
|
215
|
+
|
|
203
216
|
def finish!
|
|
217
|
+
return if @done
|
|
218
|
+
|
|
204
219
|
@done = true
|
|
205
220
|
@ctx.set_graph_metadata(thread_id: @id, phase: :__end__)
|
|
206
|
-
|
|
221
|
+
post_terminal_event(:finished, @ctx)
|
|
207
222
|
end
|
|
208
223
|
|
|
209
224
|
def halt!
|
|
225
|
+
return if @done
|
|
226
|
+
|
|
210
227
|
@done = true
|
|
211
228
|
@ctx.set_graph_metadata(thread_id: @id, phase: @current_state)
|
|
212
|
-
|
|
229
|
+
post_terminal_event(:halted, @ctx)
|
|
213
230
|
end
|
|
214
231
|
|
|
215
|
-
def finish_with_error(
|
|
232
|
+
def finish_with_error(error)
|
|
233
|
+
return if @done
|
|
234
|
+
|
|
216
235
|
@done = true
|
|
217
|
-
|
|
236
|
+
post_terminal_event(:error, error)
|
|
237
|
+
end
|
|
238
|
+
|
|
239
|
+
def post_terminal_event(type, result)
|
|
240
|
+
accepted = @event_loop.post(
|
|
241
|
+
Phronomy::Event.new(
|
|
242
|
+
type: type,
|
|
243
|
+
target_id: Phronomy::EventLoop::SYSTEM_CHANNEL_ID,
|
|
244
|
+
payload: {session_id: @id, result: result}
|
|
245
|
+
)
|
|
246
|
+
)
|
|
247
|
+
return if accepted
|
|
248
|
+
|
|
249
|
+
Phronomy.configuration.logger&.warn(
|
|
250
|
+
"[Phronomy::FSMSession] EventLoop rejected terminal event " \
|
|
251
|
+
"#{type.inspect} for #{@id}"
|
|
252
|
+
)
|
|
218
253
|
end
|
|
219
254
|
|
|
220
255
|
def fire_event!(tracker, event_name, from_state)
|
|
221
|
-
|
|
256
|
+
unless tracker.respond_to?(event_name)
|
|
257
|
+
raise ArgumentError,
|
|
258
|
+
"Unknown FSM event #{event_name.inspect} for state #{from_state.inspect}"
|
|
259
|
+
end
|
|
260
|
+
|
|
261
|
+
return true if tracker.public_send(event_name)
|
|
262
|
+
|
|
263
|
+
# A declared external event whose guards all reject is a valid no-op.
|
|
264
|
+
# Applications use this to reject stale or unrelated correlated events.
|
|
265
|
+
return false if has_external_event_from?(from_state, event_name)
|
|
222
266
|
|
|
223
267
|
raise ArgumentError,
|
|
224
268
|
"Transition from #{from_state.inspect} via event #{event_name.inspect} failed. " \
|
|
225
|
-
"
|
|
269
|
+
"The event is not declared for the current state."
|
|
226
270
|
end
|
|
227
271
|
|
|
228
|
-
def has_external_event_from?(state)
|
|
229
|
-
|
|
272
|
+
def has_external_event_from?(state, event_name = nil)
|
|
273
|
+
events = event_name ? {event_name => @external_events[event_name]} : @external_events
|
|
274
|
+
events.any? do |_name, transitions|
|
|
275
|
+
Array(transitions).any? { |transition| transition[:from] == state }
|
|
276
|
+
end
|
|
230
277
|
end
|
|
231
278
|
|
|
232
279
|
def build_tracker(from_state)
|
|
233
280
|
machine = @phase_machine_class.new
|
|
234
281
|
machine.instance_variable_set(:@phase, from_state.to_s)
|
|
235
|
-
machine.event_loop = @event_loop if machine.respond_to?(:event_loop=)
|
|
236
|
-
if machine.respond_to?(:timer_queue_provider=)
|
|
237
|
-
machine.timer_queue_provider = @timer_queue_provider
|
|
238
|
-
end
|
|
239
282
|
machine
|
|
240
283
|
end
|
|
241
284
|
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
# Task return values (strings, hashes, etc.) without hard-coding a specific
|
|
245
|
-
# class. Both WorkflowContext and Agent::InvocationContext qualify.
|
|
246
|
-
# @api private
|
|
247
|
-
def _fsm_context?(obj)
|
|
248
|
-
obj.respond_to?(:set_graph_metadata)
|
|
285
|
+
def _fsm_context?(object)
|
|
286
|
+
object.respond_to?(:set_graph_metadata)
|
|
249
287
|
end
|
|
250
288
|
end
|
|
251
289
|
end
|
data/lib/phronomy/engine/task.rb
CHANGED
|
@@ -212,17 +212,12 @@ module Phronomy
|
|
|
212
212
|
# If this task fails or is cancelled, the mapped task also fails/is
|
|
213
213
|
# cancelled with the same error. The block is never called in error cases.
|
|
214
214
|
#
|
|
215
|
-
# The
|
|
216
|
-
#
|
|
217
|
-
#
|
|
218
|
-
# +:action_completed+ path:
|
|
215
|
+
# The transformation runs from the source Task's completion callback.
|
|
216
|
+
# It is a generic value-composition API; Workflow entry actions do not await
|
|
217
|
+
# either the source Task or the mapped Task.
|
|
219
218
|
#
|
|
220
|
-
# @example
|
|
221
|
-
#
|
|
222
|
-
# TranslationAgent.new.invoke_async(ctx.query).map do |result|
|
|
223
|
-
# ctx.merge(answer: result[:output]) # returns WorkflowContext
|
|
224
|
-
# end
|
|
225
|
-
# }
|
|
219
|
+
# @example Transforming an Agent result outside a Workflow entry action
|
|
220
|
+
# output_task = agent.invoke_async("hello").map { |result| result[:output] }
|
|
226
221
|
#
|
|
227
222
|
# @yield [value] the completed value of this task
|
|
228
223
|
# @yieldreturn [Object] the value for the mapped task
|
data/lib/phronomy/event.rb
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Phronomy
|
|
4
|
-
# Immutable event
|
|
4
|
+
# Immutable event used for EventLoop communication.
|
|
5
5
|
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
9
|
-
#
|
|
10
|
-
#
|
|
11
|
-
#
|
|
12
|
-
#
|
|
6
|
+
# User-defined Workflow events carry application-owned payloads unchanged.
|
|
7
|
+
# Correlation identifiers, stale-event decisions, and domain interpretation
|
|
8
|
+
# remain application concerns.
|
|
9
|
+
#
|
|
10
|
+
# @param type [Symbol] event identifier
|
|
11
|
+
# @param target_id [String] FSMSession identifier
|
|
12
|
+
# @param payload [Object] optional event data
|
|
13
13
|
Event = Data.define(:type, :target_id, :payload)
|
|
14
14
|
end
|