phronomy 0.22.0 → 0.23.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 +200 -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 +152 -0
- data/docs/architecture/persistence.md +175 -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 +82 -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 +112 -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/README.md +172 -0
- data/docs/features.md +31 -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/persistence-backends.md +88 -38
- data/docs/runtime-and-concurrency.md +227 -33
- data/examples/README.md +13 -0
- data/lib/phronomy/agent/agent_execution.rb +19 -15
- 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 +145 -72
- data/lib/phronomy/agent/base.rb +388 -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/execution_coordinator.rb +1975 -587
- 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 +271 -0
- data/lib/phronomy/agent/recovery_coordinator/installation.rb +427 -0
- data/lib/phronomy/agent/recovery_coordinator/resolution.rb +635 -0
- data/lib/phronomy/agent/recovery_coordinator.rb +211 -0
- data/lib/phronomy/agent/recovery_support.rb +512 -0
- data/lib/phronomy/agent/ruby_llm_materializer.rb +22 -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 +336 -102
- 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.rb +77 -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/coordination_state.rb +18 -0
- data/lib/phronomy/multi_agent/coordinator.rb +154 -0
- data/lib/phronomy/multi_agent/execution_coordinator.rb +116 -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/handoff.rb +24 -45
- data/lib/phronomy/multi_agent/handoff_capability_factory.rb +87 -0
- data/lib/phronomy/multi_agent/handoff_context.rb +95 -0
- data/lib/phronomy/multi_agent/handoff_policy.rb +137 -0
- data/lib/phronomy/multi_agent/handoff_projection.rb +191 -0
- data/lib/phronomy/multi_agent/handoff_request.rb +45 -0
- data/lib/phronomy/multi_agent/orchestrator.rb +12 -15
- data/lib/phronomy/multi_agent/runner.rb +98 -0
- data/lib/phronomy/persistence/durable_codec.rb +646 -0
- data/lib/phronomy/persistence/durable_record.rb +117 -0
- data/lib/phronomy/persistence/in_memory.rb +210 -134
- data/lib/phronomy/persistence/migration/initial_format_migration.rb +226 -0
- data/lib/phronomy/persistence/repository_facades.rb +316 -0
- data/lib/phronomy/persistence.rb +81 -41
- 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 +1 -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/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/multi_agent.rbs +39 -0
- data/sig/phronomy/persistence.rbs +62 -4
- data/sig/phronomy/runtime.rbs +1 -4
- data/sig/phronomy/workflow.rbs +2 -2
- data/sig/phronomy.rbs +10 -0
- metadata +65 -17
- 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
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.23.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-08-
|
|
11
|
+
date: 2026-08-28 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,33 @@ 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/README.md
|
|
124
154
|
- docs/features.md
|
|
125
155
|
- docs/getting-started.md
|
|
126
156
|
- docs/mcp-client.md
|
|
127
157
|
- docs/migrations/0.15.md
|
|
128
158
|
- docs/migrations/0.16.md
|
|
129
159
|
- docs/migrations/0.19.md
|
|
160
|
+
- docs/migrations/0.22.md
|
|
130
161
|
- docs/persistence-backends.md
|
|
131
162
|
- docs/runtime-and-concurrency.md
|
|
132
|
-
- examples/
|
|
133
|
-
- examples/workflows/generic_task_event_mapping.rb
|
|
163
|
+
- examples/README.md
|
|
134
164
|
- gemfiles/mcp_1_0.gemfile
|
|
135
165
|
- lib/phronomy.rb
|
|
136
166
|
- lib/phronomy/agent.rb
|
|
137
|
-
- lib/phronomy/agent/activation_registry.rb
|
|
138
167
|
- lib/phronomy/agent/agent_execution.rb
|
|
139
|
-
- lib/phronomy/agent/agent_execution_activation.rb
|
|
140
168
|
- lib/phronomy/agent/agent_invocation.rb
|
|
141
169
|
- lib/phronomy/agent/agent_invocation_session_builder.rb
|
|
142
170
|
- lib/phronomy/agent/agent_root.rb
|
|
@@ -149,23 +177,15 @@ files:
|
|
|
149
177
|
- lib/phronomy/agent/context/capability/base.rb
|
|
150
178
|
- lib/phronomy/agent/context/instruction/prompt_template.rb
|
|
151
179
|
- lib/phronomy/agent/context_assembler.rb
|
|
152
|
-
- lib/phronomy/agent/context_candidate.rb
|
|
153
180
|
- lib/phronomy/agent/context_candidate_resolver.rb
|
|
154
181
|
- 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
182
|
- lib/phronomy/agent/context_parts/validators/final_budget_validator.rb
|
|
160
183
|
- lib/phronomy/agent/context_plan.rb
|
|
161
184
|
- lib/phronomy/agent/context_plan_validator.rb
|
|
162
185
|
- lib/phronomy/agent/context_policies/default.rb
|
|
163
186
|
- lib/phronomy/agent/context_policy.rb
|
|
164
|
-
- lib/phronomy/agent/
|
|
165
|
-
- lib/phronomy/agent/
|
|
166
|
-
- lib/phronomy/agent/context_request.rb
|
|
167
|
-
- lib/phronomy/agent/context_selection_unit.rb
|
|
168
|
-
- lib/phronomy/agent/derived_content_spec.rb
|
|
187
|
+
- lib/phronomy/agent/context_policy_input.rb
|
|
188
|
+
- lib/phronomy/agent/context_policy_input_builder.rb
|
|
169
189
|
- lib/phronomy/agent/execution_coordinator.rb
|
|
170
190
|
- lib/phronomy/agent/immutable.rb
|
|
171
191
|
- lib/phronomy/agent/journal_projection.rb
|
|
@@ -177,8 +197,14 @@ files:
|
|
|
177
197
|
- lib/phronomy/agent/llm_operation_result.rb
|
|
178
198
|
- lib/phronomy/agent/phase_machine_builder.rb
|
|
179
199
|
- lib/phronomy/agent/provider_call_outcome.rb
|
|
200
|
+
- lib/phronomy/agent/recovery_coordinator.rb
|
|
201
|
+
- lib/phronomy/agent/recovery_coordinator/continuation.rb
|
|
202
|
+
- lib/phronomy/agent/recovery_coordinator/installation.rb
|
|
203
|
+
- lib/phronomy/agent/recovery_coordinator/resolution.rb
|
|
204
|
+
- lib/phronomy/agent/recovery_support.rb
|
|
180
205
|
- lib/phronomy/agent/ruby_llm_materializer.rb
|
|
181
|
-
- lib/phronomy/agent/
|
|
206
|
+
- lib/phronomy/agent/selection/candidate.rb
|
|
207
|
+
- lib/phronomy/agent/selection/constraint.rb
|
|
182
208
|
- lib/phronomy/agent/shared_state.rb
|
|
183
209
|
- lib/phronomy/agent/token_budget_resolver.rb
|
|
184
210
|
- lib/phronomy/agent/tool_approval_request.rb
|
|
@@ -187,7 +213,9 @@ files:
|
|
|
187
213
|
- lib/phronomy/agent/tool_executor.rb
|
|
188
214
|
- lib/phronomy/agent/tool_invocation.rb
|
|
189
215
|
- lib/phronomy/agent/tool_invocation_session_builder.rb
|
|
216
|
+
- lib/phronomy/agent_already_exists_error.rb
|
|
190
217
|
- lib/phronomy/agent_busy_error.rb
|
|
218
|
+
- lib/phronomy/agent_purged_error.rb
|
|
191
219
|
- lib/phronomy/canonical_json.rb
|
|
192
220
|
- lib/phronomy/configuration.rb
|
|
193
221
|
- lib/phronomy/content_store/base.rb
|
|
@@ -198,10 +226,12 @@ files:
|
|
|
198
226
|
- lib/phronomy/engine/concurrency/cancellation_token.rb
|
|
199
227
|
- lib/phronomy/engine/concurrency/deadline.rb
|
|
200
228
|
- lib/phronomy/engine/concurrency/offload_pool.rb
|
|
229
|
+
- lib/phronomy/engine/concurrency/physical_completion_task.rb
|
|
201
230
|
- lib/phronomy/engine/concurrency/pool_registry.rb
|
|
202
231
|
- lib/phronomy/engine/event_loop.rb
|
|
203
232
|
- lib/phronomy/engine/fsm_session.rb
|
|
204
233
|
- lib/phronomy/engine/runtime.rb
|
|
234
|
+
- lib/phronomy/engine/runtime/agent_ownership_registry.rb
|
|
205
235
|
- lib/phronomy/engine/runtime/shutdown_result.rb
|
|
206
236
|
- lib/phronomy/engine/runtime/timer_queue.rb
|
|
207
237
|
- lib/phronomy/engine/runtime/timer_service.rb
|
|
@@ -223,18 +253,33 @@ files:
|
|
|
223
253
|
- lib/phronomy/llm_context_window/token_budget.rb
|
|
224
254
|
- lib/phronomy/llm_context_window/token_estimator.rb
|
|
225
255
|
- lib/phronomy/metrics.rb
|
|
256
|
+
- lib/phronomy/multi_agent/admission_registry.rb
|
|
257
|
+
- lib/phronomy/multi_agent/coordination_state.rb
|
|
258
|
+
- lib/phronomy/multi_agent/coordinator.rb
|
|
259
|
+
- lib/phronomy/multi_agent/execution_coordinator.rb
|
|
226
260
|
- lib/phronomy/multi_agent/fan_out_invocation.rb
|
|
227
261
|
- lib/phronomy/multi_agent/fan_out_session_builder.rb
|
|
228
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
|
|
229
268
|
- lib/phronomy/multi_agent/orchestrator.rb
|
|
230
269
|
- lib/phronomy/multi_agent/parallel_tool_chat.rb
|
|
270
|
+
- lib/phronomy/multi_agent/runner.rb
|
|
231
271
|
- lib/phronomy/multi_agent/team_coordinator.rb
|
|
232
272
|
- lib/phronomy/output_parser.rb
|
|
233
273
|
- lib/phronomy/output_parser/base.rb
|
|
234
274
|
- lib/phronomy/output_parser/json_parser.rb
|
|
235
275
|
- lib/phronomy/output_parser/structured_parser.rb
|
|
236
276
|
- lib/phronomy/persistence.rb
|
|
277
|
+
- lib/phronomy/persistence/durable_codec.rb
|
|
278
|
+
- lib/phronomy/persistence/durable_record.rb
|
|
237
279
|
- lib/phronomy/persistence/in_memory.rb
|
|
280
|
+
- lib/phronomy/persistence/migration/initial_format_migration.rb
|
|
281
|
+
- lib/phronomy/persistence/repository_facades.rb
|
|
282
|
+
- lib/phronomy/recovery.rb
|
|
238
283
|
- lib/phronomy/ruby_llm_patches.rb
|
|
239
284
|
- lib/phronomy/runnable.rb
|
|
240
285
|
- lib/phronomy/stream_callback_error.rb
|
|
@@ -266,6 +311,7 @@ files:
|
|
|
266
311
|
- lib/phronomy/tools/mcp.rb
|
|
267
312
|
- lib/phronomy/tools/vector_search.rb
|
|
268
313
|
- lib/phronomy/tracing.rb
|
|
314
|
+
- lib/phronomy/tracing/automatic.rb
|
|
269
315
|
- lib/phronomy/tracing/base.rb
|
|
270
316
|
- lib/phronomy/tracing/langfuse_tracer.rb
|
|
271
317
|
- lib/phronomy/tracing/null_tracer.rb
|
|
@@ -289,6 +335,7 @@ files:
|
|
|
289
335
|
- lib/phronomy/workflow.rb
|
|
290
336
|
- lib/phronomy/workflow/phase_machine_builder.rb
|
|
291
337
|
- lib/phronomy/workflow_context.rb
|
|
338
|
+
- lib/phronomy/workflow_recovery.rb
|
|
292
339
|
- lib/phronomy/workflow_runner.rb
|
|
293
340
|
- scripts/add_to_h_to_token_doubles.rb
|
|
294
341
|
- scripts/add_to_h_unnamed_doubles.rb
|
|
@@ -304,6 +351,7 @@ files:
|
|
|
304
351
|
- sig/phronomy/agent.rbs
|
|
305
352
|
- sig/phronomy/extensions.rbs
|
|
306
353
|
- sig/phronomy/llm_adapter.rbs
|
|
354
|
+
- sig/phronomy/multi_agent.rbs
|
|
307
355
|
- sig/phronomy/persistence.rbs
|
|
308
356
|
- sig/phronomy/runtime.rbs
|
|
309
357
|
- 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
|
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Phronomy
|
|
4
|
-
module Agent
|
|
5
|
-
module ContextParts
|
|
6
|
-
module Budget
|
|
7
|
-
class TokenBudgetPacker
|
|
8
|
-
def pack(request:, units:)
|
|
9
|
-
return Array(units).freeze unless request.token_budget
|
|
10
|
-
|
|
11
|
-
candidate_index = request.candidates.to_h { |candidate| [candidate.candidate_id, candidate] }
|
|
12
|
-
mandatory = Integer(request.metadata["mandatory_token_estimate"] || 0)
|
|
13
|
-
limit = request.token_budget.effective_input_limit
|
|
14
|
-
if mandatory > limit
|
|
15
|
-
raise Phronomy::ContextBudgetExceededError,
|
|
16
|
-
"Mandatory content (estimated #{mandatory} tokens) exceeds " \
|
|
17
|
-
"available input budget (#{limit} tokens)"
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
remaining = limit - mandatory
|
|
21
|
-
required, optional = Array(units).partition { |unit| unit.requirement != :optional }
|
|
22
|
-
required_cost = required.sum { |unit| unit_cost(unit, candidate_index) }
|
|
23
|
-
if required_cost > remaining
|
|
24
|
-
raise Phronomy::ContextBudgetExceededError,
|
|
25
|
-
"Required Context (estimated #{required_cost} tokens) exceeds " \
|
|
26
|
-
"remaining input budget (#{remaining} tokens after mandatory content)"
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
selected = required.dup
|
|
30
|
-
remaining -= required_cost
|
|
31
|
-
optional.each do |unit|
|
|
32
|
-
cost = unit_cost(unit, candidate_index)
|
|
33
|
-
next if cost > remaining
|
|
34
|
-
|
|
35
|
-
selected << unit
|
|
36
|
-
remaining -= cost
|
|
37
|
-
end
|
|
38
|
-
selected.freeze
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
private
|
|
42
|
-
|
|
43
|
-
def unit_cost(unit, candidate_index)
|
|
44
|
-
unit.candidate_ids.sum do |candidate_id|
|
|
45
|
-
candidate = candidate_index.fetch(candidate_id)
|
|
46
|
-
Integer(candidate.metadata["estimated_tokens"] || 0)
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
end
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
end
|
|
53
|
-
end
|