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
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.0
|
|
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
|
|
@@ -95,6 +95,7 @@ files:
|
|
|
95
95
|
- RELEASE_CHECKLIST.md
|
|
96
96
|
- Rakefile
|
|
97
97
|
- SECURITY.md
|
|
98
|
+
- VERIFY.sh
|
|
98
99
|
- benchmark/baseline.json
|
|
99
100
|
- benchmark/bench_agent_invoke.rb
|
|
100
101
|
- benchmark/bench_context_assembler.rb
|
|
@@ -104,6 +105,22 @@ files:
|
|
|
104
105
|
- benchmark/bench_vector_store.rb
|
|
105
106
|
- benchmark/bench_workflow.rb
|
|
106
107
|
- benchmark/run_all.rb
|
|
108
|
+
- docs/architecture.md
|
|
109
|
+
- docs/architecture/agent-context.md
|
|
110
|
+
- docs/architecture/before-llm-input.md
|
|
111
|
+
- docs/architecture/context-management.md
|
|
112
|
+
- docs/architecture/knowledge-and-rag.md
|
|
113
|
+
- docs/architecture/multi-agent-handoff.md
|
|
114
|
+
- docs/architecture/persistence.md
|
|
115
|
+
- docs/architecture/removed/agent-context.md
|
|
116
|
+
- docs/architecture/security-boundaries.md
|
|
117
|
+
- docs/architecture/tracing.md
|
|
118
|
+
- docs/archive/design/archived/04_api_design.md
|
|
119
|
+
- docs/archive/design/archived/09_guardrails.md
|
|
120
|
+
- docs/archive/design/archived/17_rails_integration.md
|
|
121
|
+
- docs/archive/design/historical/00_design_philosophy.md
|
|
122
|
+
- docs/archive/design/historical/01_rubyllm_evaluation.md
|
|
123
|
+
- docs/archive/design/historical/06_design_decisions.md
|
|
107
124
|
- docs/changelog/0.14-and-earlier.md
|
|
108
125
|
- docs/decisions/001-rubyllm-as-provider-layer.md
|
|
109
126
|
- docs/decisions/002-workflow-context-immutability.md
|
|
@@ -121,22 +138,44 @@ files:
|
|
|
121
138
|
- docs/decisions/013-journal-backed-knowledge-as-context-candidates.md
|
|
122
139
|
- docs/decisions/014-unified-persistence-durable-state.md
|
|
123
140
|
- docs/decisions/015-tool-public-facade-and-rbs-boundary.md
|
|
141
|
+
- docs/decisions/016-semantic-multi-agent-handoff.md
|
|
142
|
+
- docs/decisions/017-design-authority-and-adr-governance.md
|
|
143
|
+
- docs/decisions/018-durability-guarantees-and-failure-model.md
|
|
144
|
+
- docs/decisions/019-filter-contract-and-security-boundaries.md
|
|
145
|
+
- docs/decisions/020-canonical-workflow-instance-identity.md
|
|
146
|
+
- docs/decisions/021-generic-agent-invocation-identity-removal.md
|
|
147
|
+
- docs/decisions/022-agent-execution-parent-identity-and-runtime-routing-boundary.md
|
|
148
|
+
- docs/decisions/023-fsm-session-incarnation-identity-and-routing.md
|
|
149
|
+
- docs/decisions/024-event-loop-single-writer-agent-runtime.md
|
|
150
|
+
- docs/decisions/025-process-local-agent-ownership-and-runtime-admission.md
|
|
151
|
+
- docs/decisions/026-workflow-runtime-admission-and-durable-terminal-barrier.md
|
|
152
|
+
- docs/decisions/027-llm-adapter-provider-boundary.md
|
|
153
|
+
- docs/decisions/028-preparing-recovery-replay-contract.md
|
|
154
|
+
- docs/decisions/029-semantic-completion-and-application-effect-boundary.md
|
|
155
|
+
- docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md
|
|
156
|
+
- docs/decisions/031-durable-multi-agent-coordination.md
|
|
157
|
+
- docs/decisions/README.md
|
|
158
|
+
- docs/design/durable-semantic-coordination/CHANGELOG_V2_REVISION_2.md
|
|
159
|
+
- docs/design/durable-semantic-coordination/CONTINUATION_DECISION_REFACTOR.md
|
|
160
|
+
- docs/design/durable-semantic-coordination/IMPLEMENTATION_DESIGN_V2.md
|
|
161
|
+
- docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md
|
|
162
|
+
- docs/design/durable-semantic-coordination/RECOVERY_CONTRACT_CLARIFICATIONS.md
|
|
163
|
+
- docs/design/durable-semantic-coordination/RESPONSIBILITY_BOUNDARY_REVIEW.md
|
|
124
164
|
- docs/features.md
|
|
125
165
|
- docs/getting-started.md
|
|
126
166
|
- docs/mcp-client.md
|
|
127
167
|
- docs/migrations/0.15.md
|
|
128
168
|
- docs/migrations/0.16.md
|
|
129
169
|
- docs/migrations/0.19.md
|
|
170
|
+
- docs/migrations/0.22.md
|
|
171
|
+
- docs/migrations/durable-semantic-coordination-v2.md
|
|
130
172
|
- docs/persistence-backends.md
|
|
131
173
|
- docs/runtime-and-concurrency.md
|
|
132
|
-
- examples/
|
|
133
|
-
- examples/workflows/generic_task_event_mapping.rb
|
|
174
|
+
- examples/README.md
|
|
134
175
|
- gemfiles/mcp_1_0.gemfile
|
|
135
176
|
- lib/phronomy.rb
|
|
136
177
|
- lib/phronomy/agent.rb
|
|
137
|
-
- lib/phronomy/agent/activation_registry.rb
|
|
138
178
|
- lib/phronomy/agent/agent_execution.rb
|
|
139
|
-
- lib/phronomy/agent/agent_execution_activation.rb
|
|
140
179
|
- lib/phronomy/agent/agent_invocation.rb
|
|
141
180
|
- lib/phronomy/agent/agent_invocation_session_builder.rb
|
|
142
181
|
- lib/phronomy/agent/agent_root.rb
|
|
@@ -149,24 +188,27 @@ files:
|
|
|
149
188
|
- lib/phronomy/agent/context/capability/base.rb
|
|
150
189
|
- lib/phronomy/agent/context/instruction/prompt_template.rb
|
|
151
190
|
- lib/phronomy/agent/context_assembler.rb
|
|
152
|
-
- lib/phronomy/agent/context_candidate.rb
|
|
153
191
|
- lib/phronomy/agent/context_candidate_resolver.rb
|
|
154
192
|
- lib/phronomy/agent/context_importer.rb
|
|
155
|
-
- lib/phronomy/agent/context_parts/budget/token_budget_packer.rb
|
|
156
|
-
- lib/phronomy/agent/context_parts/requirements/required_context_resolver.rb
|
|
157
|
-
- lib/phronomy/agent/context_parts/selectors/recent_first_selector.rb
|
|
158
|
-
- lib/phronomy/agent/context_parts/unit_builders/dependency_aware_unit_builder.rb
|
|
159
193
|
- lib/phronomy/agent/context_parts/validators/final_budget_validator.rb
|
|
160
194
|
- lib/phronomy/agent/context_plan.rb
|
|
161
195
|
- lib/phronomy/agent/context_plan_validator.rb
|
|
162
196
|
- lib/phronomy/agent/context_policies/default.rb
|
|
163
197
|
- lib/phronomy/agent/context_policy.rb
|
|
164
|
-
- lib/phronomy/agent/
|
|
165
|
-
- lib/phronomy/agent/
|
|
166
|
-
- lib/phronomy/agent/
|
|
167
|
-
- lib/phronomy/agent/
|
|
168
|
-
- lib/phronomy/agent/derived_content_spec.rb
|
|
198
|
+
- lib/phronomy/agent/context_policy_input.rb
|
|
199
|
+
- lib/phronomy/agent/context_policy_input_builder.rb
|
|
200
|
+
- lib/phronomy/agent/exact_execution.rb
|
|
201
|
+
- lib/phronomy/agent/execution_cancellation.rb
|
|
169
202
|
- lib/phronomy/agent/execution_coordinator.rb
|
|
203
|
+
- lib/phronomy/agent/handoff.rb
|
|
204
|
+
- lib/phronomy/agent/handoff_capability_factory.rb
|
|
205
|
+
- lib/phronomy/agent/handoff_context.rb
|
|
206
|
+
- lib/phronomy/agent/handoff_execution_coordinator.rb
|
|
207
|
+
- lib/phronomy/agent/handoff_policy.rb
|
|
208
|
+
- lib/phronomy/agent/handoff_projection.rb
|
|
209
|
+
- lib/phronomy/agent/handoff_request.rb
|
|
210
|
+
- lib/phronomy/agent/handoff_runner.rb
|
|
211
|
+
- lib/phronomy/agent/handoff_state.rb
|
|
170
212
|
- lib/phronomy/agent/immutable.rb
|
|
171
213
|
- lib/phronomy/agent/journal_projection.rb
|
|
172
214
|
- lib/phronomy/agent/journal_record.rb
|
|
@@ -177,8 +219,14 @@ files:
|
|
|
177
219
|
- lib/phronomy/agent/llm_operation_result.rb
|
|
178
220
|
- lib/phronomy/agent/phase_machine_builder.rb
|
|
179
221
|
- lib/phronomy/agent/provider_call_outcome.rb
|
|
222
|
+
- lib/phronomy/agent/recovery_coordinator.rb
|
|
223
|
+
- lib/phronomy/agent/recovery_coordinator/continuation.rb
|
|
224
|
+
- lib/phronomy/agent/recovery_coordinator/installation.rb
|
|
225
|
+
- lib/phronomy/agent/recovery_coordinator/resolution.rb
|
|
226
|
+
- lib/phronomy/agent/recovery_support.rb
|
|
180
227
|
- lib/phronomy/agent/ruby_llm_materializer.rb
|
|
181
|
-
- lib/phronomy/agent/
|
|
228
|
+
- lib/phronomy/agent/selection/candidate.rb
|
|
229
|
+
- lib/phronomy/agent/selection/constraint.rb
|
|
182
230
|
- lib/phronomy/agent/shared_state.rb
|
|
183
231
|
- lib/phronomy/agent/token_budget_resolver.rb
|
|
184
232
|
- lib/phronomy/agent/tool_approval_request.rb
|
|
@@ -187,7 +235,9 @@ files:
|
|
|
187
235
|
- lib/phronomy/agent/tool_executor.rb
|
|
188
236
|
- lib/phronomy/agent/tool_invocation.rb
|
|
189
237
|
- lib/phronomy/agent/tool_invocation_session_builder.rb
|
|
238
|
+
- lib/phronomy/agent_already_exists_error.rb
|
|
190
239
|
- lib/phronomy/agent_busy_error.rb
|
|
240
|
+
- lib/phronomy/agent_purged_error.rb
|
|
191
241
|
- lib/phronomy/canonical_json.rb
|
|
192
242
|
- lib/phronomy/configuration.rb
|
|
193
243
|
- lib/phronomy/content_store/base.rb
|
|
@@ -198,11 +248,14 @@ files:
|
|
|
198
248
|
- lib/phronomy/engine/concurrency/cancellation_token.rb
|
|
199
249
|
- lib/phronomy/engine/concurrency/deadline.rb
|
|
200
250
|
- lib/phronomy/engine/concurrency/offload_pool.rb
|
|
251
|
+
- lib/phronomy/engine/concurrency/physical_completion_task.rb
|
|
201
252
|
- lib/phronomy/engine/concurrency/pool_registry.rb
|
|
202
253
|
- lib/phronomy/engine/event_loop.rb
|
|
203
254
|
- lib/phronomy/engine/fsm_session.rb
|
|
204
255
|
- lib/phronomy/engine/runtime.rb
|
|
256
|
+
- lib/phronomy/engine/runtime/agent_ownership_registry.rb
|
|
205
257
|
- lib/phronomy/engine/runtime/shutdown_result.rb
|
|
258
|
+
- lib/phronomy/engine/runtime/team_ownership_registry.rb
|
|
206
259
|
- lib/phronomy/engine/runtime/timer_queue.rb
|
|
207
260
|
- lib/phronomy/engine/runtime/timer_service.rb
|
|
208
261
|
- lib/phronomy/engine/task.rb
|
|
@@ -223,18 +276,26 @@ files:
|
|
|
223
276
|
- lib/phronomy/llm_context_window/token_budget.rb
|
|
224
277
|
- lib/phronomy/llm_context_window/token_estimator.rb
|
|
225
278
|
- lib/phronomy/metrics.rb
|
|
279
|
+
- lib/phronomy/multi_agent/admission_registry.rb
|
|
280
|
+
- lib/phronomy/multi_agent/durable_subagent_coordinator.rb
|
|
226
281
|
- lib/phronomy/multi_agent/fan_out_invocation.rb
|
|
227
282
|
- lib/phronomy/multi_agent/fan_out_session_builder.rb
|
|
228
|
-
- lib/phronomy/multi_agent/handoff.rb
|
|
229
283
|
- lib/phronomy/multi_agent/orchestrator.rb
|
|
230
284
|
- lib/phronomy/multi_agent/parallel_tool_chat.rb
|
|
231
285
|
- lib/phronomy/multi_agent/team_coordinator.rb
|
|
286
|
+
- lib/phronomy/multi_agent/team_execution.rb
|
|
287
|
+
- lib/phronomy/multi_agent/team_root.rb
|
|
232
288
|
- lib/phronomy/output_parser.rb
|
|
233
289
|
- lib/phronomy/output_parser/base.rb
|
|
234
290
|
- lib/phronomy/output_parser/json_parser.rb
|
|
235
291
|
- lib/phronomy/output_parser/structured_parser.rb
|
|
236
292
|
- lib/phronomy/persistence.rb
|
|
293
|
+
- lib/phronomy/persistence/durable_codec.rb
|
|
294
|
+
- lib/phronomy/persistence/durable_record.rb
|
|
237
295
|
- lib/phronomy/persistence/in_memory.rb
|
|
296
|
+
- lib/phronomy/persistence/migration/initial_format_migration.rb
|
|
297
|
+
- lib/phronomy/persistence/repository_facades.rb
|
|
298
|
+
- lib/phronomy/recovery.rb
|
|
238
299
|
- lib/phronomy/ruby_llm_patches.rb
|
|
239
300
|
- lib/phronomy/runnable.rb
|
|
240
301
|
- lib/phronomy/stream_callback_error.rb
|
|
@@ -259,6 +320,7 @@ files:
|
|
|
259
320
|
- lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb
|
|
260
321
|
- lib/phronomy/testing/persistence_contract/an_agent_repository.rb
|
|
261
322
|
- lib/phronomy/testing/persistence_contract/an_execution_repository.rb
|
|
323
|
+
- lib/phronomy/testing/persistence_contract/coordination_repositories.rb
|
|
262
324
|
- lib/phronomy/token_usage.rb
|
|
263
325
|
- lib/phronomy/tool.rb
|
|
264
326
|
- lib/phronomy/tool/base.rb
|
|
@@ -266,6 +328,7 @@ files:
|
|
|
266
328
|
- lib/phronomy/tools/mcp.rb
|
|
267
329
|
- lib/phronomy/tools/vector_search.rb
|
|
268
330
|
- lib/phronomy/tracing.rb
|
|
331
|
+
- lib/phronomy/tracing/automatic.rb
|
|
269
332
|
- lib/phronomy/tracing/base.rb
|
|
270
333
|
- lib/phronomy/tracing/langfuse_tracer.rb
|
|
271
334
|
- lib/phronomy/tracing/null_tracer.rb
|
|
@@ -289,6 +352,7 @@ files:
|
|
|
289
352
|
- lib/phronomy/workflow.rb
|
|
290
353
|
- lib/phronomy/workflow/phase_machine_builder.rb
|
|
291
354
|
- lib/phronomy/workflow_context.rb
|
|
355
|
+
- lib/phronomy/workflow_recovery.rb
|
|
292
356
|
- lib/phronomy/workflow_runner.rb
|
|
293
357
|
- scripts/add_to_h_to_token_doubles.rb
|
|
294
358
|
- scripts/add_to_h_unnamed_doubles.rb
|
|
@@ -303,7 +367,9 @@ files:
|
|
|
303
367
|
- sig/phronomy.rbs
|
|
304
368
|
- sig/phronomy/agent.rbs
|
|
305
369
|
- sig/phronomy/extensions.rbs
|
|
370
|
+
- sig/phronomy/handoff.rbs
|
|
306
371
|
- sig/phronomy/llm_adapter.rbs
|
|
372
|
+
- sig/phronomy/multi_agent.rbs
|
|
307
373
|
- sig/phronomy/persistence.rbs
|
|
308
374
|
- sig/phronomy/runtime.rbs
|
|
309
375
|
- sig/phronomy/tool.rbs
|
|
@@ -1,101 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "phronomy"
|
|
4
|
-
require "securerandom"
|
|
5
|
-
|
|
6
|
-
class GenerationContext
|
|
7
|
-
include Phronomy::WorkflowContext
|
|
8
|
-
|
|
9
|
-
field :prompt
|
|
10
|
-
field :generation_request_id
|
|
11
|
-
field :answer
|
|
12
|
-
field :error_message
|
|
13
|
-
|
|
14
|
-
# This is application logic. Phronomy transports the event and payload but
|
|
15
|
-
# does not decide which Agent invocation is current or where the result lives.
|
|
16
|
-
def handle_fsm_event(event)
|
|
17
|
-
request_id = event.payload[:generation_request_id]
|
|
18
|
-
return :consume unless request_id == generation_request_id
|
|
19
|
-
|
|
20
|
-
case event.type
|
|
21
|
-
when :generation_completed
|
|
22
|
-
self.answer = event.payload[:agent_result][:output]
|
|
23
|
-
when :generation_failed
|
|
24
|
-
self.error_message = event.payload[:error].message
|
|
25
|
-
end
|
|
26
|
-
false
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
class AnswerAgent < Phronomy::Agent::Base
|
|
31
|
-
model "gpt-4o-mini"
|
|
32
|
-
instructions "Answer clearly and briefly."
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
agent = AnswerAgent.new
|
|
36
|
-
workflow = nil
|
|
37
|
-
|
|
38
|
-
workflow = Phronomy::Workflow.define(GenerationContext) do
|
|
39
|
-
initial :generating
|
|
40
|
-
|
|
41
|
-
state :generating, action: ->(context) {
|
|
42
|
-
request_id = context.generation_request_id
|
|
43
|
-
|
|
44
|
-
# The Agent Task is intentionally not returned from the entry action.
|
|
45
|
-
# The block is the application-level integration channel.
|
|
46
|
-
agent.invoke_async(context.prompt) do |agent_event|
|
|
47
|
-
workflow_event =
|
|
48
|
-
case agent_event.type
|
|
49
|
-
when :done
|
|
50
|
-
:generation_completed
|
|
51
|
-
when :error, :timeout, :cancelled, :approval_required
|
|
52
|
-
:generation_failed
|
|
53
|
-
end
|
|
54
|
-
next unless workflow_event
|
|
55
|
-
|
|
56
|
-
workflow.signal(
|
|
57
|
-
thread_id: context.thread_id,
|
|
58
|
-
event: workflow_event,
|
|
59
|
-
payload: {
|
|
60
|
-
generation_request_id: request_id,
|
|
61
|
-
agent_result: (
|
|
62
|
-
agent_event.payload if agent_event.type == :done
|
|
63
|
-
),
|
|
64
|
-
error:
|
|
65
|
-
agent_event.payload[:error] ||
|
|
66
|
-
Phronomy::Error.new(
|
|
67
|
-
"Agent requested Tool approval"
|
|
68
|
-
)
|
|
69
|
-
}
|
|
70
|
-
)
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
context
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
state :succeeded
|
|
77
|
-
state :failed
|
|
78
|
-
|
|
79
|
-
transition(
|
|
80
|
-
from: :generating,
|
|
81
|
-
on: :generation_completed,
|
|
82
|
-
to: :succeeded
|
|
83
|
-
)
|
|
84
|
-
transition(
|
|
85
|
-
from: :generating,
|
|
86
|
-
on: :generation_failed,
|
|
87
|
-
to: :failed
|
|
88
|
-
)
|
|
89
|
-
transition from: :succeeded, to: :__finish__
|
|
90
|
-
transition from: :failed, to: :__finish__
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
result = workflow.invoke(
|
|
94
|
-
{
|
|
95
|
-
prompt: "What is Run-to-Completion?",
|
|
96
|
-
generation_request_id: SecureRandom.uuid
|
|
97
|
-
},
|
|
98
|
-
config: {thread_id: SecureRandom.uuid}
|
|
99
|
-
)
|
|
100
|
-
|
|
101
|
-
puts(result.answer || result.error_message)
|
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "phronomy"
|
|
4
|
-
|
|
5
|
-
class ImportContext
|
|
6
|
-
include Phronomy::WorkflowContext
|
|
7
|
-
|
|
8
|
-
field :record_count, default: 0
|
|
9
|
-
field :error_message
|
|
10
|
-
|
|
11
|
-
def handle_fsm_event(event)
|
|
12
|
-
case event.type
|
|
13
|
-
when :import_completed
|
|
14
|
-
self.record_count = event.payload[:record_count]
|
|
15
|
-
when :import_failed
|
|
16
|
-
self.error_message = event.payload[:error].message
|
|
17
|
-
end
|
|
18
|
-
false
|
|
19
|
-
end
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
# Example application service for synchronous work that must stay off EventLoop.
|
|
23
|
-
# The worker Thread belongs to Phronomy's bounded OffloadPool; the Workflow
|
|
24
|
-
# itself never blocks EventLoop waiting for the operation.
|
|
25
|
-
def start_import_async
|
|
26
|
-
Phronomy::Runtime.instance.offload.submit(on_full: :raise) do
|
|
27
|
-
# Replace with blocking file/DB/network work or another long synchronous call.
|
|
28
|
-
100
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
workflow = nil
|
|
33
|
-
|
|
34
|
-
workflow = Phronomy::Workflow.define(ImportContext) do
|
|
35
|
-
initial :importing
|
|
36
|
-
|
|
37
|
-
state :importing, action: ->(context) {
|
|
38
|
-
operation = start_import_async
|
|
39
|
-
|
|
40
|
-
operation.on_complete do |record_count, error|
|
|
41
|
-
workflow.signal(
|
|
42
|
-
thread_id: context.thread_id,
|
|
43
|
-
event: error ? :import_failed : :import_completed,
|
|
44
|
-
payload: {
|
|
45
|
-
record_count: record_count,
|
|
46
|
-
error: error
|
|
47
|
-
}
|
|
48
|
-
)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
# Do not return the completion handle. The state remains active after this
|
|
52
|
-
# synchronous action returns, and later completion arrives as an FSM event.
|
|
53
|
-
context
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
state :completed
|
|
57
|
-
state :failed
|
|
58
|
-
|
|
59
|
-
transition from: :importing, on: :import_completed, to: :completed
|
|
60
|
-
transition from: :importing, on: :import_failed, to: :failed
|
|
61
|
-
transition from: :completed, to: :__finish__
|
|
62
|
-
transition from: :failed, to: :__finish__
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
result = workflow.invoke({})
|
|
66
|
-
puts "Imported #{result.record_count} records"
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Phronomy
|
|
4
|
-
module Agent
|
|
5
|
-
class ActivationRegistry
|
|
6
|
-
def initialize
|
|
7
|
-
@mutex = Mutex.new
|
|
8
|
-
@records = {}
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def register(activation)
|
|
12
|
-
@mutex.synchronize do
|
|
13
|
-
raise ArgumentError, "activation already registered: #{activation.execution_id}" if @records.key?(activation.execution_id)
|
|
14
|
-
@records[activation.execution_id] = activation
|
|
15
|
-
end
|
|
16
|
-
activation
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def fetch(execution_id)
|
|
20
|
-
@mutex.synchronize { @records[execution_id.to_s] }
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
def delete(execution_id)
|
|
24
|
-
@mutex.synchronize { @records.delete(execution_id.to_s) }
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|
|
@@ -1,172 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require "securerandom"
|
|
4
|
-
require "time"
|
|
5
|
-
|
|
6
|
-
module Phronomy
|
|
7
|
-
module Agent
|
|
8
|
-
class AgentExecutionActivation
|
|
9
|
-
attr_reader :execution_id, :agent, :application_listener, :coordinator,
|
|
10
|
-
:base_manifest
|
|
11
|
-
attr_accessor :invocation, :session
|
|
12
|
-
|
|
13
|
-
def initialize(
|
|
14
|
-
execution:,
|
|
15
|
-
agent:,
|
|
16
|
-
runtime_projection:,
|
|
17
|
-
coordinator:,
|
|
18
|
-
application_listener: nil
|
|
19
|
-
)
|
|
20
|
-
@execution_id = execution.execution_id
|
|
21
|
-
@execution = execution
|
|
22
|
-
@agent = agent
|
|
23
|
-
@runtime_projection = runtime_projection
|
|
24
|
-
@base_manifest = runtime_projection.manifest
|
|
25
|
-
@coordinator = coordinator
|
|
26
|
-
@application_listener = application_listener
|
|
27
|
-
@mutex = Mutex.new
|
|
28
|
-
@active_call = nil
|
|
29
|
-
@llm_results = []
|
|
30
|
-
@runtime_events = []
|
|
31
|
-
@callback_errors = []
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def execution
|
|
35
|
-
@mutex.synchronize { @execution }
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def replace_execution(value)
|
|
39
|
-
@mutex.synchronize { @execution = value }
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
def runtime_projection
|
|
43
|
-
@mutex.synchronize { @runtime_projection }
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def replace_runtime_projection(value)
|
|
47
|
-
@mutex.synchronize { @runtime_projection = value }
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
# Allocates Provider Call identity before transport begins. This identity is
|
|
51
|
-
# provenance only; Context selection must not use it as a semantic boundary.
|
|
52
|
-
def begin_llm_call(projection)
|
|
53
|
-
call_context = {
|
|
54
|
-
llm_call_id: SecureRandom.uuid,
|
|
55
|
-
manifest_ref: projection.manifest_ref,
|
|
56
|
-
started_at: Time.now.utc.iso8601(6)
|
|
57
|
-
}.freeze
|
|
58
|
-
@mutex.synchronize do
|
|
59
|
-
if @active_call
|
|
60
|
-
raise Phronomy::Error,
|
|
61
|
-
"cannot start a Provider Call while another Provider Call is active"
|
|
62
|
-
end
|
|
63
|
-
@runtime_projection = projection
|
|
64
|
-
@active_call = call_context
|
|
65
|
-
end
|
|
66
|
-
call_context
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def record_llm_result(response:, error:, streaming:)
|
|
70
|
-
@mutex.synchronize do
|
|
71
|
-
active_call = @active_call
|
|
72
|
-
unless active_call
|
|
73
|
-
raise Phronomy::Error, "LLM result arrived without an active Provider Call"
|
|
74
|
-
end
|
|
75
|
-
@llm_results << {
|
|
76
|
-
llm_call_id: active_call.fetch(:llm_call_id),
|
|
77
|
-
response: response,
|
|
78
|
-
error: error,
|
|
79
|
-
streaming: streaming,
|
|
80
|
-
manifest_ref: active_call.fetch(:manifest_ref),
|
|
81
|
-
started_at: active_call.fetch(:started_at)
|
|
82
|
-
}
|
|
83
|
-
@active_call = nil
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
def runtime_snapshot
|
|
88
|
-
@mutex.synchronize do
|
|
89
|
-
{
|
|
90
|
-
llm_results: @llm_results.dup,
|
|
91
|
-
runtime_events: @runtime_events.dup,
|
|
92
|
-
active_call: @active_call&.dup
|
|
93
|
-
}
|
|
94
|
-
end
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
def acknowledge_runtime_snapshot(snapshot)
|
|
98
|
-
@mutex.synchronize do
|
|
99
|
-
@llm_results.shift(snapshot.fetch(:llm_results).length)
|
|
100
|
-
@runtime_events.shift(snapshot.fetch(:runtime_events).length)
|
|
101
|
-
if snapshot[:active_call] && @active_call == snapshot[:active_call]
|
|
102
|
-
@active_call = nil
|
|
103
|
-
end
|
|
104
|
-
end
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
ApplicationCallbackFailure = Data.define(:event_type, :error) do
|
|
108
|
-
def to_stream_callback_error
|
|
109
|
-
wrapped = Phronomy::StreamCallbackError.new(
|
|
110
|
-
event_type: event_type, original_error: error, result: nil
|
|
111
|
-
)
|
|
112
|
-
begin
|
|
113
|
-
raise wrapped, cause: error
|
|
114
|
-
rescue Phronomy::StreamCallbackError => caught
|
|
115
|
-
caught.set_backtrace(error.backtrace)
|
|
116
|
-
caught
|
|
117
|
-
end
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
attr_reader :callback_failure
|
|
122
|
-
|
|
123
|
-
def callback_failed?
|
|
124
|
-
@mutex.synchronize { !@callback_failure.nil? }
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
# Canonical runtime recording is independent of Application callback health.
|
|
128
|
-
# Once an event is observed it is appended even after a listener has failed.
|
|
129
|
-
def record_event(event)
|
|
130
|
-
listener = @mutex.synchronize do
|
|
131
|
-
@runtime_events << event
|
|
132
|
-
@callback_failure ? nil : @application_listener
|
|
133
|
-
end
|
|
134
|
-
return unless listener
|
|
135
|
-
|
|
136
|
-
listener.call(event)
|
|
137
|
-
rescue => callback_error
|
|
138
|
-
failure = ApplicationCallbackFailure.new(
|
|
139
|
-
event_type: event.type, error: callback_error
|
|
140
|
-
)
|
|
141
|
-
@mutex.synchronize do
|
|
142
|
-
@callback_failure ||= failure
|
|
143
|
-
@application_listener = nil
|
|
144
|
-
end
|
|
145
|
-
notify_callback_failure(failure)
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
private
|
|
149
|
-
|
|
150
|
-
def notify_callback_failure(failure)
|
|
151
|
-
invocation = @mutex.synchronize { @invocation }
|
|
152
|
-
if invocation
|
|
153
|
-
accepted = Phronomy::Runtime.instance.event_loop.post_to_session(
|
|
154
|
-
Phronomy::Event.new(
|
|
155
|
-
type: :application_callback_failed,
|
|
156
|
-
target_id: invocation.id,
|
|
157
|
-
payload: {failure: failure}
|
|
158
|
-
)
|
|
159
|
-
)
|
|
160
|
-
unless accepted
|
|
161
|
-
Phronomy.configuration.logger&.warn(
|
|
162
|
-
"[Phronomy] Callback failure recorded but could not notify FSM: execution_id=#{@execution_id}"
|
|
163
|
-
)
|
|
164
|
-
end
|
|
165
|
-
end
|
|
166
|
-
Phronomy.configuration.logger&.warn(
|
|
167
|
-
"[Phronomy] Application event listener failed: #{failure.error.class}: #{failure.error.message}"
|
|
168
|
-
)
|
|
169
|
-
end
|
|
170
|
-
end
|
|
171
|
-
end
|
|
172
|
-
end
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Phronomy
|
|
4
|
-
module Agent
|
|
5
|
-
ContextCandidate = Data.define(
|
|
6
|
-
:candidate_id,
|
|
7
|
-
:source_kind,
|
|
8
|
-
:category,
|
|
9
|
-
:role,
|
|
10
|
-
:content_ref,
|
|
11
|
-
:record_id,
|
|
12
|
-
:agent_id,
|
|
13
|
-
:execution_id,
|
|
14
|
-
:llm_call_id,
|
|
15
|
-
:tool_call_id,
|
|
16
|
-
:sequence,
|
|
17
|
-
:requirement,
|
|
18
|
-
:priority,
|
|
19
|
-
:metadata
|
|
20
|
-
) do
|
|
21
|
-
def initialize(**values)
|
|
22
|
-
normalized = values.merge(
|
|
23
|
-
candidate_id: values.fetch(:candidate_id).to_s.freeze,
|
|
24
|
-
source_kind: values.fetch(:source_kind).to_sym,
|
|
25
|
-
category: values.fetch(:category).to_sym,
|
|
26
|
-
role: values[:role]&.to_sym,
|
|
27
|
-
content_ref: values.fetch(:content_ref).to_s.freeze,
|
|
28
|
-
record_id: values[:record_id]&.to_s&.freeze,
|
|
29
|
-
agent_id: values[:agent_id]&.to_s&.freeze,
|
|
30
|
-
execution_id: values[:execution_id]&.to_s&.freeze,
|
|
31
|
-
llm_call_id: values[:llm_call_id]&.to_s&.freeze,
|
|
32
|
-
tool_call_id: values[:tool_call_id]&.to_s&.freeze,
|
|
33
|
-
sequence: values[:sequence] && Integer(values[:sequence]),
|
|
34
|
-
requirement: (values[:requirement] || :optional).to_sym,
|
|
35
|
-
priority: Integer(values[:priority] || 0),
|
|
36
|
-
metadata: Immutable.copy(values[:metadata] || {})
|
|
37
|
-
)
|
|
38
|
-
unless %i[protocol_required declared_required optional].include?(normalized[:requirement])
|
|
39
|
-
raise ArgumentError, "unknown ContextCandidate requirement: #{normalized[:requirement].inspect}"
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
super(**normalized)
|
|
43
|
-
freeze
|
|
44
|
-
end
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
end
|