phronomy 0.23.0 → 0.24.1
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/.mutant.yml +2 -2
- data/CHANGELOG.md +18 -0
- data/CONTRIBUTING.md +2 -2
- data/README.md +1 -1
- data/docs/architecture/multi-agent-handoff.md +35 -40
- data/docs/architecture/persistence.md +19 -8
- data/docs/architecture.md +8 -1
- data/docs/decisions/016-semantic-multi-agent-handoff.md +3 -1
- data/docs/decisions/028-preparing-recovery-replay-contract.md +106 -0
- data/docs/decisions/029-semantic-completion-and-application-effect-boundary.md +220 -0
- data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +235 -0
- data/docs/decisions/031-durable-multi-agent-coordination.md +301 -0
- data/docs/decisions/README.md +5 -1
- data/docs/design/durable-semantic-coordination/CHANGELOG_V2_REVISION_2.md +33 -0
- data/docs/design/durable-semantic-coordination/CONTINUATION_DECISION_REFACTOR.md +191 -0
- data/docs/design/durable-semantic-coordination/IMPLEMENTATION_DESIGN_V2.md +862 -0
- data/docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md +106 -0
- data/docs/design/durable-semantic-coordination/RECOVERY_CONTRACT_CLARIFICATIONS.md +179 -0
- data/docs/design/durable-semantic-coordination/RESPONSIBILITY_BOUNDARY_REVIEW.md +302 -0
- data/docs/features.md +35 -1
- data/docs/migrations/durable-semantic-coordination-v2.md +65 -0
- data/docs/persistence-backends.md +42 -3
- data/lib/phronomy/agent/agent_execution.rb +2 -2
- data/lib/phronomy/agent/agent_invocation.rb +1 -1
- data/lib/phronomy/agent/async_event_api.rb +18 -1
- data/lib/phronomy/agent/base.rb +28 -0
- data/lib/phronomy/agent/context_assembler.rb +1 -1
- data/lib/phronomy/agent/exact_execution.rb +153 -0
- data/lib/phronomy/agent/execution_cancellation.rb +25 -0
- data/lib/phronomy/agent/execution_coordinator.rb +484 -27
- data/lib/phronomy/{multi_agent → agent}/handoff.rb +4 -4
- data/lib/phronomy/{multi_agent → agent}/handoff_capability_factory.rb +3 -45
- data/lib/phronomy/{multi_agent → agent}/handoff_context.rb +26 -1
- data/lib/phronomy/{multi_agent/execution_coordinator.rb → agent/handoff_execution_coordinator.rb} +32 -5
- data/lib/phronomy/{multi_agent → agent}/handoff_policy.rb +7 -1
- data/lib/phronomy/{multi_agent → agent}/handoff_projection.rb +18 -2
- data/lib/phronomy/{multi_agent → agent}/handoff_request.rb +2 -2
- data/lib/phronomy/agent/handoff_runner.rb +178 -0
- data/lib/phronomy/agent/handoff_state.rb +43 -0
- data/lib/phronomy/agent/recovery_coordinator/continuation.rb +114 -211
- data/lib/phronomy/agent/recovery_coordinator/installation.rb +84 -130
- data/lib/phronomy/agent/recovery_coordinator/resolution.rb +76 -200
- data/lib/phronomy/agent/recovery_coordinator.rb +11 -5
- data/lib/phronomy/agent/recovery_support.rb +15 -23
- data/lib/phronomy/agent/ruby_llm_materializer.rb +2 -8
- data/lib/phronomy/agent/tool_invocation.rb +4 -2
- data/lib/phronomy/engine/runtime/team_ownership_registry.rb +77 -0
- data/lib/phronomy/engine/runtime.rb +16 -1
- data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +134 -0
- data/lib/phronomy/multi_agent/orchestrator.rb +59 -11
- data/lib/phronomy/multi_agent/team_coordinator.rb +473 -125
- data/lib/phronomy/multi_agent/team_execution.rb +44 -0
- data/lib/phronomy/multi_agent/team_root.rb +41 -0
- data/lib/phronomy/persistence/durable_codec.rb +60 -0
- data/lib/phronomy/persistence/in_memory.rb +264 -2
- data/lib/phronomy/persistence/repository_facades.rb +221 -2
- data/lib/phronomy/persistence.rb +95 -1
- data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +1 -0
- data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +137 -0
- data/lib/phronomy/testing/persistence_contract.rb +5 -0
- data/lib/phronomy/tools/agent.rb +1 -1
- data/lib/phronomy/version.rb +1 -1
- data/scripts/api_snapshot.rb +3 -3
- data/sig/phronomy/handoff.rbs +41 -0
- data/sig/phronomy/multi_agent.rbs +28 -32
- data/sig/phronomy/persistence.rbs +64 -3
- metadata +44 -12
- data/lib/phronomy/multi_agent/coordination_state.rb +0 -18
- data/lib/phronomy/multi_agent/coordinator.rb +0 -154
- data/lib/phronomy/multi_agent/runner.rb +0 -98
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: phronomy
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.24.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Raizo T.C.S
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-09-07 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: ruby_llm
|
|
@@ -78,6 +78,20 @@ dependencies:
|
|
|
78
78
|
- - "~>"
|
|
79
79
|
- !ruby/object:Gem::Version
|
|
80
80
|
version: '1.0'
|
|
81
|
+
- !ruby/object:Gem::Dependency
|
|
82
|
+
name: json
|
|
83
|
+
requirement: !ruby/object:Gem::Requirement
|
|
84
|
+
requirements:
|
|
85
|
+
- - "<"
|
|
86
|
+
- !ruby/object:Gem::Version
|
|
87
|
+
version: '3'
|
|
88
|
+
type: :runtime
|
|
89
|
+
prerelease: false
|
|
90
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
91
|
+
requirements:
|
|
92
|
+
- - "<"
|
|
93
|
+
- !ruby/object:Gem::Version
|
|
94
|
+
version: '3'
|
|
81
95
|
description: Phronomy is a Ruby AI agent framework that provides composable building
|
|
82
96
|
blocks — Agents, Workflows, Tools, Filters, and Tracing — for building AI agents
|
|
83
97
|
in Ruby. Powered by RubyLLM for LLM abstraction.
|
|
@@ -150,7 +164,17 @@ files:
|
|
|
150
164
|
- docs/decisions/025-process-local-agent-ownership-and-runtime-admission.md
|
|
151
165
|
- docs/decisions/026-workflow-runtime-admission-and-durable-terminal-barrier.md
|
|
152
166
|
- docs/decisions/027-llm-adapter-provider-boundary.md
|
|
167
|
+
- docs/decisions/028-preparing-recovery-replay-contract.md
|
|
168
|
+
- docs/decisions/029-semantic-completion-and-application-effect-boundary.md
|
|
169
|
+
- docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md
|
|
170
|
+
- docs/decisions/031-durable-multi-agent-coordination.md
|
|
153
171
|
- docs/decisions/README.md
|
|
172
|
+
- docs/design/durable-semantic-coordination/CHANGELOG_V2_REVISION_2.md
|
|
173
|
+
- docs/design/durable-semantic-coordination/CONTINUATION_DECISION_REFACTOR.md
|
|
174
|
+
- docs/design/durable-semantic-coordination/IMPLEMENTATION_DESIGN_V2.md
|
|
175
|
+
- docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md
|
|
176
|
+
- docs/design/durable-semantic-coordination/RECOVERY_CONTRACT_CLARIFICATIONS.md
|
|
177
|
+
- docs/design/durable-semantic-coordination/RESPONSIBILITY_BOUNDARY_REVIEW.md
|
|
154
178
|
- docs/features.md
|
|
155
179
|
- docs/getting-started.md
|
|
156
180
|
- docs/mcp-client.md
|
|
@@ -158,6 +182,7 @@ files:
|
|
|
158
182
|
- docs/migrations/0.16.md
|
|
159
183
|
- docs/migrations/0.19.md
|
|
160
184
|
- docs/migrations/0.22.md
|
|
185
|
+
- docs/migrations/durable-semantic-coordination-v2.md
|
|
161
186
|
- docs/persistence-backends.md
|
|
162
187
|
- docs/runtime-and-concurrency.md
|
|
163
188
|
- examples/README.md
|
|
@@ -186,7 +211,18 @@ files:
|
|
|
186
211
|
- lib/phronomy/agent/context_policy.rb
|
|
187
212
|
- lib/phronomy/agent/context_policy_input.rb
|
|
188
213
|
- lib/phronomy/agent/context_policy_input_builder.rb
|
|
214
|
+
- lib/phronomy/agent/exact_execution.rb
|
|
215
|
+
- lib/phronomy/agent/execution_cancellation.rb
|
|
189
216
|
- lib/phronomy/agent/execution_coordinator.rb
|
|
217
|
+
- lib/phronomy/agent/handoff.rb
|
|
218
|
+
- lib/phronomy/agent/handoff_capability_factory.rb
|
|
219
|
+
- lib/phronomy/agent/handoff_context.rb
|
|
220
|
+
- lib/phronomy/agent/handoff_execution_coordinator.rb
|
|
221
|
+
- lib/phronomy/agent/handoff_policy.rb
|
|
222
|
+
- lib/phronomy/agent/handoff_projection.rb
|
|
223
|
+
- lib/phronomy/agent/handoff_request.rb
|
|
224
|
+
- lib/phronomy/agent/handoff_runner.rb
|
|
225
|
+
- lib/phronomy/agent/handoff_state.rb
|
|
190
226
|
- lib/phronomy/agent/immutable.rb
|
|
191
227
|
- lib/phronomy/agent/journal_projection.rb
|
|
192
228
|
- lib/phronomy/agent/journal_record.rb
|
|
@@ -233,6 +269,7 @@ files:
|
|
|
233
269
|
- lib/phronomy/engine/runtime.rb
|
|
234
270
|
- lib/phronomy/engine/runtime/agent_ownership_registry.rb
|
|
235
271
|
- lib/phronomy/engine/runtime/shutdown_result.rb
|
|
272
|
+
- lib/phronomy/engine/runtime/team_ownership_registry.rb
|
|
236
273
|
- lib/phronomy/engine/runtime/timer_queue.rb
|
|
237
274
|
- lib/phronomy/engine/runtime/timer_service.rb
|
|
238
275
|
- lib/phronomy/engine/task.rb
|
|
@@ -254,21 +291,14 @@ files:
|
|
|
254
291
|
- lib/phronomy/llm_context_window/token_estimator.rb
|
|
255
292
|
- lib/phronomy/metrics.rb
|
|
256
293
|
- lib/phronomy/multi_agent/admission_registry.rb
|
|
257
|
-
- lib/phronomy/multi_agent/
|
|
258
|
-
- lib/phronomy/multi_agent/coordinator.rb
|
|
259
|
-
- lib/phronomy/multi_agent/execution_coordinator.rb
|
|
294
|
+
- lib/phronomy/multi_agent/durable_subagent_coordinator.rb
|
|
260
295
|
- lib/phronomy/multi_agent/fan_out_invocation.rb
|
|
261
296
|
- lib/phronomy/multi_agent/fan_out_session_builder.rb
|
|
262
|
-
- lib/phronomy/multi_agent/handoff.rb
|
|
263
|
-
- lib/phronomy/multi_agent/handoff_capability_factory.rb
|
|
264
|
-
- lib/phronomy/multi_agent/handoff_context.rb
|
|
265
|
-
- lib/phronomy/multi_agent/handoff_policy.rb
|
|
266
|
-
- lib/phronomy/multi_agent/handoff_projection.rb
|
|
267
|
-
- lib/phronomy/multi_agent/handoff_request.rb
|
|
268
297
|
- lib/phronomy/multi_agent/orchestrator.rb
|
|
269
298
|
- lib/phronomy/multi_agent/parallel_tool_chat.rb
|
|
270
|
-
- lib/phronomy/multi_agent/runner.rb
|
|
271
299
|
- lib/phronomy/multi_agent/team_coordinator.rb
|
|
300
|
+
- lib/phronomy/multi_agent/team_execution.rb
|
|
301
|
+
- lib/phronomy/multi_agent/team_root.rb
|
|
272
302
|
- lib/phronomy/output_parser.rb
|
|
273
303
|
- lib/phronomy/output_parser/base.rb
|
|
274
304
|
- lib/phronomy/output_parser/json_parser.rb
|
|
@@ -304,6 +334,7 @@ files:
|
|
|
304
334
|
- lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb
|
|
305
335
|
- lib/phronomy/testing/persistence_contract/an_agent_repository.rb
|
|
306
336
|
- lib/phronomy/testing/persistence_contract/an_execution_repository.rb
|
|
337
|
+
- lib/phronomy/testing/persistence_contract/coordination_repositories.rb
|
|
307
338
|
- lib/phronomy/token_usage.rb
|
|
308
339
|
- lib/phronomy/tool.rb
|
|
309
340
|
- lib/phronomy/tool/base.rb
|
|
@@ -350,6 +381,7 @@ files:
|
|
|
350
381
|
- sig/phronomy.rbs
|
|
351
382
|
- sig/phronomy/agent.rbs
|
|
352
383
|
- sig/phronomy/extensions.rbs
|
|
384
|
+
- sig/phronomy/handoff.rbs
|
|
353
385
|
- sig/phronomy/llm_adapter.rbs
|
|
354
386
|
- sig/phronomy/multi_agent.rbs
|
|
355
387
|
- sig/phronomy/persistence.rbs
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Phronomy
|
|
4
|
-
module MultiAgent
|
|
5
|
-
CoordinationState = Data.define(:active_agent, :active_handoff_context) do
|
|
6
|
-
def initialize(active_agent:, active_handoff_context: nil)
|
|
7
|
-
unless active_agent.is_a?(Phronomy::Agent::Base)
|
|
8
|
-
raise ArgumentError, "active_agent must be a Phronomy::Agent::Base"
|
|
9
|
-
end
|
|
10
|
-
if active_handoff_context && !active_handoff_context.is_a?(HandoffContext)
|
|
11
|
-
raise ArgumentError, "active_handoff_context must be a HandoffContext"
|
|
12
|
-
end
|
|
13
|
-
super
|
|
14
|
-
freeze
|
|
15
|
-
end
|
|
16
|
-
end
|
|
17
|
-
end
|
|
18
|
-
end
|
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Phronomy
|
|
4
|
-
module MultiAgent
|
|
5
|
-
class Coordinator
|
|
6
|
-
ATTACH_MUTEX = Mutex.new
|
|
7
|
-
private_constant :ATTACH_MUTEX
|
|
8
|
-
|
|
9
|
-
ApplyHandoffCommand = Data.define(:coordinator, :request, :context, :completion)
|
|
10
|
-
|
|
11
|
-
attr_reader :main_agent, :handoffs
|
|
12
|
-
|
|
13
|
-
def self.attach(main_agent:, handoffs:)
|
|
14
|
-
unless main_agent.is_a?(Phronomy::Agent::Base)
|
|
15
|
-
raise ArgumentError, "main_agent must be a Phronomy::Agent::Base"
|
|
16
|
-
end
|
|
17
|
-
normalized = Array(handoffs).freeze
|
|
18
|
-
|
|
19
|
-
ATTACH_MUTEX.synchronize do
|
|
20
|
-
existing = main_agent.instance_variable_get(:@_phronomy_multi_agent_coordinator)
|
|
21
|
-
if existing&.runtime_current?
|
|
22
|
-
existing.assert_compatible!(normalized)
|
|
23
|
-
return existing
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
created = new(main_agent: main_agent, handoffs: normalized)
|
|
27
|
-
main_agent.instance_variable_set(:@_phronomy_multi_agent_coordinator, created)
|
|
28
|
-
created
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def initialize(main_agent:, handoffs:)
|
|
33
|
-
@main_agent = main_agent
|
|
34
|
-
@handoffs = Array(handoffs).freeze
|
|
35
|
-
@runtime = Phronomy::Runtime.instance
|
|
36
|
-
validate_graph!
|
|
37
|
-
@bindings_by_source = build_bindings
|
|
38
|
-
@state_mutex = Mutex.new
|
|
39
|
-
@state = CoordinationState.new(active_agent: main_agent)
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def runtime_current?
|
|
43
|
-
Phronomy::Runtime.instance.equal?(@runtime)
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def snapshot
|
|
47
|
-
unless runtime_current?
|
|
48
|
-
raise Phronomy::RuntimeShutdownError,
|
|
49
|
-
"Multi-Agent coordination state belongs to a previous Runtime"
|
|
50
|
-
end
|
|
51
|
-
@state_mutex.synchronize { @state }
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
def outgoing_bindings(agent)
|
|
55
|
-
@bindings_by_source.fetch(agent.object_id, []).freeze
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def transition!(request, context)
|
|
59
|
-
completion = Phronomy::Task.deferred(name: "multi-agent-handoff")
|
|
60
|
-
command = ApplyHandoffCommand.new(
|
|
61
|
-
coordinator: self,
|
|
62
|
-
request: request,
|
|
63
|
-
context: context,
|
|
64
|
-
completion: completion
|
|
65
|
-
)
|
|
66
|
-
posted = Phronomy::Runtime.instance.event_loop.post(
|
|
67
|
-
Phronomy::Event.new(
|
|
68
|
-
type: :agent_terminal_ready,
|
|
69
|
-
target_id: Phronomy::EventLoop::SYSTEM_CHANNEL_ID,
|
|
70
|
-
payload: {command: command}
|
|
71
|
-
)
|
|
72
|
-
)
|
|
73
|
-
unless posted
|
|
74
|
-
completion.fail(
|
|
75
|
-
Phronomy::RuntimeShutdownError.new(
|
|
76
|
-
"EventLoop is not accepting Multi-Agent Handoff transitions"
|
|
77
|
-
)
|
|
78
|
-
)
|
|
79
|
-
end
|
|
80
|
-
completion.wait_result
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
# @api private
|
|
84
|
-
def deliver_on_event_loop(command)
|
|
85
|
-
runtime = Phronomy::Runtime.instance
|
|
86
|
-
unless runtime.event_loop.current?
|
|
87
|
-
raise Phronomy::Error,
|
|
88
|
-
"Multi-Agent coordination state may only be mutated on EventLoop"
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
request = command.request
|
|
92
|
-
context = command.context
|
|
93
|
-
current = snapshot
|
|
94
|
-
unless request.handoff.source_agent.equal?(current.active_agent)
|
|
95
|
-
raise Phronomy::HandoffError,
|
|
96
|
-
"Handoff source is no longer the active Agent"
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
next_state = CoordinationState.new(
|
|
100
|
-
active_agent: request.handoff.target_agent,
|
|
101
|
-
active_handoff_context: context
|
|
102
|
-
)
|
|
103
|
-
@state_mutex.synchronize { @state = next_state }
|
|
104
|
-
command.completion.complete(next_state)
|
|
105
|
-
rescue => error
|
|
106
|
-
command.completion.fail(error)
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
def assert_compatible!(handoffs)
|
|
110
|
-
incoming = graph_signature(handoffs)
|
|
111
|
-
current = graph_signature(@handoffs)
|
|
112
|
-
return true if incoming == current
|
|
113
|
-
|
|
114
|
-
raise Phronomy::ConfigurationError,
|
|
115
|
-
"a MultiAgent::Runner for this main Agent already exists with a different Handoff graph"
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
private
|
|
119
|
-
|
|
120
|
-
def validate_graph!
|
|
121
|
-
unless @handoffs.all? { |handoff| handoff.is_a?(Handoff) }
|
|
122
|
-
raise ArgumentError, "handoffs must contain only MultiAgent::Handoff values"
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
duplicates = @handoffs.group_by do |handoff|
|
|
126
|
-
[handoff.source_agent.object_id, handoff.target_agent.object_id]
|
|
127
|
-
end.select { |_key, values| values.length > 1 }
|
|
128
|
-
unless duplicates.empty?
|
|
129
|
-
raise ArgumentError, "duplicate Source → Target Handoff edges are not allowed"
|
|
130
|
-
end
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
def build_bindings
|
|
134
|
-
@handoffs.group_by(&:source_agent).to_h do |source, edges|
|
|
135
|
-
[
|
|
136
|
-
source.object_id,
|
|
137
|
-
edges.map { |handoff| HandoffCapabilityFactory.build(handoff) }.freeze
|
|
138
|
-
]
|
|
139
|
-
end.freeze
|
|
140
|
-
end
|
|
141
|
-
|
|
142
|
-
def graph_signature(handoffs)
|
|
143
|
-
Array(handoffs).map do |handoff|
|
|
144
|
-
[
|
|
145
|
-
handoff.source_agent.object_id,
|
|
146
|
-
handoff.target_agent.object_id,
|
|
147
|
-
handoff.policy.to_h,
|
|
148
|
-
handoff.description
|
|
149
|
-
]
|
|
150
|
-
end.sort_by { |row| [row[0], row[1], row[3]] }
|
|
151
|
-
end
|
|
152
|
-
end
|
|
153
|
-
end
|
|
154
|
-
end
|
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Phronomy
|
|
4
|
-
module MultiAgent
|
|
5
|
-
class Runner
|
|
6
|
-
MAX_HANDOFFS = 20
|
|
7
|
-
|
|
8
|
-
attr_reader :main_agent, :handoffs
|
|
9
|
-
|
|
10
|
-
# @api public
|
|
11
|
-
def initialize(main_agent:, handoffs: [])
|
|
12
|
-
@main_agent = main_agent
|
|
13
|
-
@handoffs = Array(handoffs).freeze
|
|
14
|
-
@coordinator = Coordinator.attach(
|
|
15
|
-
main_agent: main_agent,
|
|
16
|
-
handoffs: @handoffs
|
|
17
|
-
)
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
# @api public
|
|
21
|
-
def invoke(input, config: {})
|
|
22
|
-
trace_handle = Phronomy::Tracing::Automatic.start(
|
|
23
|
-
"multi_agent.turn",
|
|
24
|
-
input: input,
|
|
25
|
-
main_agent_id: @main_agent.agent_id,
|
|
26
|
-
**@main_agent.send(:_build_caller_meta, config)
|
|
27
|
-
)
|
|
28
|
-
result = nil
|
|
29
|
-
operation_error = nil
|
|
30
|
-
|
|
31
|
-
@coordinator = Coordinator.attach(
|
|
32
|
-
main_agent: @main_agent,
|
|
33
|
-
handoffs: @handoffs
|
|
34
|
-
)
|
|
35
|
-
runtime = Phronomy::Runtime.instance
|
|
36
|
-
runtime.__admit_multi_agent(@coordinator)
|
|
37
|
-
handoffs_taken = 0
|
|
38
|
-
current_input = input
|
|
39
|
-
|
|
40
|
-
result = loop do
|
|
41
|
-
state = @coordinator.snapshot
|
|
42
|
-
active_agent = state.active_agent
|
|
43
|
-
bindings = @coordinator.outgoing_bindings(active_agent)
|
|
44
|
-
agent_result = active_agent.invoke(
|
|
45
|
-
current_input,
|
|
46
|
-
config: config.merge(
|
|
47
|
-
phronomy_handoff_bindings: bindings,
|
|
48
|
-
phronomy_handoff_context: state.active_handoff_context
|
|
49
|
-
)
|
|
50
|
-
)
|
|
51
|
-
|
|
52
|
-
request = agent_result[:handoff_request]
|
|
53
|
-
break public_result(agent_result, active_agent) unless request
|
|
54
|
-
|
|
55
|
-
if handoffs_taken >= MAX_HANDOFFS
|
|
56
|
-
raise Phronomy::HandoffError,
|
|
57
|
-
"Exceeded maximum Handoffs (#{MAX_HANDOFFS}) in one user turn"
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
manifest = agent_result.fetch(:_phronomy_handoff_manifest)
|
|
61
|
-
context = HandoffProjection.new.build(
|
|
62
|
-
request: request,
|
|
63
|
-
manifest: manifest,
|
|
64
|
-
persistence: active_agent.persistence,
|
|
65
|
-
source_agent: active_agent
|
|
66
|
-
)
|
|
67
|
-
@coordinator.transition!(request, context)
|
|
68
|
-
current_input = request.responsibility
|
|
69
|
-
handoffs_taken += 1
|
|
70
|
-
end
|
|
71
|
-
result
|
|
72
|
-
rescue => error
|
|
73
|
-
operation_error = error
|
|
74
|
-
raise
|
|
75
|
-
ensure
|
|
76
|
-
runtime&.__release_multi_agent(@coordinator) if defined?(@coordinator)
|
|
77
|
-
trace_output = if result.is_a?(Hash)
|
|
78
|
-
result[:output] || result["output"]
|
|
79
|
-
else
|
|
80
|
-
result
|
|
81
|
-
end
|
|
82
|
-
Phronomy::Tracing::Automatic.finish(
|
|
83
|
-
trace_handle,
|
|
84
|
-
output: trace_output,
|
|
85
|
-
error: operation_error
|
|
86
|
-
)
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
private
|
|
90
|
-
|
|
91
|
-
def public_result(result, agent)
|
|
92
|
-
result.reject { |key, _| key.to_s.start_with?("_phronomy_") }
|
|
93
|
-
.except(:handoff_request)
|
|
94
|
-
.merge(agent: agent)
|
|
95
|
-
end
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
end
|