phronomy 0.27.0 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.mutant.yml +9 -9
- data/CHANGELOG.md +52 -0
- data/CONTRIBUTING.md +8 -8
- data/README.md +28 -7
- data/benchmark/bench_agent_invoke.rb +25 -50
- data/benchmark/bench_context_assembler.rb +14 -11
- data/benchmark/bench_regression.rb +1 -1
- data/benchmark/bench_tool_schema.rb +1 -1
- data/docs/application-recipes.md +229 -0
- data/docs/architecture/agent-chat-and-state-ownership.md +7 -1
- data/docs/architecture/agent-transition-ownership.md +13 -89
- data/docs/architecture/backend-groups.md +152 -0
- data/docs/architecture/before-llm-input.md +3 -3
- data/docs/architecture/context-management.md +1 -1
- data/docs/architecture/dx05-agent-scope.md +119 -0
- data/docs/architecture/dx05-workflow-task-state.md +134 -0
- data/docs/architecture/error-api-review.md +245 -0
- data/docs/architecture/execution-abstraction-review.md +88 -0
- data/docs/architecture/execution-boundaries.md +28 -0
- data/docs/architecture/execution-metadata-and-values.md +6 -0
- data/docs/architecture/multi-agent-handoff.md +6 -5
- data/docs/architecture/persistence.md +27 -9
- data/docs/architecture/r8-unit1.md +104 -0
- data/docs/architecture/r8-unit10.md +72 -0
- data/docs/architecture/r8-unit11.md +71 -0
- data/docs/architecture/r8-unit12.md +67 -0
- data/docs/architecture/r8-unit13.md +61 -0
- data/docs/architecture/r8-unit14.md +72 -0
- data/docs/architecture/r8-unit15.md +65 -0
- data/docs/architecture/r8-unit16.md +54 -0
- data/docs/architecture/r8-unit17.md +73 -0
- data/docs/architecture/r8-unit18.md +92 -0
- data/docs/architecture/r8-unit19.md +61 -0
- data/docs/architecture/r8-unit2.md +55 -0
- data/docs/architecture/r8-unit20.md +51 -0
- data/docs/architecture/r8-unit21.md +79 -0
- data/docs/architecture/r8-unit22.md +74 -0
- data/docs/architecture/r8-unit23.md +81 -0
- data/docs/architecture/r8-unit3.md +130 -0
- data/docs/architecture/r8-unit4.md +79 -0
- data/docs/architecture/r8-unit5.md +115 -0
- data/docs/architecture/r8-unit6.md +71 -0
- data/docs/architecture/r8-unit7.md +70 -0
- data/docs/architecture/r8-unit8.md +78 -0
- data/docs/architecture/r8-unit9.md +73 -0
- data/docs/architecture/refactoring-closure.md +10 -1
- data/docs/architecture/remaining-refactoring-plan.md +16 -0
- data/docs/architecture/storage-execution-boundaries.md +54 -0
- data/docs/architecture/tool-schema-recording-gap.md +6 -0
- data/docs/architecture/tracing.md +1 -1
- data/docs/architecture/workflow-terminal-ownership-design.md +2 -2
- data/docs/architecture.md +24 -11
- data/docs/decisions/015-tool-public-facade-and-rbs-boundary.md +3 -3
- data/docs/decisions/027-llm-adapter-provider-boundary.md +2 -0
- data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +2 -0
- data/docs/decisions/031-durable-multi-agent-coordination.md +2 -0
- data/docs/decisions/032-storage-backend-composition.md +5 -0
- data/docs/decisions/034-handoff-runner-coordination-ownership.md +2 -0
- data/docs/decisions/035-tool-executor-capability-ownership.md +1 -1
- data/docs/decisions/038-responsibility-based-source-layout.md +14 -0
- data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +8 -0
- data/docs/decisions/040-configuration-default-composition.md +12 -1
- data/docs/decisions/045-worker-input-restriction-ownership.md +4 -0
- data/docs/decisions/051-execution-outcome-worker-ownership.md +2 -0
- data/docs/decisions/056-workflow-terminal-policy-ownership.md +1 -1
- data/docs/decisions/058-neutral-storage-primitives.md +5 -0
- data/docs/decisions/059-backend-contracts-and-async-clients.md +74 -0
- data/docs/decisions/060-runtime-settings-and-application-configuration.md +86 -0
- data/docs/decisions/061-persistence-failure-contracts.md +89 -0
- data/docs/decisions/062-content-store-service.md +52 -0
- data/docs/decisions/063-execution-contracts-and-services.md +88 -0
- data/docs/decisions/064-agent-execution-and-multi-agent-responsibility.md +36 -0
- data/docs/decisions/065-vector-and-embeddings-common-operations.md +38 -0
- data/docs/decisions/066-llm-and-tool-operation-contracts.md +20 -0
- data/docs/decisions/067-agent-progress-and-engine-connection.md +29 -0
- data/docs/decisions/068-agent-tool-child-progress-and-tool-operation.md +36 -0
- data/docs/decisions/README.md +18 -1
- data/docs/execution-boundaries-preview.md +45 -0
- data/docs/failure-reason-codes.md +45 -0
- data/docs/features.md +12 -4
- data/docs/getting-started.md +52 -48
- data/docs/migrations/0.22.md +2 -2
- data/docs/migrations/durable-semantic-coordination-v2.md +2 -0
- data/docs/migrations/handoff-runner-multi-agent.md +2 -0
- data/docs/migrations/persistence-failure-contracts.md +67 -0
- data/docs/migrations/r8-unit10.md +36 -0
- data/docs/migrations/r8-unit11.md +33 -0
- data/docs/migrations/r8-unit12.md +31 -0
- data/docs/migrations/r8-unit13.md +36 -0
- data/docs/migrations/r8-unit14.md +31 -0
- data/docs/migrations/r8-unit15.md +31 -0
- data/docs/migrations/r8-unit16.md +26 -0
- data/docs/migrations/r8-unit17.md +28 -0
- data/docs/migrations/r8-unit18.md +29 -0
- data/docs/migrations/r8-unit19.md +28 -0
- data/docs/migrations/r8-unit20.md +24 -0
- data/docs/migrations/r8-unit21.md +28 -0
- data/docs/migrations/r8-unit22.md +29 -0
- data/docs/migrations/r8-unit23.md +20 -0
- data/docs/migrations/r8-unit4.md +67 -0
- data/docs/migrations/r8-unit5.md +75 -0
- data/docs/migrations/r8-unit6.md +109 -0
- data/docs/migrations/r8-unit7.md +31 -0
- data/docs/migrations/r8-unit8.md +33 -0
- data/docs/migrations/r8-unit9.md +28 -0
- data/docs/migrations/storage-async-client.md +103 -0
- data/docs/migrations/vector-async-clients.md +110 -0
- data/docs/ordered-event-delivery.md +54 -0
- data/docs/persistence-backends.md +38 -21
- data/docs/runtime-and-concurrency.md +26 -1
- data/docs/workflow-completion.md +92 -0
- data/gemfiles/http_minimum.gemfile +5 -0
- data/lib/phronomy/agent/admission.rb +78 -0
- data/lib/phronomy/agent/{lifecycle_contract/agent_already_exists_error.rb → agent_already_exists_error.rb} +1 -1
- data/lib/phronomy/agent/{lifecycle_contract/agent_busy_error.rb → agent_busy_error.rb} +1 -1
- data/lib/phronomy/agent/agent_not_found_error.rb +16 -0
- data/lib/phronomy/agent/{lifecycle_contract/agent_purged_error.rb → agent_purged_error.rb} +1 -1
- data/lib/phronomy/agent/agent_unloaded_error.rb +14 -0
- data/lib/phronomy/agent/api/agent.rb +13 -0
- data/lib/phronomy/{recovery → agent/api}/execution_rehydration_required_error.rb +1 -1
- data/lib/phronomy/agent/async_event_api.rb +132 -103
- data/lib/phronomy/agent/base.rb +146 -113
- data/lib/phronomy/agent/before_llm_input.rb +70 -0
- data/lib/phronomy/agent/composition/run_once.rb +1 -1
- data/lib/phronomy/agent/{context_assembly/context_candidate_resolver.rb → context_candidate_resolver.rb} +2 -2
- data/lib/phronomy/agent/{context_assembly/context_assembler.rb → context_preparation.rb} +35 -221
- data/lib/phronomy/agent/control_request.rb +21 -0
- data/lib/phronomy/agent/execution/agent_execution.rb +1 -1
- data/lib/phronomy/agent/execution/agent_invocation.rb +27 -53
- data/lib/phronomy/agent/execution/approval_resume_commit.rb +1 -1
- data/lib/phronomy/agent/execution/dispatch_preparation.rb +17 -127
- data/lib/phronomy/agent/execution/exact_execution.rb +55 -32
- data/lib/phronomy/agent/execution/execution_cancellation.rb +8 -4
- data/lib/phronomy/agent/execution/execution_coordinator.rb +229 -407
- data/lib/phronomy/agent/execution/execution_metadata.rb +1 -0
- data/lib/phronomy/agent/execution/execution_outcome_committer.rb +71 -127
- data/lib/phronomy/agent/execution/execution_registry.rb +6 -2
- data/lib/phronomy/agent/execution/execution_session_runner.rb +22 -27
- data/lib/phronomy/agent/execution/initial_preparation.rb +19 -120
- data/lib/phronomy/agent/execution/{agent_invocation_session_builder.rb → invocation_actions.rb} +54 -194
- data/lib/phronomy/agent/execution/invocation_transitions.rb +39 -1
- data/lib/phronomy/agent/execution/runtime_record_encoder.rb +8 -11
- data/lib/phronomy/agent/execution_change.rb +161 -0
- data/lib/phronomy/agent/execution_environment.rb +36 -0
- data/lib/phronomy/agent/execution_extension_state.rb +21 -0
- data/lib/phronomy/agent/execution_observation.rb +35 -0
- data/lib/phronomy/agent/filterable.rb +188 -0
- data/lib/phronomy/agent/handoff/{handoff_context.rb → transfer_context.rb} +4 -4
- data/lib/phronomy/agent/handoff/{handoff_projection.rb → transfer_projection.rb} +14 -33
- data/lib/phronomy/{llm_contract/transport_error.rb → agent/handoff_error.rb} +1 -1
- data/lib/phronomy/agent/journal/journal_projection.rb +1 -1
- data/lib/phronomy/agent/knowledge_item.rb +13 -0
- data/lib/phronomy/agent/lifecycle/agent_root.rb +1 -1
- data/lib/phronomy/agent/lifecycle/lifetime_api.rb +76 -0
- data/lib/phronomy/agent/lifecycle/operation_lease.rb +33 -0
- data/lib/phronomy/agent/lifecycle/operation_result.rb +53 -0
- data/lib/phronomy/agent/lifecycle/ownership_registry.rb +117 -31
- data/lib/phronomy/agent/lifecycle/scope.rb +198 -0
- data/lib/phronomy/agent/lifecycle/scope_cleanup_error.rb +19 -0
- data/lib/phronomy/agent/lifecycle/unload_transition.rb +78 -0
- data/lib/phronomy/agent/persistence/agent_repository.rb +29 -21
- data/lib/phronomy/agent/persistence/cancellation_repository.rb +51 -0
- data/lib/phronomy/agent/persistence/codec.rb +1 -21
- data/lib/phronomy/agent/persistence/execution_repository.rb +57 -41
- data/lib/phronomy/agent/persistence/journal_repository.rb +27 -18
- data/lib/phronomy/agent/persistence/records.rb +33 -0
- data/lib/phronomy/agent/persistence/retention_repository.rb +70 -0
- data/lib/phronomy/agent/persistence/storage_schema.rb +7 -2
- data/lib/phronomy/agent/persistence/watermark.rb +13 -11
- data/lib/phronomy/agent/recovery/invocation_restorer.rb +13 -21
- data/lib/phronomy/agent/recovery/recovery_coordinator/continuation.rb +13 -12
- data/lib/phronomy/agent/recovery/recovery_coordinator/installation.rb +34 -34
- data/lib/phronomy/agent/recovery/recovery_coordinator/resolution.rb +33 -39
- data/lib/phronomy/agent/recovery/recovery_coordinator.rb +28 -25
- data/lib/phronomy/agent/recovery/recovery_rules.rb +119 -0
- data/lib/phronomy/agent/recovery/recovery_support.rb +10 -19
- data/lib/phronomy/agent/reserved_execution.rb +16 -0
- data/lib/phronomy/agent/retention.rb +17 -0
- data/lib/phronomy/agent/runtime_binding/engine_environment.rb +60 -0
- data/lib/phronomy/agent/runtime_binding/engine_session_builder.rb +34 -0
- data/lib/phronomy/agent/runtime_binding/phase_machine_builder.rb +64 -0
- data/lib/phronomy/agent/runtime_binding/tool_session_builder.rb +34 -0
- data/lib/phronomy/agent/{context_assembly/ruby_llm_materializer.rb → runtime_input.rb} +18 -35
- data/lib/phronomy/agent/{context_assembly/saved_context_reader.rb → saved_context_reader.rb} +6 -4
- data/lib/phronomy/agent/settings.rb +47 -0
- data/lib/phronomy/agent/{context_assembly/state_writer.rb → state_writer.rb} +4 -3
- data/lib/phronomy/agent/store.rb +253 -0
- data/lib/phronomy/agent/{lifecycle_contract/stream_callback_error.rb → stream_callback_error.rb} +3 -3
- data/lib/phronomy/agent/tool_execution/tool_binding.rb +4 -64
- data/lib/phronomy/agent/tool_execution/tool_invocation.rb +50 -219
- data/lib/phronomy/agent/tool_execution/tool_invocation_actions.rb +107 -0
- data/lib/phronomy/agent/tool_execution/tool_invocation_transitions.rb +70 -0
- data/lib/phronomy/common/error.rb +65 -1
- data/lib/phronomy/configuration/runtime_settings.rb +49 -0
- data/lib/phronomy/content_store/base.rb +7 -2
- data/lib/phronomy/content_store/storage_schema.rb +1 -0
- data/lib/phronomy/content_store/stored_contents.rb +2 -1
- data/lib/phronomy/context/assembly.rb +195 -0
- data/lib/phronomy/{llm_contract/context_budget_exceeded_error.rb → context/budget_exceeded_error.rb} +3 -1
- data/lib/phronomy/context/candidate.rb +51 -0
- data/lib/phronomy/context/constraint.rb +47 -0
- data/lib/phronomy/{agent/context_contract → context}/context_plan.rb +1 -1
- data/lib/phronomy/{agent/context_contract → context}/context_plan_validator.rb +3 -3
- data/lib/phronomy/{agent/context_contract → context}/context_policy.rb +1 -1
- data/lib/phronomy/{agent/context_contract → context}/context_policy_input.rb +1 -1
- data/lib/phronomy/{agent/context_assembly → context}/context_policy_input_builder.rb +1 -1
- data/lib/phronomy/context/default_policy.rb +140 -0
- data/lib/phronomy/context/final_budget_validator.rb +33 -0
- data/lib/phronomy/{agent/context_contract → context}/llm_input_build_context.rb +1 -1
- data/lib/phronomy/{agent/context_contract → context}/llm_input_manifest.rb +19 -19
- data/lib/phronomy/{agent/context_contract → context}/llm_input_patch.rb +1 -1
- data/lib/phronomy/context/manifest_error.rb +10 -0
- data/lib/phronomy/context/prompt_template.rb +98 -0
- data/lib/phronomy/{vector_store → documents}/loader/base.rb +1 -1
- data/lib/phronomy/{vector_store → documents}/loader/csv_loader.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/loader/markdown_loader.rb +3 -3
- data/lib/phronomy/{vector_store → documents}/loader/plain_text_loader.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/splitter/base.rb +1 -1
- data/lib/phronomy/{vector_store → documents}/splitter/fixed_size_splitter.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/splitter/recursive_splitter.rb +2 -2
- data/lib/phronomy/embeddings/async/async_client.rb +44 -0
- data/lib/phronomy/embeddings/authentication_error.rb +11 -0
- data/lib/phronomy/embeddings/backends/ruby_llm_embeddings.rb +52 -0
- data/lib/phronomy/embeddings/base.rb +43 -0
- data/lib/phronomy/embeddings/invalid_result_error.rb +11 -0
- data/lib/phronomy/embeddings/rate_limit_error.rb +11 -0
- data/lib/phronomy/embeddings/transport_error.rb +11 -0
- data/lib/phronomy/engine/concurrency/offload_pool.rb +36 -21
- data/lib/phronomy/engine/concurrency/pool_registry.rb +47 -12
- data/lib/phronomy/engine/diagnostics.rb +1 -1
- data/lib/phronomy/engine/event_loop.rb +7 -7
- data/lib/phronomy/engine/execution_binding.rb +23 -0
- data/lib/phronomy/engine/execution_receiver_binding.rb +43 -0
- data/lib/phronomy/engine/fsm_protocol.rb +1 -6
- data/lib/phronomy/engine/fsm_session.rb +3 -3
- data/lib/phronomy/engine/metrics.rb +16 -14
- data/lib/phronomy/engine/runtime/timer_queue.rb +1 -1
- data/lib/phronomy/engine/runtime.rb +25 -12
- data/lib/phronomy/engine/terminal_decision.rb +7 -0
- data/lib/phronomy/execution/async_operation.rb +41 -0
- data/lib/phronomy/{engine → execution}/blocking.rb +2 -2
- data/lib/phronomy/{engine → execution}/concurrency/cancellation_token.rb +1 -1
- data/lib/phronomy/{engine → execution}/concurrency/deadline.rb +0 -17
- data/lib/phronomy/{engine → execution}/concurrency/physical_completion_task.rb +1 -1
- data/lib/phronomy/{engine → execution}/concurrency/result_collector.rb +1 -1
- data/lib/phronomy/{engine → execution}/concurrency/result_composition.rb +33 -12
- data/lib/phronomy/{engine/concurrency/subscriptions.rb → execution/concurrency/result_subscriptions.rb} +14 -18
- data/lib/phronomy/execution/concurrency/subscriptions.rb +25 -0
- data/lib/phronomy/{engine → execution}/execution.rb +61 -27
- data/lib/phronomy/{engine → execution}/execution_cancellation_error.rb +2 -2
- data/lib/phronomy/execution/execution_receiver.rb +78 -0
- data/lib/phronomy/execution/execution_scope_state.rb +35 -0
- data/lib/phronomy/{engine → execution}/execution_timeout_error.rb +2 -2
- data/lib/phronomy/{engine → execution}/invocation_context.rb +0 -15
- data/lib/phronomy/execution/invocation_controls.rb +73 -0
- data/lib/phronomy/execution/runnable.rb +24 -0
- data/lib/phronomy/{engine → execution}/task_result.rb +9 -4
- data/lib/phronomy/execution/wait_policy.rb +24 -0
- data/lib/phronomy/filter/{contract/filter_block_error.rb → filter_block_error.rb} +3 -3
- data/lib/phronomy/generation/generator_verifier/agent_operation.rb +65 -0
- data/lib/phronomy/generation/generator_verifier/default_parser.rb +27 -0
- data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +2 -2
- data/lib/phronomy/generation/generator_verifier.rb +7 -7
- data/lib/phronomy/generation/low_confidence_error.rb +2 -2
- data/lib/phronomy/integrations/ordered_event_delivery.rb +208 -0
- data/lib/phronomy/llm_adapter/async/async_client.rb +47 -0
- data/lib/phronomy/{llm_contract → llm_adapter}/authentication_error.rb +3 -1
- data/lib/phronomy/llm_adapter/backends/ruby_llm.rb +183 -0
- data/lib/phronomy/llm_adapter/base.rb +49 -68
- data/lib/phronomy/{llm_contract → llm_adapter}/context_length_error.rb +3 -1
- data/lib/phronomy/llm_adapter/invalid_result_error.rb +10 -0
- data/lib/phronomy/llm_adapter/message.rb +62 -0
- data/lib/phronomy/{llm_contract → llm_adapter}/rate_limit_error.rb +3 -1
- data/lib/phronomy/llm_adapter/request.rb +79 -0
- data/lib/phronomy/llm_adapter/response.rb +36 -0
- data/lib/phronomy/llm_adapter/stream_chunk.rb +18 -0
- data/lib/phronomy/llm_adapter/token_usage.rb +64 -0
- data/lib/phronomy/llm_adapter/transport_error.rb +9 -0
- data/lib/phronomy/llm_adapter.rb +3 -16
- data/lib/phronomy/multi_agent/admission_registry.rb +1 -8
- data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +108 -84
- data/lib/phronomy/multi_agent/execution_environment.rb +29 -0
- data/lib/phronomy/{agent/handoff → multi_agent}/handoff.rb +2 -2
- data/lib/phronomy/{agent/handoff → multi_agent}/handoff_capability_factory.rb +21 -2
- data/lib/phronomy/multi_agent/handoff_participant.rb +79 -0
- data/lib/phronomy/{agent/handoff → multi_agent}/handoff_policy.rb +1 -1
- data/lib/phronomy/multi_agent/handoff_runner.rb +77 -100
- data/lib/phronomy/{agent/handoff → multi_agent}/handoff_state.rb +1 -1
- data/lib/phronomy/multi_agent/orchestrator.rb +61 -75
- data/lib/phronomy/multi_agent/persistence/codec.rb +20 -0
- data/lib/phronomy/multi_agent/persistence/handoff_state_repository.rb +77 -0
- data/lib/phronomy/multi_agent/persistence/records.rb +27 -0
- data/lib/phronomy/multi_agent/persistence/storage_schema.rb +21 -0
- data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +60 -44
- data/lib/phronomy/multi_agent/persistence/team_repository.rb +29 -21
- data/lib/phronomy/multi_agent/reserved_child_admission.rb +88 -0
- data/lib/phronomy/multi_agent/runtime_binding/engine_environment.rb +35 -0
- data/lib/phronomy/multi_agent/shared_state.rb +2 -2
- data/lib/phronomy/multi_agent/store.rb +156 -0
- data/lib/phronomy/multi_agent/team_busy_error.rb +18 -0
- data/lib/phronomy/multi_agent/team_coordinator.rb +99 -77
- data/lib/phronomy/multi_agent/team_not_found_error.rb +18 -0
- data/lib/phronomy/multi_agent/team_ownership_registry.rb +2 -12
- data/lib/phronomy/output_parser/parse_error.rb +7 -0
- data/lib/phronomy/persistence/content_repository.rb +26 -0
- data/lib/phronomy/persistence/errors.rb +16 -0
- data/lib/phronomy/persistence/migration/initial_format_migration.rb +2 -2
- data/lib/phronomy/persistence/persistence.rb +23 -0
- data/lib/phronomy/persistence/save_outcome.rb +24 -0
- data/lib/phronomy/persistence/snapshot_comparison.rb +81 -0
- data/lib/phronomy/persistence/storage_boundary.rb +32 -0
- data/lib/phronomy/persistence/transaction.rb +86 -0
- data/lib/phronomy/persistence_composition/storage_schema.rb +9 -4
- data/lib/phronomy/persistence_composition/stores.rb +34 -0
- data/lib/phronomy/persistence_composition/unit4_migration.rb +276 -0
- data/lib/phronomy/runtime_composition/agent_defaults.rb +5 -1
- data/lib/phronomy/runtime_composition/configuration.rb +146 -0
- data/lib/phronomy/runtime_composition/evaluation_defaults.rb +7 -0
- data/lib/phronomy/runtime_composition/execution_defaults.rb +5 -0
- data/lib/phronomy/runtime_composition/generation_defaults.rb +6 -0
- data/lib/phronomy/runtime_composition/global_configuration.rb +38 -0
- data/lib/phronomy/runtime_composition/multi_agent_defaults.rb +9 -0
- data/lib/phronomy/runtime_composition/workflow_defaults.rb +4 -0
- data/lib/phronomy/storage/async/async_client.rb +60 -0
- data/lib/phronomy/storage/backend.rb +8 -2
- data/lib/phronomy/storage/condition_failed_error.rb +2 -2
- data/lib/phronomy/storage/unique_constraint_error.rb +2 -2
- data/lib/phronomy/testing/eval/metrics.rb +1 -1
- data/lib/phronomy/testing/eval/scorer/llm_judge.rb +19 -3
- data/lib/phronomy/testing/persistence_contract/a_content_store.rb +2 -2
- data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +6 -6
- data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +55 -52
- data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +3 -3
- data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +7 -7
- data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +11 -11
- data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +57 -51
- data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +1 -1
- data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +23 -22
- data/lib/phronomy/tool/authorization.rb +77 -0
- data/lib/phronomy/tool/base.rb +551 -9
- data/lib/phronomy/tool/call_request.rb +40 -0
- data/lib/phronomy/{agent/tool_execution/tool_definition_set.rb → tool/definition_set.rb} +7 -8
- data/lib/phronomy/tool/operation.rb +88 -0
- data/lib/phronomy/tool/schema.rb +214 -0
- data/lib/phronomy/tool/settings.rb +31 -0
- data/lib/phronomy/tool/tool_error.rb +7 -0
- data/lib/phronomy/tool/tool_executor.rb +58 -0
- data/lib/phronomy/tools/agent.rb +22 -90
- data/lib/phronomy/tools/mcp.rb +21 -14
- data/lib/phronomy/tools/vector_search.rb +4 -4
- data/lib/phronomy/tracing/automatic.rb +11 -6
- data/lib/phronomy/tracing/base.rb +36 -7
- data/lib/phronomy/tracing/observation.rb +37 -0
- data/lib/phronomy/tracing/open_telemetry_tracer.rb +4 -29
- data/lib/phronomy/tracing/settings.rb +36 -0
- data/lib/phronomy/vector_store/{async_backend.rb → async/async_client.rb} +35 -29
- data/lib/phronomy/vector_store/{in_memory.rb → backends/in_memory.rb} +12 -10
- data/lib/phronomy/vector_store/{pgvector.rb → backends/pgvector.rb} +44 -16
- data/lib/phronomy/vector_store/{redis_search.rb → backends/redis_search.rb} +74 -37
- data/lib/phronomy/vector_store/base.rb +118 -49
- data/lib/phronomy/vector_store/invalid_result_error.rb +12 -0
- data/lib/phronomy/vector_store.rb +2 -17
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/workflow/completion.rb +11 -0
- data/lib/phronomy/workflow/execution/workflow.rb +37 -9
- data/lib/phronomy/workflow/execution/workflow_action_rules.rb +67 -0
- data/lib/phronomy/workflow/execution/workflow_context.rb +2 -2
- data/lib/phronomy/workflow/execution/workflow_execution_environment.rb +28 -0
- data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +77 -8
- data/lib/phronomy/workflow/execution/workflow_runner.rb +105 -92
- data/lib/phronomy/workflow/execution/workflow_settings.rb +28 -0
- data/lib/phronomy/workflow/execution/workflow_task_execution.rb +153 -0
- data/lib/phronomy/workflow/execution/workflow_task_reception.rb +72 -0
- data/lib/phronomy/workflow/execution/workflow_task_resume_error.rb +14 -0
- data/lib/phronomy/workflow/persistence/state_repository.rb +28 -22
- data/lib/phronomy/workflow/persistence/storage_schema.rb +13 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_engine_environment.rb +61 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb +158 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_task_session.rb +76 -0
- data/lib/phronomy/workflow/{execution → runtime_binding}/workflow_terminal_policy.rb +4 -4
- data/lib/phronomy.rb +63 -16
- data/scripts/api_snapshot.rb +47 -15
- data/scripts/check_readme_ruby.rb +24 -29
- data/scripts/check_readme_runnable.rb +92 -81
- data/sig/_private/phronomy/agent/settings.rbs +19 -0
- data/sig/_private/phronomy/configuration/runtime_settings.rbs +20 -0
- data/sig/_private/phronomy/llm_adapter/async/async_client.rbs +10 -0
- data/sig/_private/phronomy/multi_agent/store.rbs +8 -0
- data/sig/_private/phronomy/persistence/storage_boundary.rbs +7 -0
- data/sig/_private/phronomy/tool/settings.rbs +13 -0
- data/sig/_private/phronomy/tracing/settings.rbs +15 -0
- data/sig/_private/phronomy/workflow/execution/workflow_settings.rbs +13 -0
- data/sig/phronomy/agent/admission.rbs +19 -0
- data/sig/phronomy/agent/agent_not_found_error.rbs +6 -0
- data/sig/phronomy/agent/agent_unloaded_error.rbs +6 -0
- data/sig/phronomy/agent/execution_contract.rbs +132 -0
- data/sig/phronomy/agent/execution_environment.rbs +20 -0
- data/sig/phronomy/agent/store.rbs +73 -0
- data/sig/phronomy/agent/stream_callback_error.rbs +8 -0
- data/sig/phronomy/agent.rbs +29 -135
- data/sig/phronomy/common.rbs +7 -0
- data/sig/phronomy/content_store/contracts.rbs +13 -0
- data/sig/phronomy/context.rbs +152 -0
- data/sig/phronomy/embeddings/async/async_client.rbs +9 -0
- data/sig/phronomy/embeddings/base.rbs +23 -0
- data/sig/phronomy/engine/concurrency/timer_bindings.rbs +22 -0
- data/sig/phronomy/engine/control_bindings.rbs +11 -0
- data/sig/phronomy/engine/control_protocols.rbs +23 -0
- data/sig/phronomy/engine/execution_binding.rbs +6 -0
- data/sig/phronomy/engine/execution_receiver.rbs +15 -0
- data/sig/phronomy/engine/observability.rbs +13 -0
- data/sig/phronomy/execution/async_operation.rbs +7 -0
- data/sig/phronomy/execution/blocking.rbs +5 -0
- data/sig/phronomy/execution/concurrency/cancellation_token.rbs +13 -0
- data/sig/phronomy/execution/concurrency/deadline.rbs +10 -0
- data/sig/phronomy/execution/concurrency/result_composition.rbs +7 -0
- data/sig/phronomy/execution/control_protocols.rbs +18 -0
- data/sig/phronomy/execution/errors.rbs +7 -0
- data/sig/phronomy/execution/execution.rbs +41 -0
- data/sig/phronomy/execution/execution_receiver.rbs +40 -0
- data/sig/phronomy/execution/execution_scope_state.rbs +9 -0
- data/sig/phronomy/execution/invocation_context.rbs +18 -0
- data/sig/phronomy/execution/invocation_controls.rbs +13 -0
- data/sig/phronomy/execution/task_result.rbs +29 -0
- data/sig/phronomy/extensions.rbs +1 -21
- data/sig/phronomy/filter/filter_block_error.rbs +6 -0
- data/sig/phronomy/generation/low_confidence_error.rbs +6 -0
- data/sig/phronomy/handoff.rbs +10 -4
- data/sig/phronomy/integrations.rbs +14 -0
- data/sig/phronomy/llm_adapter/backends/ruby_llm.rbs +6 -0
- data/sig/phronomy/llm_adapter/base.rbs +14 -0
- data/sig/phronomy/llm_adapter/usage.rbs +14 -0
- data/sig/phronomy/llm_adapter/values.rbs +43 -0
- data/sig/phronomy/llm_context_window.rbs +10 -0
- data/sig/phronomy/multi_agent/reserved_child_admission.rbs +8 -0
- data/sig/phronomy/multi_agent/store.rbs +54 -0
- data/sig/phronomy/multi_agent/team_busy_error.rbs +8 -0
- data/sig/phronomy/multi_agent/team_not_found_error.rbs +8 -0
- data/sig/phronomy/multi_agent.rbs +6 -3
- data/sig/phronomy/persistence/errors.rbs +18 -0
- data/sig/phronomy/persistence/migration.rbs +13 -0
- data/sig/phronomy/persistence/transaction.rbs +19 -0
- data/sig/phronomy/persistence.rbs +1 -99
- data/sig/phronomy/persistence_composition.rbs +19 -0
- data/sig/phronomy/runtime_composition/configuration.rbs +36 -0
- data/sig/phronomy/storage/async/async_client.rbs +8 -0
- data/sig/phronomy/storage/backends/in_memory.rbs +9 -0
- data/sig/phronomy/{storage.rbs → storage/contracts.rbs} +4 -18
- data/sig/phronomy/tool.rbs +48 -4
- data/sig/phronomy/tracing/observation.rbs +7 -0
- data/sig/phronomy/vector_store/async/async_client.rbs +13 -0
- data/sig/phronomy/vector_store/base.rbs +23 -0
- data/sig/phronomy/workflow/execution/workflow_execution_environment.rbs +15 -0
- data/sig/phronomy/workflow/runtime_binding/workflow_terminal_policy.rbs +7 -0
- data/sig/phronomy/workflow/store.rbs +8 -0
- data/sig/phronomy/workflow.rbs +5 -1
- data/sig/phronomy.rbs +4 -49
- metadata +365 -117
- data/lib/phronomy/agent/concerns/before_llm_input.rb +0 -66
- data/lib/phronomy/agent/concerns/error_translation.rb +0 -45
- data/lib/phronomy/agent/concerns/filterable.rb +0 -184
- data/lib/phronomy/agent/context/capability/base.rb +0 -548
- data/lib/phronomy/agent/context/capability/tool_executor.rb +0 -62
- data/lib/phronomy/agent/context/instruction/prompt_template.rb +0 -102
- data/lib/phronomy/agent/context_assembly/runtime_chat_builder.rb +0 -36
- data/lib/phronomy/agent/context_assembly/token_budget_resolver.rb +0 -22
- data/lib/phronomy/agent/context_parts/validators/final_budget_validator.rb +0 -37
- data/lib/phronomy/agent/context_policies/default.rb +0 -142
- data/lib/phronomy/agent/execution/phase_machine_builder.rb +0 -101
- data/lib/phronomy/agent/execution/provider_call_outcome.rb +0 -106
- data/lib/phronomy/agent/handoff/handoff_execution_coordinator.rb +0 -15
- data/lib/phronomy/agent/handoff/handoff_outcome_committer.rb +0 -131
- data/lib/phronomy/agent/handoff/handoff_request.rb +0 -45
- data/lib/phronomy/agent/lifecycle_contract/handoff_error.rb +0 -7
- data/lib/phronomy/agent/persistence/handoff_state_repository.rb +0 -58
- data/lib/phronomy/agent/persistence/queries.rb +0 -61
- data/lib/phronomy/agent/selection/candidate.rb +0 -53
- data/lib/phronomy/agent/selection/constraint.rb +0 -49
- data/lib/phronomy/agent/tool_execution/tool_call_intercepted.rb +0 -29
- data/lib/phronomy/agent/tool_execution/tool_invocation_session_builder.rb +0 -276
- data/lib/phronomy/configuration/configuration.rb +0 -68
- data/lib/phronomy/configuration/global_configuration.rb +0 -26
- data/lib/phronomy/engine/concurrency/cancellation_scope.rb +0 -124
- data/lib/phronomy/engine/concurrency/operation_binding.rb +0 -43
- data/lib/phronomy/engine/execution_receiver.rb +0 -65
- data/lib/phronomy/engine/runnable.rb +0 -54
- data/lib/phronomy/llm_adapter/ruby_llm.rb +0 -47
- data/lib/phronomy/llm_contract/token_usage.rb +0 -85
- data/lib/phronomy/multi_agent/persistence/queries.rb +0 -30
- data/lib/phronomy/multi_agent/storage_contract/team_storage_schema.rb +0 -15
- data/lib/phronomy/output_parser/contract/parse_error.rb +0 -7
- data/lib/phronomy/persistence/api/persistence.rb +0 -140
- data/lib/phronomy/persistence_composition/repositories.rb +0 -77
- data/lib/phronomy/recovery/recovery.rb +0 -186
- data/lib/phronomy/tool/contract/tool_error.rb +0 -7
- data/lib/phronomy/vector_store/embeddings/base.rb +0 -44
- data/lib/phronomy/vector_store/embeddings/ruby_llm_embeddings.rb +0 -47
- data/lib/phronomy/workflow/phase_machine_builder.rb +0 -205
- data/lib/phronomy/workflow/storage_contract/workflow_storage_schema.rb +0 -9
- data/sig/phronomy/execution_receiver.rbs +0 -34
- data/sig/phronomy/llm_adapter.rbs +0 -11
- data/sig/phronomy/runtime.rbs +0 -76
- /data/lib/phronomy/agent/{context_assembly/context_importer.rb → context_importer.rb} +0 -0
- /data/lib/phronomy/{engine → execution}/backpressure_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/cancellation_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/concurrency/worker_input_restricted.rb +0 -0
- /data/lib/phronomy/{engine → execution}/event_loop_reentrancy_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/invalid_async_entry_action_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/invalid_async_transition_action_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/invalid_async_workflow_action_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/pool_shutdown_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/recursion_limit_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/runtime_shutdown_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/runtime_shutdown_reentrancy_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/scheduler_reentrancy_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/timeout_error.rb +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "worker_input_restricted"
|
|
3
|
+
require_relative "../../execution/concurrency/worker_input_restricted"
|
|
4
4
|
|
|
5
5
|
module Phronomy
|
|
6
6
|
module Concurrency
|
|
@@ -81,14 +81,12 @@ module Phronomy
|
|
|
81
81
|
@submitted_at = submitted_at ||
|
|
82
82
|
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
83
83
|
@mutex = Mutex.new
|
|
84
|
-
@timer_subscriptions =
|
|
84
|
+
@timer_subscriptions = ResultSubscriptions.new
|
|
85
|
+
@cancellation_subscriptions = ResultSubscriptions.new
|
|
85
86
|
|
|
86
87
|
# Explicit submit cancellation is operation-wide. Deadline-only tokens are
|
|
87
88
|
# promoted to cancel! by OffloadPool#submit using the Runtime timer queue.
|
|
88
|
-
@
|
|
89
|
-
-> { fire_cancellation! }
|
|
90
|
-
end
|
|
91
|
-
@cancellation_token&.on_cancel(&@cancellation_callback)
|
|
89
|
+
@cancellation_subscriptions.explicit_cancellation(@cancellation_token) { fire_cancellation! }
|
|
92
90
|
end
|
|
93
91
|
|
|
94
92
|
# @return [Boolean] true when caller-facing settlement has been claimed
|
|
@@ -260,7 +258,7 @@ module Phronomy
|
|
|
260
258
|
def notify_abandoned
|
|
261
259
|
@on_abandoned&.call(self)
|
|
262
260
|
rescue => error
|
|
263
|
-
Phronomy.
|
|
261
|
+
Phronomy::RuntimeSettings.current.logger&.error do
|
|
264
262
|
"OffloadPool abandoned callback failed: #{error.class}: #{error.message}"
|
|
265
263
|
end
|
|
266
264
|
end
|
|
@@ -293,13 +291,7 @@ module Phronomy
|
|
|
293
291
|
|
|
294
292
|
def detach_submit_cancellation
|
|
295
293
|
@timer_subscriptions.close
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
@cancellation_token.send(
|
|
299
|
-
:unregister_cancel_callback,
|
|
300
|
-
@cancellation_callback
|
|
301
|
-
)
|
|
302
|
-
@cancellation_callback = nil
|
|
294
|
+
@cancellation_subscriptions.close
|
|
303
295
|
end
|
|
304
296
|
end
|
|
305
297
|
private_constant :Operation
|
|
@@ -384,7 +376,7 @@ module Phronomy
|
|
|
384
376
|
full_timeout: nil,
|
|
385
377
|
&block
|
|
386
378
|
)
|
|
387
|
-
if Phronomy::
|
|
379
|
+
if Phronomy::WaitPolicy.blocking_forbidden? && on_full != :raise
|
|
388
380
|
raise Phronomy::EventLoopReentrancyError,
|
|
389
381
|
"OffloadPool admission cannot wait on EventLoop; use on_full: :raise"
|
|
390
382
|
end
|
|
@@ -491,19 +483,42 @@ module Phronomy
|
|
|
491
483
|
task
|
|
492
484
|
end
|
|
493
485
|
|
|
494
|
-
#
|
|
495
|
-
#
|
|
486
|
+
# Close admission without waiting for worker termination. Already queued
|
|
487
|
+
# operations drain normally; running workers are never interrupted.
|
|
488
|
+
# @api private
|
|
489
|
+
def begin_shutdown
|
|
490
|
+
@shutdown = true
|
|
491
|
+
@queue.close
|
|
492
|
+
self
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
# Whether shutdown has closed admission and all workers have exited.
|
|
496
|
+
# Logical TaskResult settlement alone does not establish this fact.
|
|
497
|
+
# @api private
|
|
498
|
+
def terminated?
|
|
499
|
+
@shutdown && @workers.none?(&:alive?)
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
# Gracefully drains the pool and waits for worker threads to terminate.
|
|
503
|
+
# All workers share +drain_timeout+ or the supplied monotonic +deadline+.
|
|
504
|
+
# A deadline expiry leaves workers running; callers inspect terminated?.
|
|
496
505
|
#
|
|
497
506
|
# Closing the underlying SizedQueue signals workers to exit after draining
|
|
498
507
|
# remaining items, without blocking on a full-queue push.
|
|
499
508
|
#
|
|
500
509
|
# @param drain_timeout [Numeric] seconds to wait for workers to finish
|
|
510
|
+
# @param deadline [Numeric, nil] absolute monotonic shutdown deadline
|
|
501
511
|
# @return [self]
|
|
502
512
|
# @api private
|
|
503
|
-
def shutdown(drain_timeout: 30)
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
@workers.each
|
|
513
|
+
def shutdown(drain_timeout: 30, deadline: nil)
|
|
514
|
+
deadline ||= Process.clock_gettime(Process::CLOCK_MONOTONIC) + drain_timeout
|
|
515
|
+
begin_shutdown
|
|
516
|
+
@workers.each do |thread|
|
|
517
|
+
remaining = deadline - Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
518
|
+
break if remaining <= 0
|
|
519
|
+
|
|
520
|
+
thread.join(remaining)
|
|
521
|
+
end
|
|
507
522
|
self
|
|
508
523
|
end
|
|
509
524
|
|
|
@@ -16,6 +16,7 @@ module Phronomy
|
|
|
16
16
|
@mutex = Mutex.new
|
|
17
17
|
@pools = {}
|
|
18
18
|
@default = nil
|
|
19
|
+
@closed = false
|
|
19
20
|
end
|
|
20
21
|
|
|
21
22
|
# Returns (or lazily creates) the unnamed default pool.
|
|
@@ -24,12 +25,22 @@ module Phronomy
|
|
|
24
25
|
# @return [OffloadPool]
|
|
25
26
|
# @api private
|
|
26
27
|
def default_pool(pool_size: 10, queue_size: 100)
|
|
27
|
-
@
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
28
|
+
@mutex.synchronize do
|
|
29
|
+
ensure_open!
|
|
30
|
+
@default ||= OffloadPool.new(
|
|
31
|
+
name: :default,
|
|
32
|
+
pool_size: pool_size,
|
|
33
|
+
queue_size: queue_size,
|
|
34
|
+
timer_queue_provider: @timer_queue_provider
|
|
35
|
+
)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Lookup only, including after registration has closed.
|
|
40
|
+
# @return [OffloadPool, nil]
|
|
41
|
+
# @api private
|
|
42
|
+
def default_pool_if_initialized
|
|
43
|
+
@mutex.synchronize { @default }
|
|
33
44
|
end
|
|
34
45
|
|
|
35
46
|
# Returns (or lazily creates) a named pool.
|
|
@@ -40,6 +51,7 @@ module Phronomy
|
|
|
40
51
|
# @api private
|
|
41
52
|
def named_pool(name, size: 10, queue_size: 100)
|
|
42
53
|
@mutex.synchronize do
|
|
54
|
+
ensure_open!
|
|
43
55
|
@pools[name.to_sym] ||= OffloadPool.new(
|
|
44
56
|
name: name,
|
|
45
57
|
pool_size: size,
|
|
@@ -49,13 +61,36 @@ module Phronomy
|
|
|
49
61
|
end
|
|
50
62
|
end
|
|
51
63
|
|
|
52
|
-
#
|
|
53
|
-
#
|
|
64
|
+
# Closes registration and every pool before waiting, outside the registry
|
|
65
|
+
# lock. All pools share one absolute monotonic deadline. Attempt every
|
|
66
|
+
# cleanup even if one fails; report physical completion separately.
|
|
67
|
+
# @return [Boolean] whether every owned worker has stopped
|
|
54
68
|
# @api private
|
|
55
|
-
def shutdown
|
|
56
|
-
@
|
|
57
|
-
|
|
58
|
-
|
|
69
|
+
def shutdown(deadline: Process.clock_gettime(Process::CLOCK_MONOTONIC) + 30)
|
|
70
|
+
pools = @mutex.synchronize do
|
|
71
|
+
@closed = true
|
|
72
|
+
[@default, *@pools.values].compact
|
|
73
|
+
end
|
|
74
|
+
error = nil
|
|
75
|
+
pools.each do |pool|
|
|
76
|
+
pool.begin_shutdown
|
|
77
|
+
rescue => caught
|
|
78
|
+
error ||= caught
|
|
79
|
+
end
|
|
80
|
+
pools.each do |pool|
|
|
81
|
+
pool.shutdown(deadline: deadline)
|
|
82
|
+
rescue => caught
|
|
83
|
+
error ||= caught
|
|
84
|
+
end
|
|
85
|
+
raise error if error
|
|
86
|
+
|
|
87
|
+
pools.all?(&:terminated?)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
private
|
|
91
|
+
|
|
92
|
+
def ensure_open!
|
|
93
|
+
raise Phronomy::PoolShutdownError, "pool registry has been shut down" if @closed
|
|
59
94
|
end
|
|
60
95
|
end
|
|
61
96
|
end
|
|
@@ -24,7 +24,7 @@ module Phronomy
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def self.assert_not_in_event_loop!
|
|
27
|
-
return unless Phronomy::
|
|
27
|
+
return unless Phronomy::WaitPolicy.blocking_forbidden?
|
|
28
28
|
|
|
29
29
|
raise Phronomy::EventLoopReentrancyError,
|
|
30
30
|
"Blocking invoke called from inside an EventLoop action. Use invoke_async instead."
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "concurrency/worker_input_restricted"
|
|
3
|
+
require_relative "../execution/concurrency/worker_input_restricted"
|
|
4
4
|
|
|
5
5
|
module Phronomy
|
|
6
6
|
# Runtime-owned FIFO event loop for FSMSession instances.
|
|
@@ -53,7 +53,7 @@ module Phronomy
|
|
|
53
53
|
@dispatch_count = 0
|
|
54
54
|
@total_lag_ns = 0
|
|
55
55
|
|
|
56
|
-
@thread = Thread.new { run_loop }
|
|
56
|
+
@thread = Thread.new { Phronomy::WaitPolicy.without_blocking { run_loop } }
|
|
57
57
|
@thread.name = "phronomy-event-loop"
|
|
58
58
|
end
|
|
59
59
|
|
|
@@ -632,7 +632,7 @@ module Phronomy
|
|
|
632
632
|
end
|
|
633
633
|
|
|
634
634
|
def warn_queue_backlog(message)
|
|
635
|
-
logger = Phronomy.
|
|
635
|
+
logger = Phronomy::RuntimeSettings.current.logger
|
|
636
636
|
logger ? logger.warn(message) : Kernel.warn(message)
|
|
637
637
|
rescue
|
|
638
638
|
nil
|
|
@@ -648,11 +648,11 @@ module Phronomy
|
|
|
648
648
|
end
|
|
649
649
|
|
|
650
650
|
def check_starvation_lag(lag_ns, event)
|
|
651
|
-
threshold = Phronomy.
|
|
651
|
+
threshold = Phronomy::RuntimeSettings.current.event_loop_starvation_threshold_seconds
|
|
652
652
|
return unless threshold
|
|
653
653
|
return unless lag_ns > (threshold * 1_000_000_000)
|
|
654
654
|
|
|
655
|
-
Phronomy.
|
|
655
|
+
Phronomy::RuntimeSettings.current.logger&.warn do
|
|
656
656
|
"[Phronomy::EventLoop] Starvation detected: event #{event.type.inspect} " \
|
|
657
657
|
"for target #{event.target_id.inspect} waited " \
|
|
658
658
|
"#{format("%.3f", lag_ns / 1_000_000_000.0)}s in queue " \
|
|
@@ -661,13 +661,13 @@ module Phronomy
|
|
|
661
661
|
end
|
|
662
662
|
|
|
663
663
|
def check_dispatch_time(dispatch_start_ns, event)
|
|
664
|
-
threshold = Phronomy.
|
|
664
|
+
threshold = Phronomy::RuntimeSettings.current.event_loop_dispatch_threshold_seconds
|
|
665
665
|
return unless threshold
|
|
666
666
|
|
|
667
667
|
elapsed_ns = monotonic_nanoseconds - dispatch_start_ns
|
|
668
668
|
return unless elapsed_ns > (threshold * 1_000_000_000)
|
|
669
669
|
|
|
670
|
-
Phronomy.
|
|
670
|
+
Phronomy::RuntimeSettings.current.logger&.warn do
|
|
671
671
|
"[Phronomy::EventLoop] Long dispatch: event #{event.type.inspect} " \
|
|
672
672
|
"for target #{event.target_id.inspect} took " \
|
|
673
673
|
"#{format("%.3f", elapsed_ns / 1_000_000_000.0)}s on the EventLoop thread " \
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
# Engine implementation of the Execution mechanism protocol. Installing this
|
|
5
|
+
# binding does not acquire any resources; default Runtime is resolved on use.
|
|
6
|
+
# @api private
|
|
7
|
+
module ExecutionBinding
|
|
8
|
+
def self.submit(pool: nil, runtime: nil, pool_name: nil, size: 10,
|
|
9
|
+
queue_size: 100, **options, &operation)
|
|
10
|
+
unless pool
|
|
11
|
+
runtime ||= Runtime.instance
|
|
12
|
+
pool = pool_name ? runtime.pool(pool_name, size: size, queue_size: queue_size) : runtime.offload
|
|
13
|
+
end
|
|
14
|
+
pool.submit(**options, &operation)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.after(seconds, &callback)
|
|
18
|
+
timer = Runtime.instance.timer_queue
|
|
19
|
+
timer.schedule(seconds: seconds, &callback)
|
|
20
|
+
-> { timer.cancel(callback) }
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
# Concrete EventLoop transport and lifecycle registration for execution state.
|
|
5
|
+
# @api private
|
|
6
|
+
class ExecutionReceiverBinding
|
|
7
|
+
def self.receiver(receiver_class, event_loop)
|
|
8
|
+
event_loop.__execution_receiver(key: receiver_class) ||
|
|
9
|
+
event_loop.__register_execution_receiver(key: receiver_class,
|
|
10
|
+
receiver: receiver_class.new(channel: new(event_loop)))
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.existing_receiver(receiver_class, runtime)
|
|
14
|
+
runtime.__event_loop_if_initialized&.__execution_receiver(key: receiver_class)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def initialize(event_loop)
|
|
18
|
+
@event_loop = event_loop
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def bound_to?(owner) = @event_loop.equal?(owner)
|
|
22
|
+
def current? = @event_loop.current?
|
|
23
|
+
|
|
24
|
+
def synchronize(&block)
|
|
25
|
+
@event_loop.__synchronize_execution_state(&block)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def admit(receiver, &block)
|
|
29
|
+
@event_loop.__admit_execution(receiver, &block)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def post(receiver, message, admission:, completion:)
|
|
33
|
+
@event_loop.__post_execution(receiver, message, admission: admission, completion: completion)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def route(receiver, type:, payload:)
|
|
37
|
+
@event_loop.__route_execution_event(receiver) do
|
|
38
|
+
target = yield
|
|
39
|
+
target && Phronomy::Event.new(type: type, target_id: target, payload: payload)
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -1,14 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Phronomy
|
|
4
|
-
#
|
|
5
|
-
# This protocol does not depend on a particular runner or session instance.
|
|
4
|
+
# Internal terminal vocabulary shared only by FSM adapters and sessions.
|
|
6
5
|
# @api private
|
|
7
6
|
module FSMProtocol
|
|
8
7
|
FINISH = :__end__
|
|
9
|
-
|
|
10
|
-
# A domain policy permits completion, reports failure, or retires a session
|
|
11
|
-
# without settling its caller. It never carries live session authority.
|
|
12
|
-
TerminalDecision = Data.define(:action, :error)
|
|
13
8
|
end
|
|
14
9
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "concurrency/worker_input_restricted"
|
|
3
|
+
require_relative "../execution/concurrency/worker_input_restricted"
|
|
4
4
|
|
|
5
5
|
require "securerandom"
|
|
6
6
|
|
|
@@ -407,7 +407,7 @@ module Phronomy
|
|
|
407
407
|
)
|
|
408
408
|
return if accepted
|
|
409
409
|
|
|
410
|
-
Phronomy.
|
|
410
|
+
Phronomy::RuntimeSettings.current.logger&.warn(
|
|
411
411
|
"[Phronomy::FSMSession] EventLoop rejected recovery-required event for #{@id}"
|
|
412
412
|
)
|
|
413
413
|
end
|
|
@@ -422,7 +422,7 @@ module Phronomy
|
|
|
422
422
|
)
|
|
423
423
|
return if accepted
|
|
424
424
|
|
|
425
|
-
Phronomy.
|
|
425
|
+
Phronomy::RuntimeSettings.current.logger&.warn(
|
|
426
426
|
"[Phronomy::FSMSession] EventLoop rejected terminal event " \
|
|
427
427
|
"#{type.inspect} for #{@id}"
|
|
428
428
|
)
|
|
@@ -2,24 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
module Phronomy
|
|
4
4
|
# Runtime observability snapshot for the two concurrency boundaries:
|
|
5
|
-
# EventLoop and OffloadPool.
|
|
5
|
+
# EventLoop and the default OffloadPool. Observation never creates resources;
|
|
6
|
+
# missing resources contribute zero values. Individual counters are sampled
|
|
7
|
+
# independently, not as an atomic view of concurrent execution.
|
|
6
8
|
module Metrics
|
|
7
9
|
def self.snapshot
|
|
8
|
-
runtime = Runtime.
|
|
9
|
-
pool = runtime
|
|
10
|
-
event_loop = runtime
|
|
10
|
+
runtime = Runtime.__default_if_initialized
|
|
11
|
+
pool = runtime&.__offload_if_initialized
|
|
12
|
+
event_loop = runtime&.__event_loop_if_initialized
|
|
11
13
|
|
|
12
14
|
{
|
|
13
|
-
offload_pool_active: pool
|
|
14
|
-
offload_pool_queue_length: pool
|
|
15
|
-
offload_pool_abandoned_active: pool
|
|
16
|
-
offload_pool_abandoned_total: pool
|
|
17
|
-
offload_pool_size: pool
|
|
18
|
-
event_loop_queue_depth: event_loop
|
|
19
|
-
event_loop_queue_max_depth: event_loop
|
|
20
|
-
event_loop_lag_last_ms: (event_loop
|
|
21
|
-
event_loop_lag_max_ms: (event_loop
|
|
22
|
-
event_loop_lag_average_ms: (event_loop
|
|
15
|
+
offload_pool_active: pool&.active_count || 0,
|
|
16
|
+
offload_pool_queue_length: pool&.queue_depth || 0,
|
|
17
|
+
offload_pool_abandoned_active: pool&.abandoned_active_count || 0,
|
|
18
|
+
offload_pool_abandoned_total: pool&.abandoned_count || 0,
|
|
19
|
+
offload_pool_size: pool&.pool_size || 0,
|
|
20
|
+
event_loop_queue_depth: event_loop&.queue_depth || 0,
|
|
21
|
+
event_loop_queue_max_depth: event_loop&.max_queue_depth || 0,
|
|
22
|
+
event_loop_lag_last_ms: ((event_loop&.last_lag_seconds || 0.0) * 1000).round(3),
|
|
23
|
+
event_loop_lag_max_ms: ((event_loop&.max_lag_seconds || 0.0) * 1000).round(3),
|
|
24
|
+
event_loop_lag_average_ms: ((event_loop&.average_lag_seconds || 0.0) * 1000).round(3)
|
|
23
25
|
}
|
|
24
26
|
end
|
|
25
27
|
end
|
|
@@ -68,7 +68,7 @@ module Phronomy
|
|
|
68
68
|
callbacks.each do |callback|
|
|
69
69
|
callback.call
|
|
70
70
|
rescue => error
|
|
71
|
-
Phronomy.
|
|
71
|
+
Phronomy::RuntimeSettings.current.logger&.error do
|
|
72
72
|
"[TimerQueue] callback raised #{error.class}: #{error.message}"
|
|
73
73
|
end
|
|
74
74
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require_relative "concurrency/worker_input_restricted"
|
|
3
|
+
require_relative "../execution/concurrency/worker_input_restricted"
|
|
4
4
|
|
|
5
5
|
require_relative "runtime/timer_queue"
|
|
6
6
|
require_relative "runtime/shutdown_result"
|
|
@@ -17,10 +17,16 @@ module Phronomy
|
|
|
17
17
|
instance_mutex.synchronize { @instance ||= new }
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
# Lookup only: diagnostics must not initialize the default Runtime.
|
|
21
|
+
# @api private
|
|
22
|
+
def __default_if_initialized
|
|
21
23
|
instance_mutex.synchronize { @instance }
|
|
22
24
|
end
|
|
23
25
|
|
|
26
|
+
def default_if_initialized_for_test
|
|
27
|
+
__default_if_initialized
|
|
28
|
+
end
|
|
29
|
+
|
|
24
30
|
def replace_default_for_test(runtime)
|
|
25
31
|
instance_mutex.synchronize do
|
|
26
32
|
previous = @instance
|
|
@@ -33,7 +39,7 @@ module Phronomy
|
|
|
33
39
|
instance_mutex.synchronize { @instance = runtime }
|
|
34
40
|
end
|
|
35
41
|
|
|
36
|
-
def reset_default!(timeout: Phronomy.
|
|
42
|
+
def reset_default!(timeout: Phronomy::RuntimeSettings.current.event_loop_stop_grace_seconds)
|
|
37
43
|
runtime = instance_mutex.synchronize { @instance }
|
|
38
44
|
return ShutdownResult.not_started unless runtime
|
|
39
45
|
|
|
@@ -50,8 +56,7 @@ module Phronomy
|
|
|
50
56
|
end
|
|
51
57
|
|
|
52
58
|
def in_event_loop_context?
|
|
53
|
-
|
|
54
|
-
runtime&.event_loop_current? || false
|
|
59
|
+
Phronomy::WaitPolicy.blocking_forbidden?
|
|
55
60
|
end
|
|
56
61
|
|
|
57
62
|
private
|
|
@@ -81,8 +86,8 @@ module Phronomy
|
|
|
81
86
|
end
|
|
82
87
|
|
|
83
88
|
def offload(
|
|
84
|
-
pool_size: Phronomy.
|
|
85
|
-
queue_size: Phronomy.
|
|
89
|
+
pool_size: Phronomy::RuntimeSettings.current.offload_pool_size,
|
|
90
|
+
queue_size: Phronomy::RuntimeSettings.current.offload_queue_size
|
|
86
91
|
)
|
|
87
92
|
ensure_accepting_work!
|
|
88
93
|
@pool_registry.default_pool(pool_size: pool_size, queue_size: queue_size)
|
|
@@ -93,6 +98,12 @@ module Phronomy
|
|
|
93
98
|
@pool_registry.named_pool(name, size: size, queue_size: queue_size)
|
|
94
99
|
end
|
|
95
100
|
|
|
101
|
+
# Lookup only; retained pools remain observable during and after shutdown.
|
|
102
|
+
# @api private
|
|
103
|
+
def __offload_if_initialized
|
|
104
|
+
@pool_registry.default_pool_if_initialized
|
|
105
|
+
end
|
|
106
|
+
|
|
96
107
|
def timer_queue
|
|
97
108
|
ensure_accepting_work!
|
|
98
109
|
timer = @timer_service.timer_queue
|
|
@@ -177,7 +188,7 @@ module Phronomy
|
|
|
177
188
|
end
|
|
178
189
|
|
|
179
190
|
def shutdown(
|
|
180
|
-
timeout: Phronomy.
|
|
191
|
+
timeout: Phronomy::RuntimeSettings.current.event_loop_stop_grace_seconds,
|
|
181
192
|
cancel_grace: timeout
|
|
182
193
|
)
|
|
183
194
|
if event_loop_current?
|
|
@@ -212,13 +223,14 @@ module Phronomy
|
|
|
212
223
|
:not_started
|
|
213
224
|
end
|
|
214
225
|
|
|
215
|
-
subsystem_error = shutdown_pools_and_timer
|
|
226
|
+
pools_stopped, subsystem_error = shutdown_pools_and_timer(deadline: stop_deadline)
|
|
216
227
|
participant_error = @lifecycle_mutex.synchronize { @shutdown_participant_error }
|
|
217
228
|
cleanup_complete = participants_idle && participant_error.nil? &&
|
|
218
229
|
loop_idle &&
|
|
219
230
|
(!loop_instance || !loop_instance.thread_alive?) &&
|
|
220
231
|
event_loop_status != :cancel_timeout &&
|
|
221
232
|
(!loop_instance || loop_instance.__receiver_cleanup_complete?) &&
|
|
233
|
+
pools_stopped &&
|
|
222
234
|
subsystem_error.nil?
|
|
223
235
|
|
|
224
236
|
cleanup_complete = finalize_participants(participants) if cleanup_complete
|
|
@@ -294,10 +306,11 @@ module Phronomy
|
|
|
294
306
|
"Runtime is #{current_state}; new work is not accepted"
|
|
295
307
|
end
|
|
296
308
|
|
|
297
|
-
def shutdown_pools_and_timer
|
|
309
|
+
def shutdown_pools_and_timer(deadline:)
|
|
298
310
|
error = nil
|
|
311
|
+
pools_stopped = false
|
|
299
312
|
begin
|
|
300
|
-
@pool_registry.shutdown
|
|
313
|
+
pools_stopped = @pool_registry.shutdown(deadline: deadline)
|
|
301
314
|
rescue => caught
|
|
302
315
|
error ||= caught
|
|
303
316
|
ensure
|
|
@@ -307,7 +320,7 @@ module Phronomy
|
|
|
307
320
|
error ||= caught
|
|
308
321
|
end
|
|
309
322
|
end
|
|
310
|
-
error
|
|
323
|
+
[pools_stopped, error]
|
|
311
324
|
end
|
|
312
325
|
|
|
313
326
|
def validate_timeout!(value, name)
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
# Completion adaptation for framework operations. Owns start-error capture,
|
|
5
|
+
# once-only propagation, error-to-value policy and physical completion tracking.
|
|
6
|
+
# Callers supply domain transformations, never completion setters or callbacks.
|
|
7
|
+
# @api private
|
|
8
|
+
module AsyncOperation
|
|
9
|
+
def self.map(source, name:, on_error: nil, &transform)
|
|
10
|
+
raise ArgumentError, "async mapping requires a block" unless transform
|
|
11
|
+
unless source.respond_to?(:on_complete)
|
|
12
|
+
raise TypeError, "asynchronous operation must return a completion handle"
|
|
13
|
+
end
|
|
14
|
+
Concurrency::ResultComposition.new(source, flatten: false, name: name,
|
|
15
|
+
completion_only: true, recover: on_error, &transform).start
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Capture synchronous preparation/start errors under the same domain policy.
|
|
19
|
+
def self.call(name:, on_error: nil, transform: ->(value) { value })
|
|
20
|
+
source = begin
|
|
21
|
+
yield
|
|
22
|
+
rescue => error
|
|
23
|
+
TaskResult.failed(error, name: name)
|
|
24
|
+
end
|
|
25
|
+
map(source, name: name, on_error: on_error, &transform)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Short synchronous fallback/validation only; never offloads or awaits work.
|
|
29
|
+
def self.capture(name:, on_error: nil)
|
|
30
|
+
value = yield
|
|
31
|
+
TaskResult.completed(value, name: name)
|
|
32
|
+
rescue => error
|
|
33
|
+
begin
|
|
34
|
+
raise error unless on_error
|
|
35
|
+
TaskResult.completed(on_error.call(error), name: name)
|
|
36
|
+
rescue => failure
|
|
37
|
+
TaskResult.failed(failure, name: name)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -22,10 +22,10 @@ module Phronomy
|
|
|
22
22
|
|
|
23
23
|
begin
|
|
24
24
|
unless invocation_context.nil?
|
|
25
|
-
binding =
|
|
25
|
+
binding = InvocationControls.new(invocation_context: invocation_context,
|
|
26
26
|
cancellation_token: cancellation_token)
|
|
27
27
|
end
|
|
28
|
-
result = Phronomy::
|
|
28
|
+
result = Phronomy::Execution.submit(
|
|
29
29
|
on_full: :raise,
|
|
30
30
|
timeout: timeout,
|
|
31
31
|
cancellation_token: binding ? binding.token : cancellation_token,
|
|
@@ -98,7 +98,7 @@ module Phronomy
|
|
|
98
98
|
def deliver_cancel_callback(callback)
|
|
99
99
|
callback.call
|
|
100
100
|
rescue => error
|
|
101
|
-
Phronomy.
|
|
101
|
+
Phronomy::RuntimeSettings.current.logger&.error do
|
|
102
102
|
"[CancellationToken] on_cancel callback raised #{error.class}: #{error.message}"
|
|
103
103
|
end
|
|
104
104
|
end
|
|
@@ -42,23 +42,6 @@ module Phronomy
|
|
|
42
42
|
remaining = @monotonic_at - Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
43
43
|
[remaining, 0.0].max
|
|
44
44
|
end
|
|
45
|
-
|
|
46
|
-
# Attaches this deadline to a {CancellationToken} by cancelling the token
|
|
47
|
-
# when the deadline expires. The Runtime timer queue is driven by EventLoop;
|
|
48
|
-
# no timer-specific OS thread is created.
|
|
49
|
-
#
|
|
50
|
-
# @param token [CancellationToken]
|
|
51
|
-
# @param timer_queue [Runtime::TimerQueue, nil] queue to register with;
|
|
52
|
-
# defaults to +Phronomy::Runtime.instance.timer_queue+
|
|
53
|
-
# @return [self]
|
|
54
|
-
# @api private
|
|
55
|
-
def attach_to(token, timer_queue: Phronomy::Runtime.instance.timer_queue)
|
|
56
|
-
seconds = remaining_seconds
|
|
57
|
-
return self if seconds <= 0
|
|
58
|
-
|
|
59
|
-
timer_queue.schedule(seconds: seconds) { token.cancel! }
|
|
60
|
-
self
|
|
61
|
-
end
|
|
62
45
|
end
|
|
63
46
|
end
|
|
64
47
|
end
|
|
@@ -68,7 +68,7 @@ module Phronomy
|
|
|
68
68
|
def deliver_physical_callback(callback)
|
|
69
69
|
callback.call
|
|
70
70
|
rescue => error
|
|
71
|
-
Phronomy.
|
|
71
|
+
Phronomy::RuntimeSettings.current.logger&.error do
|
|
72
72
|
"[PhysicalCompletionTask] callback raised #{error.class}: #{error.message}"
|
|
73
73
|
end
|
|
74
74
|
end
|