phronomy 0.28.0 → 0.29.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/README.md +20 -5
- data/docs/application-recipes.md +229 -0
- data/docs/architecture/dx05-agent-scope.md +119 -0
- data/docs/architecture/dx05-workflow-task-state.md +134 -0
- data/docs/architecture/error-api-review.md +245 -0
- data/docs/failure-reason-codes.md +45 -0
- data/docs/features.md +4 -1
- data/docs/getting-started.md +44 -43
- data/docs/ordered-event-delivery.md +54 -0
- data/docs/workflow-completion.md +92 -0
- data/gemfiles/http_minimum.gemfile +5 -0
- data/lib/phronomy/agent/agent_not_found_error.rb +16 -0
- data/lib/phronomy/agent/agent_unloaded_error.rb +14 -0
- data/lib/phronomy/agent/api/agent.rb +13 -0
- data/lib/phronomy/agent/async_event_api.rb +111 -87
- data/lib/phronomy/agent/base.rb +74 -43
- data/lib/phronomy/agent/before_llm_input.rb +7 -1
- data/lib/phronomy/agent/execution/exact_execution.rb +11 -9
- data/lib/phronomy/agent/execution/execution_coordinator.rb +16 -13
- data/lib/phronomy/agent/execution/execution_outcome_committer.rb +1 -1
- data/lib/phronomy/agent/execution/initial_preparation.rb +1 -4
- data/lib/phronomy/agent/execution/runtime_record_encoder.rb +1 -4
- data/lib/phronomy/agent/filterable.rb +21 -15
- data/lib/phronomy/agent/lifecycle/lifetime_api.rb +76 -0
- data/lib/phronomy/agent/lifecycle/operation_lease.rb +33 -0
- data/lib/phronomy/agent/lifecycle/operation_result.rb +53 -0
- data/lib/phronomy/agent/lifecycle/ownership_registry.rb +99 -8
- data/lib/phronomy/agent/lifecycle/scope.rb +198 -0
- data/lib/phronomy/agent/lifecycle/scope_cleanup_error.rb +19 -0
- data/lib/phronomy/agent/lifecycle/unload_transition.rb +78 -0
- data/lib/phronomy/agent/recovery/recovery_coordinator/continuation.rb +3 -3
- data/lib/phronomy/agent/recovery/recovery_coordinator/installation.rb +7 -7
- data/lib/phronomy/agent/recovery/recovery_coordinator/resolution.rb +14 -18
- data/lib/phronomy/agent/recovery/recovery_coordinator.rb +5 -9
- data/lib/phronomy/agent/recovery/recovery_support.rb +2 -8
- data/lib/phronomy/agent/stream_callback_error.rb +2 -2
- data/lib/phronomy/common/error.rb +65 -1
- data/lib/phronomy/content_store/base.rb +7 -2
- data/lib/phronomy/context/assembly.rb +11 -18
- data/lib/phronomy/embeddings/authentication_error.rb +11 -0
- data/lib/phronomy/embeddings/backends/ruby_llm_embeddings.rb +5 -1
- data/lib/phronomy/embeddings/rate_limit_error.rb +11 -0
- data/lib/phronomy/execution/execution_cancellation_error.rb +2 -2
- data/lib/phronomy/execution/execution_timeout_error.rb +2 -2
- data/lib/phronomy/execution/task_result.rb +7 -2
- data/lib/phronomy/filter/filter_block_error.rb +2 -2
- data/lib/phronomy/generation/low_confidence_error.rb +2 -2
- data/lib/phronomy/integrations/ordered_event_delivery.rb +208 -0
- data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +5 -5
- data/lib/phronomy/multi_agent/team_busy_error.rb +18 -0
- data/lib/phronomy/multi_agent/team_coordinator.rb +20 -7
- data/lib/phronomy/multi_agent/team_not_found_error.rb +18 -0
- data/lib/phronomy/storage/condition_failed_error.rb +2 -2
- data/lib/phronomy/storage/unique_constraint_error.rb +2 -2
- data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +1 -1
- data/lib/phronomy/tool/base.rb +25 -23
- data/lib/phronomy/tracing/base.rb +35 -6
- data/lib/phronomy/tracing/open_telemetry_tracer.rb +4 -29
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/workflow/execution/workflow.rb +30 -2
- data/lib/phronomy/workflow/execution/workflow_execution_environment.rb +1 -0
- data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +74 -3
- data/lib/phronomy/workflow/execution/workflow_runner.rb +48 -14
- data/lib/phronomy/workflow/execution/workflow_task_execution.rb +153 -0
- data/lib/phronomy/workflow/execution/workflow_task_reception.rb +72 -0
- data/lib/phronomy/workflow/execution/workflow_task_resume_error.rb +14 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_engine_environment.rb +26 -3
- data/lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb +3 -1
- data/lib/phronomy/workflow/runtime_binding/workflow_task_session.rb +76 -0
- data/lib/phronomy.rb +6 -0
- data/scripts/api_snapshot.rb +12 -0
- data/scripts/check_readme_ruby.rb +24 -29
- data/scripts/check_readme_runnable.rb +92 -81
- data/sig/phronomy/agent/agent_not_found_error.rbs +6 -0
- data/sig/phronomy/agent/agent_unloaded_error.rbs +6 -0
- data/sig/phronomy/agent/stream_callback_error.rbs +8 -0
- data/sig/phronomy/agent.rbs +18 -0
- data/sig/phronomy/embeddings/base.rbs +6 -0
- data/sig/phronomy/execution/execution.rbs +2 -2
- data/sig/phronomy/extensions.rbs +1 -0
- data/sig/phronomy/filter/filter_block_error.rbs +6 -0
- data/sig/phronomy/generation/low_confidence_error.rbs +6 -0
- data/sig/phronomy/integrations.rbs +14 -0
- data/sig/phronomy/multi_agent/team_busy_error.rbs +8 -0
- data/sig/phronomy/multi_agent/team_not_found_error.rbs +8 -0
- data/sig/phronomy/storage/contracts.rbs +2 -2
- data/sig/phronomy/workflow/execution/workflow_execution_environment.rbs +1 -0
- data/sig/phronomy/workflow.rbs +4 -0
- data/sig/phronomy.rbs +4 -0
- metadata +49 -2
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "../persistence/errors"
|
|
4
|
+
|
|
5
|
+
module Phronomy
|
|
6
|
+
# Only the requested Agent root is absent, not one of its referenced records.
|
|
7
|
+
# @api public
|
|
8
|
+
class AgentNotFoundError < Phronomy::Persistence::NotFoundError
|
|
9
|
+
attr_reader :agent_id
|
|
10
|
+
|
|
11
|
+
def initialize(agent_id:)
|
|
12
|
+
@agent_id = agent_id.to_s.dup.freeze
|
|
13
|
+
super("Agent not found: #{@agent_id}", code: "agent.not_found")
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
# An old Agent reference cannot act on a later incarnation of the same ID.
|
|
5
|
+
# @api public
|
|
6
|
+
class AgentUnloadedError < Phronomy::Error
|
|
7
|
+
attr_reader :agent_id
|
|
8
|
+
|
|
9
|
+
def initialize(agent_id:)
|
|
10
|
+
@agent_id = agent_id.to_s.dup.freeze
|
|
11
|
+
super("Agent #{@agent_id.inspect} has been unloaded", code: "agent.unloaded")
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -3,6 +3,17 @@
|
|
|
3
3
|
module Phronomy
|
|
4
4
|
module Agent
|
|
5
5
|
StreamEvent = Data.define(:type, :payload)
|
|
6
|
+
|
|
7
|
+
# Own Agents acquired by the block, then attempt every release. A returned
|
|
8
|
+
# TaskResult is awaited and unwrapped; a plain value is returned unchanged.
|
|
9
|
+
# @yield [scope] the Runtime-bound owner of newly materialized Agents
|
|
10
|
+
# @yieldparam scope [Phronomy::Agent::Scope]
|
|
11
|
+
# @return [Object] the block value or resolved TaskResult value
|
|
12
|
+
# @raise [ScopeCleanupError] successful work whose Agent release failed
|
|
13
|
+
# @api public
|
|
14
|
+
def self.open_scope(&block)
|
|
15
|
+
Scope.send(:open, &block)
|
|
16
|
+
end
|
|
6
17
|
end
|
|
7
18
|
end
|
|
8
19
|
|
|
@@ -15,3 +26,5 @@ end
|
|
|
15
26
|
# Durable Agent Recovery and Agent-incarnation event binding.
|
|
16
27
|
require_relative "../recovery/recovery_support"
|
|
17
28
|
require_relative "../recovery/recovery_coordinator"
|
|
29
|
+
|
|
30
|
+
Phronomy::Agent::Base.prepend(Phronomy::Agent::LifetimeApi)
|
|
@@ -13,31 +13,39 @@ module Phronomy
|
|
|
13
13
|
# - Recovery resolution uses resolve/resolve_async.
|
|
14
14
|
module AsyncEventApi
|
|
15
15
|
def start_reserved_async(reservation:, input:, config: {})
|
|
16
|
-
|
|
17
|
-
|
|
16
|
+
__with_operation(async: true) do
|
|
17
|
+
unless reservation.is_a?(ReservedExecution) && reservation.agent_id == agent_id
|
|
18
|
+
raise ArgumentError, "reservation must belong to this Agent"
|
|
19
|
+
end
|
|
20
|
+
config = __invocation_config(_prepare_invocation_config(config, nil))
|
|
21
|
+
ExactExecution.start(agent: self, execution_id: reservation.execution_id, input: input,
|
|
22
|
+
config: config.merge(phronomy_reservation: reservation.correlation))
|
|
18
23
|
end
|
|
19
|
-
config = __invocation_config(_prepare_invocation_config(config, nil))
|
|
20
|
-
ExactExecution.start(agent: self, execution_id: reservation.execution_id, input: input,
|
|
21
|
-
config: config.merge(phronomy_reservation: reservation.correlation))
|
|
22
24
|
end
|
|
23
25
|
|
|
24
26
|
def resume_async(execution_id, config: {})
|
|
25
|
-
|
|
26
|
-
|
|
27
|
+
__with_operation(async: true) do
|
|
28
|
+
config = __invocation_config(_prepare_invocation_config(config, nil))
|
|
29
|
+
ExactExecution.start(agent: self, execution_id: execution_id, input: nil, config: config, resume_only: true)
|
|
30
|
+
end
|
|
27
31
|
end
|
|
28
32
|
|
|
29
33
|
def resume(execution_id, config: {})
|
|
30
|
-
|
|
31
|
-
|
|
34
|
+
__with_operation do
|
|
35
|
+
_check_event_loop_reentrancy(:resume, :resume_async)
|
|
36
|
+
resume_async(execution_id, config: config).wait_result
|
|
37
|
+
end
|
|
32
38
|
end
|
|
33
39
|
|
|
34
40
|
def cancel_async(execution_id)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
+
__with_operation(async: true) do
|
|
42
|
+
__assert_live_agent!
|
|
43
|
+
environment = __execution_environment
|
|
44
|
+
environment.submit(on_full: :raise) do
|
|
45
|
+
requested = persistence.request_cancellation(agent_id: agent_id, execution_id: execution_id)
|
|
46
|
+
ExecutionCancellation.signal(execution_id, agent_id, environment: environment) if requested
|
|
47
|
+
persistence.observe_execution(agent_id: agent_id, execution_id: execution_id)
|
|
48
|
+
end
|
|
41
49
|
end
|
|
42
50
|
end
|
|
43
51
|
|
|
@@ -48,15 +56,17 @@ module Phronomy
|
|
|
48
56
|
**removed_options,
|
|
49
57
|
&removed_block
|
|
50
58
|
)
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
__with_operation do
|
|
60
|
+
_reject_removed_invocation_listener_arguments!(removed_options, removed_block)
|
|
61
|
+
config = _prepare_invocation_config(config, invocation_context)
|
|
62
|
+
_check_event_loop_reentrancy(:invoke, :invoke_async)
|
|
63
|
+
_start_agent_operation(
|
|
64
|
+
input,
|
|
65
|
+
config: config,
|
|
66
|
+
mode: :invoke,
|
|
67
|
+
listener: _phronomy_event_listener
|
|
68
|
+
).wait_result
|
|
69
|
+
end
|
|
60
70
|
end
|
|
61
71
|
|
|
62
72
|
def invoke_async(
|
|
@@ -66,14 +76,16 @@ module Phronomy
|
|
|
66
76
|
**removed_options,
|
|
67
77
|
&removed_block
|
|
68
78
|
)
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
79
|
+
__with_operation(async: true) do
|
|
80
|
+
_reject_removed_invocation_listener_arguments!(removed_options, removed_block)
|
|
81
|
+
config = _prepare_invocation_config(config, invocation_context)
|
|
82
|
+
_start_agent_operation(
|
|
83
|
+
input,
|
|
84
|
+
config: config,
|
|
85
|
+
mode: :invoke,
|
|
86
|
+
listener: _phronomy_event_listener
|
|
87
|
+
)
|
|
88
|
+
end
|
|
77
89
|
end
|
|
78
90
|
|
|
79
91
|
def stream(
|
|
@@ -83,16 +95,18 @@ module Phronomy
|
|
|
83
95
|
**removed_options,
|
|
84
96
|
&removed_block
|
|
85
97
|
)
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
98
|
+
__with_operation do
|
|
99
|
+
_reject_removed_invocation_listener_arguments!(removed_options, removed_block)
|
|
100
|
+
listener = _required_stream_listener!(:stream)
|
|
101
|
+
config = _prepare_invocation_config(config, invocation_context)
|
|
102
|
+
_check_event_loop_reentrancy(:stream, :stream_async)
|
|
103
|
+
_start_agent_operation(
|
|
104
|
+
input,
|
|
105
|
+
config: config,
|
|
106
|
+
mode: :stream,
|
|
107
|
+
listener: listener
|
|
108
|
+
).wait_result
|
|
109
|
+
end
|
|
96
110
|
end
|
|
97
111
|
|
|
98
112
|
def stream_async(
|
|
@@ -102,41 +116,47 @@ module Phronomy
|
|
|
102
116
|
**removed_options,
|
|
103
117
|
&removed_block
|
|
104
118
|
)
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
119
|
+
__with_operation(async: true) do
|
|
120
|
+
_reject_removed_invocation_listener_arguments!(removed_options, removed_block)
|
|
121
|
+
listener = _required_stream_listener!(:stream_async)
|
|
122
|
+
config = _prepare_invocation_config(config, invocation_context)
|
|
123
|
+
_start_agent_operation(
|
|
124
|
+
input,
|
|
125
|
+
config: config,
|
|
126
|
+
mode: :stream,
|
|
127
|
+
listener: listener
|
|
128
|
+
)
|
|
129
|
+
end
|
|
114
130
|
end
|
|
115
131
|
|
|
116
132
|
def approve(execution_id, approval_request_id:, approved: true, config: {})
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
133
|
+
__with_operation do
|
|
134
|
+
_check_event_loop_reentrancy(:approve, :approve_async)
|
|
135
|
+
approve_async(
|
|
136
|
+
execution_id,
|
|
137
|
+
approval_request_id: approval_request_id,
|
|
138
|
+
approved: approved,
|
|
139
|
+
config: config
|
|
140
|
+
).wait_result
|
|
141
|
+
end
|
|
124
142
|
end
|
|
125
143
|
|
|
126
144
|
def approve_async(execution_id, approval_request_id:, approved: true, config: {})
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
owner
|
|
131
|
-
|
|
132
|
-
|
|
145
|
+
__with_operation(async: true) do
|
|
146
|
+
_reject_removed_generic_identity_keys!(config)
|
|
147
|
+
owner = __execution_environment.existing_registry&.agent_execution_owner(execution_id)
|
|
148
|
+
coordinator = if owner&.agent&.equal?(self)
|
|
149
|
+
owner.coordinator
|
|
150
|
+
else
|
|
151
|
+
execution_coordinator_for(config)
|
|
152
|
+
end
|
|
153
|
+
coordinator.resume(
|
|
154
|
+
execution_id,
|
|
155
|
+
approval_request_id: approval_request_id,
|
|
156
|
+
approved: approved,
|
|
157
|
+
config: config
|
|
158
|
+
)
|
|
133
159
|
end
|
|
134
|
-
coordinator.resume(
|
|
135
|
-
execution_id,
|
|
136
|
-
approval_request_id: approval_request_id,
|
|
137
|
-
approved: approved,
|
|
138
|
-
config: config
|
|
139
|
-
)
|
|
140
160
|
end
|
|
141
161
|
|
|
142
162
|
def resolve(
|
|
@@ -147,15 +167,17 @@ module Phronomy
|
|
|
147
167
|
result: Phronomy::Agent::RecoveryRules::MISSING,
|
|
148
168
|
error: Phronomy::Agent::RecoveryRules::MISSING
|
|
149
169
|
)
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
170
|
+
__with_operation do
|
|
171
|
+
_check_event_loop_reentrancy(:resolve, :resolve_async)
|
|
172
|
+
resolve_async(
|
|
173
|
+
execution_id,
|
|
174
|
+
expected_execution_revision: expected_execution_revision,
|
|
175
|
+
subject: subject,
|
|
176
|
+
outcome: outcome,
|
|
177
|
+
result: result,
|
|
178
|
+
error: error
|
|
179
|
+
).wait_result
|
|
180
|
+
end
|
|
159
181
|
end
|
|
160
182
|
|
|
161
183
|
def resolve_async(
|
|
@@ -166,14 +188,16 @@ module Phronomy
|
|
|
166
188
|
result: Phronomy::Agent::RecoveryRules::MISSING,
|
|
167
189
|
error: Phronomy::Agent::RecoveryRules::MISSING
|
|
168
190
|
)
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
191
|
+
__with_operation(async: true) do
|
|
192
|
+
Phronomy::Agent::RecoveryCoordinator.new(self).resolve(
|
|
193
|
+
execution_id,
|
|
194
|
+
expected_execution_revision: expected_execution_revision,
|
|
195
|
+
subject: subject,
|
|
196
|
+
outcome: outcome,
|
|
197
|
+
result: result,
|
|
198
|
+
error: error
|
|
199
|
+
)
|
|
200
|
+
end
|
|
177
201
|
end
|
|
178
202
|
|
|
179
203
|
private
|
data/lib/phronomy/agent/base.rb
CHANGED
|
@@ -292,10 +292,14 @@ module Phronomy
|
|
|
292
292
|
# @api private
|
|
293
293
|
def __invocation_config(config) = config
|
|
294
294
|
|
|
295
|
-
def knowledge_snapshot
|
|
295
|
+
def knowledge_snapshot
|
|
296
|
+
__with_operation { persistence.knowledge_snapshot(agent_id: agent_id) }
|
|
297
|
+
end
|
|
296
298
|
|
|
297
299
|
# Listener inheritance is an explicit orchestration choice, not a private getter.
|
|
298
|
-
def event_listener
|
|
300
|
+
def event_listener
|
|
301
|
+
__with_operation(read_only: true) { _phronomy_event_listener }
|
|
302
|
+
end
|
|
299
303
|
|
|
300
304
|
attr_reader :agent_id, :persistence
|
|
301
305
|
|
|
@@ -371,65 +375,81 @@ module Phronomy
|
|
|
371
375
|
end
|
|
372
376
|
|
|
373
377
|
def agent_root
|
|
374
|
-
|
|
375
|
-
|
|
378
|
+
__with_operation(read_only: true) do
|
|
379
|
+
__assert_agent_accessible!
|
|
380
|
+
@root
|
|
381
|
+
end
|
|
376
382
|
end
|
|
377
383
|
|
|
378
384
|
def journal_projection
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
385
|
+
__with_operation(read_only: true) do
|
|
386
|
+
__assert_agent_accessible!
|
|
387
|
+
Agent::JournalProjection.new(
|
|
388
|
+
agent_root: @root,
|
|
389
|
+
records: _journal_records_snapshot
|
|
390
|
+
)
|
|
391
|
+
end
|
|
384
392
|
end
|
|
385
393
|
|
|
386
394
|
def transcript
|
|
387
|
-
|
|
395
|
+
__with_operation(read_only: true) do
|
|
396
|
+
journal_projection.transcript_records
|
|
397
|
+
end
|
|
388
398
|
end
|
|
389
399
|
|
|
390
400
|
def clear_transcript!
|
|
391
|
-
|
|
392
|
-
root
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
401
|
+
__with_operation do
|
|
402
|
+
mutate_context!(:transcript_cleared) do |root|
|
|
403
|
+
root.with(
|
|
404
|
+
agent_revision: root.agent_revision + 1,
|
|
405
|
+
context_revision: root.context_revision + 1,
|
|
406
|
+
transcript_generation: root.transcript_generation + 1
|
|
407
|
+
)
|
|
408
|
+
end
|
|
397
409
|
end
|
|
398
410
|
end
|
|
399
411
|
|
|
400
412
|
def clear_knowledge!
|
|
401
|
-
|
|
402
|
-
root
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
413
|
+
__with_operation do
|
|
414
|
+
mutate_context!(:knowledge_cleared) do |root|
|
|
415
|
+
root.with(
|
|
416
|
+
agent_revision: root.agent_revision + 1,
|
|
417
|
+
context_revision: root.context_revision + 1
|
|
418
|
+
)
|
|
419
|
+
end
|
|
406
420
|
end
|
|
407
421
|
end
|
|
408
422
|
|
|
409
423
|
def add_knowledge(content, metadata: {})
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
424
|
+
__with_operation do
|
|
425
|
+
__assert_live_agent!
|
|
426
|
+
current = agent_root
|
|
427
|
+
update = state_writer.add_knowledge(root: current, content: content, metadata: metadata)
|
|
428
|
+
apply_state_update(update)
|
|
429
|
+
self
|
|
430
|
+
end
|
|
415
431
|
end
|
|
416
432
|
|
|
417
433
|
def reset_context!
|
|
418
|
-
|
|
419
|
-
root
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
434
|
+
__with_operation do
|
|
435
|
+
mutate_context!(:context_reset) do |root|
|
|
436
|
+
root.with(
|
|
437
|
+
agent_revision: root.agent_revision + 1,
|
|
438
|
+
context_revision: root.context_revision + 1,
|
|
439
|
+
transcript_generation: root.transcript_generation + 1
|
|
440
|
+
)
|
|
441
|
+
end
|
|
424
442
|
end
|
|
425
443
|
end
|
|
426
444
|
|
|
427
445
|
def close!
|
|
428
|
-
|
|
429
|
-
root
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
446
|
+
__with_operation do
|
|
447
|
+
mutate_context!(:agent_closed, context_affecting: false) do |root|
|
|
448
|
+
root.with(
|
|
449
|
+
agent_revision: root.agent_revision + 1,
|
|
450
|
+
lifecycle_status: :closed
|
|
451
|
+
)
|
|
452
|
+
end
|
|
433
453
|
end
|
|
434
454
|
end
|
|
435
455
|
|
|
@@ -544,7 +564,7 @@ module Phronomy
|
|
|
544
564
|
|
|
545
565
|
# @api private
|
|
546
566
|
def __release_runtime_owner!(runtime)
|
|
547
|
-
return self if @_phronomy_runtime_owner_state
|
|
567
|
+
return self if %i[purged unloaded].include?(@_phronomy_runtime_owner_state)
|
|
548
568
|
return self unless @_phronomy_runtime_owner.equal?(runtime)
|
|
549
569
|
|
|
550
570
|
@_phronomy_runtime_owner_state = :released
|
|
@@ -579,7 +599,12 @@ module Phronomy
|
|
|
579
599
|
if load_existing
|
|
580
600
|
root = records = nil
|
|
581
601
|
@persistence.transaction do |tx|
|
|
582
|
-
|
|
602
|
+
begin
|
|
603
|
+
root = tx.agents.load(@agent_id)
|
|
604
|
+
rescue Phronomy::Persistence::NotFoundError => error
|
|
605
|
+
raise if error.is_a?(Phronomy::AgentNotFoundError)
|
|
606
|
+
raise Phronomy::AgentNotFoundError.new(agent_id: @agent_id), cause: error
|
|
607
|
+
end
|
|
583
608
|
records = tx.journals.read(
|
|
584
609
|
@agent_id,
|
|
585
610
|
limit: root.journal_position
|
|
@@ -656,11 +681,13 @@ module Phronomy
|
|
|
656
681
|
public
|
|
657
682
|
|
|
658
683
|
def tool_approval_policy(&block)
|
|
659
|
-
|
|
660
|
-
|
|
684
|
+
__with_operation do
|
|
685
|
+
__assert_live_agent!
|
|
686
|
+
raise ArgumentError, "tool_approval_policy requires a block" unless block
|
|
661
687
|
|
|
662
|
-
|
|
663
|
-
|
|
688
|
+
_approval_configuration_mutex.synchronize { @tool_approval_policy = block }
|
|
689
|
+
self
|
|
690
|
+
end
|
|
664
691
|
end
|
|
665
692
|
|
|
666
693
|
private
|
|
@@ -671,6 +698,8 @@ module Phronomy
|
|
|
671
698
|
true
|
|
672
699
|
when :purged
|
|
673
700
|
raise Phronomy::AgentPurgedError, "Agent #{agent_id.inspect} has been purged"
|
|
701
|
+
when :unloaded
|
|
702
|
+
raise Phronomy::AgentUnloadedError.new(agent_id: agent_id)
|
|
674
703
|
when :purging
|
|
675
704
|
raise Phronomy::Error, "Agent #{agent_id.inspect} is being purged"
|
|
676
705
|
when :recovery_required
|
|
@@ -728,10 +757,12 @@ module Phronomy
|
|
|
728
757
|
end
|
|
729
758
|
|
|
730
759
|
def _complete_result_task(task, result)
|
|
760
|
+
task.finish_operation! if task.respond_to?(:finish_operation!)
|
|
731
761
|
task.complete(result)
|
|
732
762
|
end
|
|
733
763
|
|
|
734
764
|
def _fail_result_task(task, error)
|
|
765
|
+
task.finish_operation! if task.respond_to?(:finish_operation!)
|
|
735
766
|
task.fail(error)
|
|
736
767
|
end
|
|
737
768
|
|
|
@@ -19,7 +19,13 @@ module Phronomy
|
|
|
19
19
|
def _before_llm_input = @before_llm_input
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
def before_llm_input
|
|
23
|
+
__with_operation(read_only: true) { @before_llm_input }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def before_llm_input=(value)
|
|
27
|
+
__with_operation { @before_llm_input = value }
|
|
28
|
+
end
|
|
23
29
|
|
|
24
30
|
private
|
|
25
31
|
|
|
@@ -18,7 +18,7 @@ module Phronomy
|
|
|
18
18
|
@agent, @id, @input, @config = agent, execution_id.to_s.freeze, input, config.freeze
|
|
19
19
|
@resume_only = resume_only
|
|
20
20
|
@environment = @agent.__execution_environment
|
|
21
|
-
@completion =
|
|
21
|
+
@completion = OperationResult.new(agent: @agent, name: "exact-execution:#{@id}")
|
|
22
22
|
end
|
|
23
23
|
|
|
24
24
|
def start
|
|
@@ -43,13 +43,14 @@ module Phronomy
|
|
|
43
43
|
[:absent, nil]
|
|
44
44
|
end
|
|
45
45
|
end
|
|
46
|
+
@completion.track(preparation)
|
|
46
47
|
preparation.on_complete do |prepared, error|
|
|
47
48
|
if error
|
|
48
|
-
@completion.
|
|
49
|
+
@completion.settle(error: error)
|
|
49
50
|
else
|
|
50
51
|
disposition, result = prepared
|
|
51
52
|
case disposition
|
|
52
|
-
when :terminal then @completion.
|
|
53
|
+
when :terminal then @completion.settle(result)
|
|
53
54
|
when :absent
|
|
54
55
|
source = @agent.send(:_start_agent_operation, @input,
|
|
55
56
|
config: @config.merge(phronomy_reserved_execution_id: @id, phronomy_exact_observers: [@completion].freeze),
|
|
@@ -58,15 +59,15 @@ module Phronomy
|
|
|
58
59
|
when :active
|
|
59
60
|
command = Wait.new(coordinator: self, agent: @agent, execution_id: @id)
|
|
60
61
|
posted = @environment.registry.post(command, completion: @completion)
|
|
61
|
-
@completion.
|
|
62
|
+
@completion.settle(error: Phronomy::RuntimeShutdownError.new("Exact execution observer rejected")) unless posted
|
|
62
63
|
end
|
|
63
64
|
end
|
|
64
65
|
rescue => failure
|
|
65
|
-
@completion.
|
|
66
|
+
@completion.settle(error: failure)
|
|
66
67
|
end
|
|
67
68
|
@completion
|
|
68
69
|
rescue => failure
|
|
69
|
-
@completion.
|
|
70
|
+
@completion.settle(error: failure)
|
|
70
71
|
@completion
|
|
71
72
|
end
|
|
72
73
|
|
|
@@ -101,7 +102,7 @@ module Phronomy
|
|
|
101
102
|
waiter.on_complete { |_result, failure| reconcile(failure) }
|
|
102
103
|
@environment.registry.register_agent_completion_waiter(@id, waiter)
|
|
103
104
|
rescue => failure
|
|
104
|
-
@completion.
|
|
105
|
+
@completion.settle(error: failure)
|
|
105
106
|
end
|
|
106
107
|
|
|
107
108
|
private
|
|
@@ -157,11 +158,12 @@ module Phronomy
|
|
|
157
158
|
))
|
|
158
159
|
end
|
|
159
160
|
end
|
|
161
|
+
@completion.track(task)
|
|
160
162
|
task.on_complete do |result, error|
|
|
161
|
-
error ? @completion.
|
|
163
|
+
error ? @completion.settle(error: error) : @completion.settle(result)
|
|
162
164
|
end
|
|
163
165
|
rescue => error
|
|
164
|
-
@completion.
|
|
166
|
+
@completion.settle(error: error)
|
|
165
167
|
end
|
|
166
168
|
|
|
167
169
|
def materialize(execution)
|