phronomy 0.22.0 → 0.24.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/.mutant.yml +3 -4
- data/CHANGELOG.md +215 -10
- data/CONTRIBUTING.md +81 -9
- data/README.md +15 -6
- data/VERIFY.sh +587 -0
- data/benchmark/bench_agent_invoke.rb +2 -2
- data/benchmark/bench_context_assembler.rb +39 -68
- data/benchmark/bench_regression.rb +2 -2
- data/docs/architecture/agent-context.md +174 -0
- data/docs/architecture/before-llm-input.md +78 -0
- data/docs/architecture/context-management.md +232 -0
- data/docs/architecture/knowledge-and-rag.md +130 -0
- data/docs/architecture/multi-agent-handoff.md +147 -0
- data/docs/architecture/persistence.md +186 -0
- data/docs/architecture/removed/agent-context.md +72 -0
- data/docs/architecture/security-boundaries.md +173 -0
- data/docs/architecture/tracing.md +194 -0
- data/docs/architecture.md +89 -0
- data/docs/archive/design/archived/04_api_design.md +507 -0
- data/docs/archive/design/archived/09_guardrails.md +186 -0
- data/docs/archive/design/archived/17_rails_integration.md +175 -0
- data/docs/archive/design/historical/00_design_philosophy.md +122 -0
- data/docs/archive/design/historical/01_rubyllm_evaluation.md +178 -0
- data/docs/archive/design/historical/06_design_decisions.md +143 -0
- data/docs/changelog/0.14-and-earlier.md +1 -1
- data/docs/decisions/001-rubyllm-as-provider-layer.md +6 -1
- data/docs/decisions/002-workflow-context-immutability.md +26 -1
- data/docs/decisions/006-no-built-in-guardrails.md +2 -1
- data/docs/decisions/012-canonical-execution-log-and-context-policy.md +120 -38
- data/docs/decisions/014-unified-persistence-durable-state.md +9 -2
- data/docs/decisions/016-semantic-multi-agent-handoff.md +114 -0
- data/docs/decisions/017-design-authority-and-adr-governance.md +200 -0
- data/docs/decisions/018-durability-guarantees-and-failure-model.md +488 -0
- data/docs/decisions/019-filter-contract-and-security-boundaries.md +229 -0
- data/docs/decisions/020-canonical-workflow-instance-identity.md +177 -0
- data/docs/decisions/021-generic-agent-invocation-identity-removal.md +119 -0
- data/docs/decisions/022-agent-execution-parent-identity-and-runtime-routing-boundary.md +193 -0
- data/docs/decisions/023-fsm-session-incarnation-identity-and-routing.md +139 -0
- data/docs/decisions/024-event-loop-single-writer-agent-runtime.md +188 -0
- data/docs/decisions/025-process-local-agent-ownership-and-runtime-admission.md +249 -0
- data/docs/decisions/026-workflow-runtime-admission-and-durable-terminal-barrier.md +257 -0
- data/docs/decisions/027-llm-adapter-provider-boundary.md +93 -0
- 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 +176 -0
- 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 +65 -11
- data/docs/getting-started.md +77 -45
- data/docs/migrations/0.19.md +14 -7
- data/docs/migrations/0.22.md +390 -0
- data/docs/migrations/durable-semantic-coordination-v2.md +65 -0
- data/docs/persistence-backends.md +129 -40
- data/docs/runtime-and-concurrency.md +227 -33
- data/examples/README.md +13 -0
- data/lib/phronomy/agent/agent_execution.rb +21 -17
- data/lib/phronomy/agent/agent_invocation.rb +288 -93
- data/lib/phronomy/agent/agent_invocation_session_builder.rb +236 -202
- data/lib/phronomy/agent/agent_root.rb +3 -3
- data/lib/phronomy/agent/approval_evaluation_request.rb +37 -19
- data/lib/phronomy/agent/async_event_api.rb +162 -72
- data/lib/phronomy/agent/base.rb +416 -181
- data/lib/phronomy/agent/concerns/before_llm_input.rb +1 -1
- data/lib/phronomy/agent/context_assembler.rb +437 -178
- data/lib/phronomy/agent/context_candidate_resolver.rb +2 -2
- data/lib/phronomy/agent/context_plan.rb +18 -13
- data/lib/phronomy/agent/context_plan_validator.rb +246 -88
- data/lib/phronomy/agent/context_policies/default.rb +123 -34
- data/lib/phronomy/agent/context_policy.rb +109 -3
- data/lib/phronomy/agent/context_policy_input.rb +244 -0
- data/lib/phronomy/agent/context_policy_input_builder.rb +241 -0
- 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 +2417 -572
- data/lib/phronomy/agent/handoff.rb +43 -0
- data/lib/phronomy/agent/handoff_capability_factory.rb +45 -0
- data/lib/phronomy/agent/handoff_context.rb +120 -0
- data/lib/phronomy/agent/handoff_execution_coordinator.rb +143 -0
- data/lib/phronomy/agent/handoff_policy.rb +143 -0
- data/lib/phronomy/agent/handoff_projection.rb +207 -0
- data/lib/phronomy/agent/handoff_request.rb +45 -0
- data/lib/phronomy/agent/handoff_runner.rb +178 -0
- data/lib/phronomy/agent/handoff_state.rb +43 -0
- data/lib/phronomy/agent/journal_record.rb +17 -4
- data/lib/phronomy/agent/llm_input_build_context.rb +1 -1
- data/lib/phronomy/agent/llm_input_manifest.rb +277 -2
- data/lib/phronomy/agent/llm_operation_result.rb +12 -7
- data/lib/phronomy/agent/phase_machine_builder.rb +19 -7
- data/lib/phronomy/agent/provider_call_outcome.rb +23 -7
- data/lib/phronomy/agent/recovery_coordinator/continuation.rb +174 -0
- data/lib/phronomy/agent/recovery_coordinator/installation.rb +381 -0
- data/lib/phronomy/agent/recovery_coordinator/resolution.rb +511 -0
- data/lib/phronomy/agent/recovery_coordinator.rb +217 -0
- data/lib/phronomy/agent/recovery_support.rb +504 -0
- data/lib/phronomy/agent/ruby_llm_materializer.rb +16 -13
- data/lib/phronomy/agent/selection/candidate.rb +53 -0
- data/lib/phronomy/agent/selection/constraint.rb +49 -0
- data/lib/phronomy/agent/shared_state.rb +38 -1
- data/lib/phronomy/agent/tool_approval_request.rb +33 -5
- data/lib/phronomy/agent/tool_definition_set.rb +49 -3
- data/lib/phronomy/agent/tool_invocation.rb +339 -103
- data/lib/phronomy/agent/tool_invocation_session_builder.rb +49 -45
- data/lib/phronomy/agent.rb +20 -2
- data/lib/phronomy/agent_already_exists_error.rb +5 -0
- data/lib/phronomy/agent_purged_error.rb +5 -0
- data/lib/phronomy/engine/concurrency/offload_pool.rb +17 -3
- data/lib/phronomy/engine/concurrency/physical_completion_task.rb +135 -0
- data/lib/phronomy/engine/event_loop.rb +622 -63
- data/lib/phronomy/engine/fsm_session.rb +194 -21
- data/lib/phronomy/engine/runtime/agent_ownership_registry.rb +352 -0
- data/lib/phronomy/engine/runtime/team_ownership_registry.rb +77 -0
- data/lib/phronomy/engine/runtime.rb +92 -20
- data/lib/phronomy/generator_verifier.rb +12 -14
- data/lib/phronomy/invocation_context.rb +9 -29
- data/lib/phronomy/multi_agent/admission_registry.rb +51 -0
- data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +134 -0
- data/lib/phronomy/multi_agent/fan_out_invocation.rb +24 -33
- data/lib/phronomy/multi_agent/fan_out_session_builder.rb +12 -19
- data/lib/phronomy/multi_agent/orchestrator.rb +71 -26
- 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 +706 -0
- data/lib/phronomy/persistence/durable_record.rb +117 -0
- data/lib/phronomy/persistence/in_memory.rb +471 -133
- data/lib/phronomy/persistence/migration/initial_format_migration.rb +226 -0
- data/lib/phronomy/persistence/repository_facades.rb +535 -0
- data/lib/phronomy/persistence.rb +174 -40
- data/lib/phronomy/recovery.rb +186 -0
- data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +2 -2
- data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +2 -1
- data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +19 -19
- data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +3 -3
- data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +5 -5
- 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/tracing/automatic.rb +176 -0
- data/lib/phronomy/tracing/base.rb +11 -2
- data/lib/phronomy/tracing/langfuse_tracer.rb +20 -12
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/workflow.rb +3 -6
- data/lib/phronomy/workflow_context.rb +14 -5
- data/lib/phronomy/workflow_recovery.rb +123 -0
- data/lib/phronomy/workflow_runner.rb +468 -256
- data/lib/phronomy.rb +6 -0
- data/scripts/api_snapshot.rb +12 -0
- data/sig/phronomy/agent.rbs +209 -7
- data/sig/phronomy/handoff.rbs +41 -0
- data/sig/phronomy/multi_agent.rbs +35 -0
- data/sig/phronomy/persistence.rbs +123 -4
- data/sig/phronomy/runtime.rbs +1 -4
- data/sig/phronomy/workflow.rbs +2 -2
- data/sig/phronomy.rbs +10 -0
- metadata +84 -18
- data/examples/workflows/agent_event_mapping.rb +0 -101
- data/examples/workflows/generic_task_event_mapping.rb +0 -66
- data/lib/phronomy/agent/activation_registry.rb +0 -28
- data/lib/phronomy/agent/agent_execution_activation.rb +0 -172
- data/lib/phronomy/agent/context_candidate.rb +0 -47
- data/lib/phronomy/agent/context_parts/budget/token_budget_packer.rb +0 -53
- data/lib/phronomy/agent/context_parts/requirements/required_context_resolver.rb +0 -56
- data/lib/phronomy/agent/context_parts/selectors/recent_first_selector.rb +0 -30
- data/lib/phronomy/agent/context_parts/unit_builders/dependency_aware_unit_builder.rb +0 -118
- data/lib/phronomy/agent/context_policy_descriptor.rb +0 -49
- data/lib/phronomy/agent/context_policy_registry.rb +0 -46
- data/lib/phronomy/agent/context_request.rb +0 -35
- data/lib/phronomy/agent/context_selection_unit.rb +0 -38
- data/lib/phronomy/agent/derived_content_spec.rb +0 -34
- data/lib/phronomy/agent/runner.rb +0 -97
- data/lib/phronomy/multi_agent/handoff.rb +0 -64
|
@@ -8,19 +8,19 @@ module Phronomy
|
|
|
8
8
|
LIFECYCLE_STATUSES = %i[idle active suspended closed invalidated].freeze
|
|
9
9
|
|
|
10
10
|
ATTRIBUTES = %i[
|
|
11
|
-
agent_id agent_definition_id
|
|
11
|
+
agent_id agent_definition_id agent_definition_version agent_revision
|
|
12
12
|
context_revision journal_position lifecycle_status transcript_generation
|
|
13
13
|
created_at updated_at metadata
|
|
14
14
|
].freeze
|
|
15
15
|
|
|
16
16
|
attr_reader(*ATTRIBUTES)
|
|
17
17
|
|
|
18
|
-
def self.create(agent_id:, agent_definition_id:,
|
|
18
|
+
def self.create(agent_id:, agent_definition_id:, agent_definition_version:, metadata: {})
|
|
19
19
|
now = Time.now.utc.iso8601(6)
|
|
20
20
|
new(
|
|
21
21
|
agent_id: agent_id,
|
|
22
22
|
agent_definition_id: agent_definition_id,
|
|
23
|
-
|
|
23
|
+
agent_definition_version: agent_definition_version,
|
|
24
24
|
agent_revision: 0,
|
|
25
25
|
context_revision: 0,
|
|
26
26
|
journal_position: 0,
|
|
@@ -2,17 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
module Phronomy
|
|
4
4
|
module Agent
|
|
5
|
-
#
|
|
5
|
+
# Value-oriented input passed to Tool approval policy callables.
|
|
6
6
|
#
|
|
7
7
|
# This object intentionally contains the internal, validated arguments used
|
|
8
|
-
# for policy evaluation. It
|
|
8
|
+
# for policy evaluation. It carries Agent identity metadata and Tool
|
|
9
|
+
# description data only; it never exposes live Agent or Tool objects.
|
|
10
|
+
#
|
|
11
|
+
# Hash, Array, and String values are recursively copied/frozen. Other
|
|
12
|
+
# application-owned opaque values remain application-owned; complete
|
|
13
|
+
# value-type enforcement for those objects is outside ACS-11.
|
|
14
|
+
#
|
|
15
|
+
# This object must not be exposed directly to Application UI.
|
|
9
16
|
# Use {ToolApprovalRequest} for notifications.
|
|
10
17
|
#
|
|
11
18
|
# @api public
|
|
12
19
|
class ApprovalEvaluationRequest
|
|
13
|
-
attr_reader :
|
|
14
|
-
:
|
|
15
|
-
:
|
|
20
|
+
attr_reader :agent_id,
|
|
21
|
+
:agent_definition_id,
|
|
22
|
+
:agent_definition_version,
|
|
23
|
+
:execution_id,
|
|
16
24
|
:tool_name,
|
|
17
25
|
:tool_schema,
|
|
18
26
|
:tool_invocation_id,
|
|
@@ -27,9 +35,10 @@ module Phronomy
|
|
|
27
35
|
VALID_DECISIONS = %i[allow require_approval reject].freeze
|
|
28
36
|
|
|
29
37
|
def initialize(
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
38
|
+
agent_id:,
|
|
39
|
+
agent_definition_id:,
|
|
40
|
+
agent_definition_version:,
|
|
41
|
+
execution_id:,
|
|
33
42
|
tool_name:,
|
|
34
43
|
tool_schema:,
|
|
35
44
|
tool_invocation_id:,
|
|
@@ -41,9 +50,21 @@ module Phronomy
|
|
|
41
50
|
metadata: {},
|
|
42
51
|
default_decision: nil
|
|
43
52
|
)
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
53
|
+
if agent_id.nil? || agent_id.to_s.empty?
|
|
54
|
+
raise ArgumentError, "ApprovalEvaluationRequest requires agent_id"
|
|
55
|
+
end
|
|
56
|
+
if agent_definition_id.nil? || agent_definition_id.to_s.empty?
|
|
57
|
+
raise ArgumentError,
|
|
58
|
+
"ApprovalEvaluationRequest requires agent_definition_id"
|
|
59
|
+
end
|
|
60
|
+
if execution_id.nil? || execution_id.to_s.empty?
|
|
61
|
+
raise ArgumentError, "ApprovalEvaluationRequest requires execution_id"
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
@agent_id = agent_id.to_s.freeze
|
|
65
|
+
@agent_definition_id = agent_definition_id.to_s.freeze
|
|
66
|
+
@agent_definition_version = Integer(agent_definition_version)
|
|
67
|
+
@execution_id = execution_id.to_s.freeze
|
|
47
68
|
@tool_name = tool_name.to_s.freeze
|
|
48
69
|
@tool_schema = immutable_copy(tool_schema)
|
|
49
70
|
@tool_invocation_id = tool_invocation_id.to_s.freeze
|
|
@@ -61,9 +82,10 @@ module Phronomy
|
|
|
61
82
|
# @api private
|
|
62
83
|
def with(facts: @facts, default_decision: @default_decision)
|
|
63
84
|
self.class.new(
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
85
|
+
agent_id: @agent_id,
|
|
86
|
+
agent_definition_id: @agent_definition_id,
|
|
87
|
+
agent_definition_version: @agent_definition_version,
|
|
88
|
+
execution_id: @execution_id,
|
|
67
89
|
tool_name: @tool_name,
|
|
68
90
|
tool_schema: @tool_schema,
|
|
69
91
|
tool_invocation_id: @tool_invocation_id,
|
|
@@ -90,11 +112,7 @@ module Phronomy
|
|
|
90
112
|
when String
|
|
91
113
|
value.dup.freeze
|
|
92
114
|
else
|
|
93
|
-
|
|
94
|
-
value.frozen? ? value : value.dup.freeze
|
|
95
|
-
rescue TypeError
|
|
96
|
-
value
|
|
97
|
-
end
|
|
115
|
+
value
|
|
98
116
|
end
|
|
99
117
|
end
|
|
100
118
|
end
|
|
@@ -2,108 +2,85 @@
|
|
|
2
2
|
|
|
3
3
|
module Phronomy
|
|
4
4
|
module Agent
|
|
5
|
-
# Public Agent execution
|
|
6
|
-
#
|
|
5
|
+
# Public Agent execution facade.
|
|
6
|
+
#
|
|
7
|
+
# CG-09 clean break:
|
|
8
|
+
# - application event listeners are bound to the live Agent incarnation at
|
|
9
|
+
# new/create/load time;
|
|
10
|
+
# - invoke/invoke_async/stream/stream_async do not accept per-call event
|
|
11
|
+
# listeners or listener blocks;
|
|
12
|
+
# - approval notifications share the Agent listener as :approval_required;
|
|
13
|
+
# - Recovery resolution uses resolve/resolve_async.
|
|
7
14
|
module AsyncEventApi
|
|
8
15
|
def invoke(
|
|
9
16
|
input,
|
|
10
|
-
thread_id: nil,
|
|
11
17
|
config: {},
|
|
12
18
|
invocation_context: nil,
|
|
13
|
-
|
|
14
|
-
&
|
|
19
|
+
**removed_options,
|
|
20
|
+
&removed_block
|
|
15
21
|
)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
thread_id, config = _apply_invocation_context(thread_id, config, invocation_context)
|
|
19
|
-
end
|
|
22
|
+
_reject_removed_invocation_listener_arguments!(removed_options, removed_block)
|
|
23
|
+
config = _prepare_invocation_config(config, invocation_context)
|
|
20
24
|
_check_event_loop_reentrancy(:invoke, :invoke_async)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
).wait_result
|
|
28
|
-
[result, result[:usage]]
|
|
29
|
-
end
|
|
25
|
+
_start_agent_operation(
|
|
26
|
+
input,
|
|
27
|
+
config: config,
|
|
28
|
+
mode: :invoke,
|
|
29
|
+
listener: _phronomy_event_listener
|
|
30
|
+
).wait_result
|
|
30
31
|
end
|
|
31
32
|
|
|
32
33
|
def invoke_async(
|
|
33
34
|
input,
|
|
34
|
-
thread_id: nil,
|
|
35
35
|
config: {},
|
|
36
36
|
invocation_context: nil,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
&block
|
|
37
|
+
**removed_options,
|
|
38
|
+
&removed_block
|
|
40
39
|
)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
end
|
|
45
|
-
approval = _approval_configuration_snapshot(on_tool_approval_required)
|
|
46
|
-
execution_coordinator.start(
|
|
40
|
+
_reject_removed_invocation_listener_arguments!(removed_options, removed_block)
|
|
41
|
+
config = _prepare_invocation_config(config, invocation_context)
|
|
42
|
+
_start_agent_operation(
|
|
47
43
|
input,
|
|
48
|
-
thread_id: thread_id,
|
|
49
44
|
config: config,
|
|
50
45
|
mode: :invoke,
|
|
51
|
-
|
|
52
|
-
approval_listener: approval[:listener],
|
|
53
|
-
on_event: listener
|
|
46
|
+
listener: _phronomy_event_listener
|
|
54
47
|
)
|
|
55
48
|
end
|
|
56
49
|
|
|
57
50
|
def stream(
|
|
58
51
|
input,
|
|
59
|
-
thread_id: nil,
|
|
60
52
|
config: {},
|
|
61
53
|
invocation_context: nil,
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
&block
|
|
54
|
+
**removed_options,
|
|
55
|
+
&removed_block
|
|
65
56
|
)
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
thread_id, config = _apply_invocation_context(thread_id, config, invocation_context)
|
|
70
|
-
end
|
|
57
|
+
_reject_removed_invocation_listener_arguments!(removed_options, removed_block)
|
|
58
|
+
listener = _required_stream_listener!(:stream)
|
|
59
|
+
config = _prepare_invocation_config(config, invocation_context)
|
|
71
60
|
_check_event_loop_reentrancy(:stream, :stream_async)
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
on_event: listener
|
|
79
|
-
).wait_result
|
|
80
|
-
[result, result[:usage]]
|
|
81
|
-
end
|
|
61
|
+
_start_agent_operation(
|
|
62
|
+
input,
|
|
63
|
+
config: config,
|
|
64
|
+
mode: :stream,
|
|
65
|
+
listener: listener
|
|
66
|
+
).wait_result
|
|
82
67
|
end
|
|
83
68
|
|
|
84
69
|
def stream_async(
|
|
85
70
|
input,
|
|
86
|
-
thread_id: nil,
|
|
87
71
|
config: {},
|
|
88
72
|
invocation_context: nil,
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
&block
|
|
73
|
+
**removed_options,
|
|
74
|
+
&removed_block
|
|
92
75
|
)
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
end
|
|
98
|
-
approval = _approval_configuration_snapshot(on_tool_approval_required)
|
|
99
|
-
execution_coordinator.start(
|
|
76
|
+
_reject_removed_invocation_listener_arguments!(removed_options, removed_block)
|
|
77
|
+
listener = _required_stream_listener!(:stream_async)
|
|
78
|
+
config = _prepare_invocation_config(config, invocation_context)
|
|
79
|
+
_start_agent_operation(
|
|
100
80
|
input,
|
|
101
|
-
thread_id: thread_id,
|
|
102
81
|
config: config,
|
|
103
82
|
mode: :stream,
|
|
104
|
-
|
|
105
|
-
approval_listener: approval[:listener],
|
|
106
|
-
on_event: listener
|
|
83
|
+
listener: listener
|
|
107
84
|
)
|
|
108
85
|
end
|
|
109
86
|
|
|
@@ -118,7 +95,14 @@ module Phronomy
|
|
|
118
95
|
end
|
|
119
96
|
|
|
120
97
|
def approve_async(execution_id, approval_request_id:, approved: true, config: {})
|
|
121
|
-
|
|
98
|
+
_reject_removed_generic_identity_keys!(config)
|
|
99
|
+
owner = Phronomy::Runtime.instance.__agent_execution_owner(execution_id)
|
|
100
|
+
coordinator = if owner&.agent&.equal?(self)
|
|
101
|
+
owner.coordinator
|
|
102
|
+
else
|
|
103
|
+
execution_coordinator_for(config)
|
|
104
|
+
end
|
|
105
|
+
coordinator.resume(
|
|
122
106
|
execution_id,
|
|
123
107
|
approval_request_id: approval_request_id,
|
|
124
108
|
approved: approved,
|
|
@@ -126,17 +110,123 @@ module Phronomy
|
|
|
126
110
|
)
|
|
127
111
|
end
|
|
128
112
|
|
|
113
|
+
def resolve(
|
|
114
|
+
execution_id,
|
|
115
|
+
expected_execution_revision:,
|
|
116
|
+
subject:,
|
|
117
|
+
outcome:,
|
|
118
|
+
result: Phronomy::Recovery::MISSING,
|
|
119
|
+
error: Phronomy::Recovery::MISSING
|
|
120
|
+
)
|
|
121
|
+
_check_event_loop_reentrancy(:resolve, :resolve_async)
|
|
122
|
+
resolve_async(
|
|
123
|
+
execution_id,
|
|
124
|
+
expected_execution_revision: expected_execution_revision,
|
|
125
|
+
subject: subject,
|
|
126
|
+
outcome: outcome,
|
|
127
|
+
result: result,
|
|
128
|
+
error: error
|
|
129
|
+
).wait_result
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def resolve_async(
|
|
133
|
+
execution_id,
|
|
134
|
+
expected_execution_revision:,
|
|
135
|
+
subject:,
|
|
136
|
+
outcome:,
|
|
137
|
+
result: Phronomy::Recovery::MISSING,
|
|
138
|
+
error: Phronomy::Recovery::MISSING
|
|
139
|
+
)
|
|
140
|
+
Phronomy::Agent::RecoveryCoordinator.new(self).resolve(
|
|
141
|
+
execution_id,
|
|
142
|
+
expected_execution_revision: expected_execution_revision,
|
|
143
|
+
subject: subject,
|
|
144
|
+
outcome: outcome,
|
|
145
|
+
result: result,
|
|
146
|
+
error: error
|
|
147
|
+
)
|
|
148
|
+
end
|
|
149
|
+
|
|
129
150
|
private
|
|
130
151
|
|
|
152
|
+
# Framework-private execution-local event routing. This is intentionally
|
|
153
|
+
# not a public compatibility path for invoke_async(..., on_event:).
|
|
154
|
+
def __invoke_async_with_event_sink(
|
|
155
|
+
input,
|
|
156
|
+
on_event:, config: {},
|
|
157
|
+
invocation_context: nil
|
|
158
|
+
)
|
|
159
|
+
raise ArgumentError, "on_event is required" unless on_event
|
|
160
|
+
|
|
161
|
+
config = _prepare_invocation_config(config, invocation_context)
|
|
162
|
+
_start_agent_operation(
|
|
163
|
+
input,
|
|
164
|
+
config: config,
|
|
165
|
+
mode: :invoke,
|
|
166
|
+
listener: on_event
|
|
167
|
+
)
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
def _start_agent_operation(input, config:, mode:, listener:)
|
|
171
|
+
config = _snapshot_durable_context(config)
|
|
172
|
+
approval = _approval_configuration_snapshot(nil)
|
|
173
|
+
execution_coordinator_for(config).start(
|
|
174
|
+
input,
|
|
175
|
+
config: config.merge(phronomy_recovery_mode: mode.to_sym),
|
|
176
|
+
mode: mode,
|
|
177
|
+
approval_policy: approval[:policy],
|
|
178
|
+
approval_listener: nil,
|
|
179
|
+
on_event: listener
|
|
180
|
+
)
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def _snapshot_durable_context(config)
|
|
184
|
+
return config unless config.key?(:durable_context)
|
|
185
|
+
|
|
186
|
+
value = config[:durable_context]
|
|
187
|
+
unless value.is_a?(Hash)
|
|
188
|
+
raise ArgumentError,
|
|
189
|
+
"config[:durable_context] must be a canonical JSON-compatible Hash"
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
bytes = Phronomy::CanonicalJSON.dump(value)
|
|
193
|
+
snapshot = Phronomy::Agent::Immutable.copy(
|
|
194
|
+
Phronomy::CanonicalJSON.load(bytes)
|
|
195
|
+
)
|
|
196
|
+
config.merge(durable_context: snapshot)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def _required_stream_listener!(method_name)
|
|
200
|
+
listener = _phronomy_event_listener
|
|
201
|
+
return listener if listener
|
|
202
|
+
|
|
203
|
+
raise ArgumentError,
|
|
204
|
+
"#{method_name} requires an Agent on_event listener registered at new/create/load"
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
def _reject_removed_invocation_listener_arguments!(options, block)
|
|
208
|
+
if block
|
|
209
|
+
raise ArgumentError,
|
|
210
|
+
"invoke/stream blocks no longer register Agent events; register the listener at new/create/load"
|
|
211
|
+
end
|
|
212
|
+
return if options.empty?
|
|
213
|
+
|
|
214
|
+
removed = options.keys.map(&:inspect).join(", ")
|
|
215
|
+
raise ArgumentError,
|
|
216
|
+
"removed per-invocation Agent option(s): #{removed}; register on_event at new/create/load"
|
|
217
|
+
end
|
|
218
|
+
|
|
131
219
|
def execution_coordinator
|
|
132
220
|
@execution_coordinator ||= Agent::ExecutionCoordinator.new(self)
|
|
133
221
|
end
|
|
134
222
|
|
|
135
|
-
def
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
223
|
+
def execution_coordinator_for(config)
|
|
224
|
+
multi_agent = config.key?(:phronomy_handoff_bindings) ||
|
|
225
|
+
config.key?(:phronomy_handoff_context)
|
|
226
|
+
return execution_coordinator unless multi_agent
|
|
227
|
+
|
|
228
|
+
@multi_agent_execution_coordinator ||=
|
|
229
|
+
Phronomy::Agent::HandoffExecutionCoordinator.new(self)
|
|
140
230
|
end
|
|
141
231
|
end
|
|
142
232
|
end
|