phronomy 0.26.0 → 0.28.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 +92 -0
- data/CONTRIBUTING.md +8 -8
- data/README.md +13 -3
- data/VERIFY.sh +27 -27
- data/benchmark/bench_agent_invoke.rb +26 -47
- data/benchmark/bench_context_assembler.rb +17 -15
- data/benchmark/bench_regression.rb +1 -1
- data/benchmark/bench_tool_schema.rb +1 -1
- data/docs/architecture/agent-chat-and-state-ownership.md +153 -0
- data/docs/architecture/agent-configuration-and-tool-binding.md +139 -0
- data/docs/architecture/agent-context.md +4 -2
- data/docs/architecture/agent-transition-ownership.md +13 -0
- data/docs/architecture/backend-groups.md +152 -0
- data/docs/architecture/before-llm-input.md +9 -3
- data/docs/architecture/context-management.md +31 -1
- data/docs/architecture/context-preparation-steps.md +90 -0
- data/docs/architecture/entry-action-and-team-wording.md +72 -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 +86 -0
- data/docs/architecture/generator-verifier-ownership.md +111 -0
- data/docs/architecture/multi-agent-handoff.md +11 -4
- data/docs/architecture/persistence-refactoring-plan.md +50 -0
- data/docs/architecture/persistence.md +45 -4
- 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 +343 -0
- data/docs/architecture/remaining-refactoring-plan.md +390 -0
- data/docs/architecture/rubyllm-2-token-ownership.md +82 -0
- data/docs/architecture/storage-execution-boundaries.md +54 -0
- data/docs/architecture/tool-schema-recording-gap.md +56 -0
- data/docs/architecture/tracing.md +2 -2
- data/docs/architecture/workflow-terminal-ownership-design.md +104 -0
- data/docs/architecture.md +222 -0
- data/docs/decisions/014-unified-persistence-durable-state.md +11 -0
- data/docs/decisions/015-tool-public-facade-and-rbs-boundary.md +3 -3
- data/docs/decisions/024-event-loop-single-writer-agent-runtime.md +5 -1
- data/docs/decisions/025-process-local-agent-ownership-and-runtime-admission.md +13 -3
- data/docs/decisions/026-workflow-runtime-admission-and-durable-terminal-barrier.md +5 -1
- data/docs/decisions/027-llm-adapter-provider-boundary.md +2 -0
- data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +7 -0
- data/docs/decisions/031-durable-multi-agent-coordination.md +7 -0
- data/docs/decisions/032-storage-backend-composition.md +85 -0
- data/docs/decisions/033-domain-persistence-ownership.md +79 -0
- data/docs/decisions/034-handoff-runner-coordination-ownership.md +78 -0
- data/docs/decisions/035-tool-executor-capability-ownership.md +68 -0
- data/docs/decisions/036-context-contract-ownership.md +86 -0
- data/docs/decisions/037-common-definition-ownership.md +62 -0
- data/docs/decisions/038-responsibility-based-source-layout.md +145 -0
- data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +82 -0
- data/docs/decisions/040-configuration-default-composition.md +105 -0
- data/docs/decisions/041-feature-owned-identity-registries.md +100 -0
- data/docs/decisions/042-feature-owned-execution-state.md +106 -0
- data/docs/decisions/043-storage-execution-constraint-notifications.md +75 -0
- data/docs/decisions/044-agent-default-and-one-shot-composition.md +106 -0
- data/docs/decisions/045-worker-input-restriction-ownership.md +104 -0
- data/docs/decisions/046-agent-responsibility-layout-and-shared-records.md +110 -0
- data/docs/decisions/047-recovered-execution-continuation-contract.md +116 -0
- data/docs/decisions/048-dispatch-preparation-worker-ownership.md +120 -0
- data/docs/decisions/049-initial-preparation-worker-ownership.md +111 -0
- data/docs/decisions/050-approval-resume-snapshot-and-commit-ownership.md +98 -0
- data/docs/decisions/051-execution-outcome-worker-ownership.md +153 -0
- data/docs/decisions/052-tool-invocation-restoration-ownership.md +92 -0
- data/docs/decisions/053-shared-state-coordination-ownership.md +71 -0
- data/docs/decisions/054-workflow-terminal-save-single-owner.md +76 -0
- data/docs/decisions/055-terminal-observer-failure-settlement.md +60 -0
- data/docs/decisions/056-workflow-terminal-policy-ownership.md +87 -0
- data/docs/decisions/057-storage-transaction-boundaries.md +73 -0
- data/docs/decisions/058-neutral-storage-primitives.md +83 -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 +52 -6
- data/docs/design/durable-semantic-coordination/IMPLEMENTATION_DESIGN_V2.md +6 -0
- data/docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md +6 -0
- data/docs/execution-boundaries-preview.md +45 -0
- data/docs/features.md +16 -5
- data/docs/getting-started.md +11 -8
- data/docs/migrations/0.15.md +5 -0
- data/docs/migrations/0.22.md +2 -2
- data/docs/migrations/durable-semantic-coordination-v2.md +8 -0
- data/docs/migrations/handoff-runner-multi-agent.md +37 -0
- data/docs/migrations/neutral-storage-spi.md +43 -0
- data/docs/migrations/parallel-tool-chat-removal.md +49 -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/shared-state-multi-agent.md +44 -0
- data/docs/migrations/storage-async-client.md +103 -0
- data/docs/migrations/storage-backend-composition.md +117 -0
- data/docs/migrations/storage-transaction-boundaries.md +74 -0
- data/docs/migrations/vector-async-clients.md +110 -0
- data/docs/persistence-backends.md +190 -215
- data/docs/runtime-and-concurrency.md +142 -32
- data/lib/phronomy/agent/admission.rb +78 -0
- data/lib/phronomy/{agent_already_exists_error.rb → agent/agent_already_exists_error.rb} +2 -0
- data/lib/phronomy/{agent_busy_error.rb → agent/agent_busy_error.rb} +2 -0
- data/lib/phronomy/{agent_purged_error.rb → agent/agent_purged_error.rb} +2 -0
- data/lib/phronomy/agent/api/agent.rb +17 -0
- data/lib/phronomy/{execution_rehydration_required_error.rb → agent/api/execution_rehydration_required_error.rb} +2 -0
- data/lib/phronomy/agent/async_event_api.rb +38 -33
- data/lib/phronomy/agent/base.rb +106 -294
- data/lib/phronomy/agent/before_llm_input.rb +64 -0
- data/lib/phronomy/{agent.rb → agent/composition/run_once.rb} +4 -13
- data/lib/phronomy/agent/context_candidate_resolver.rb +2 -2
- data/lib/phronomy/agent/context_importer.rb +2 -2
- data/lib/phronomy/agent/{context_assembler.rb → context_preparation.rb} +157 -288
- data/lib/phronomy/agent/control_request.rb +21 -0
- data/lib/phronomy/agent/{agent_execution.rb → execution/agent_execution.rb} +8 -4
- data/lib/phronomy/agent/{agent_invocation.rb → execution/agent_invocation.rb} +32 -63
- data/lib/phronomy/agent/execution/approval_resume_commit.rb +108 -0
- data/lib/phronomy/agent/execution/dispatch_preparation.rb +195 -0
- data/lib/phronomy/agent/{exact_execution.rb → execution/exact_execution.rb} +43 -23
- data/lib/phronomy/agent/{execution_cancellation.rb → execution/execution_cancellation.rb} +8 -5
- data/lib/phronomy/agent/execution/execution_coordinator.rb +1744 -0
- data/lib/phronomy/agent/execution/execution_failure.rb +30 -0
- data/lib/phronomy/agent/execution/execution_metadata.rb +54 -0
- data/lib/phronomy/agent/execution/execution_outcome_committer.rb +288 -0
- data/lib/phronomy/agent/execution/execution_registry.rb +463 -0
- data/lib/phronomy/agent/execution/execution_session_runner.rb +113 -0
- data/lib/phronomy/agent/execution/initial_preparation.rb +323 -0
- data/lib/phronomy/agent/execution/invocation_actions.rb +368 -0
- data/lib/phronomy/agent/execution/invocation_transitions.rb +124 -0
- data/lib/phronomy/agent/execution/runtime_record_encoder.rb +210 -0
- 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 +182 -0
- data/lib/phronomy/agent/{handoff_context.rb → handoff/transfer_context.rb} +6 -6
- data/lib/phronomy/agent/{handoff_projection.rb → handoff/transfer_projection.rb} +14 -33
- data/lib/phronomy/agent/handoff_error.rb +7 -0
- data/lib/phronomy/agent/{journal_projection.rb → journal/journal_projection.rb} +4 -0
- data/lib/phronomy/agent/{journal_record.rb → journal/journal_record.rb} +3 -3
- data/lib/phronomy/agent/{llm_call_record.rb → journal/llm_call_record.rb} +2 -2
- data/lib/phronomy/agent/knowledge_item.rb +13 -0
- data/lib/phronomy/agent/{agent_root.rb → lifecycle/agent_root.rb} +6 -2
- data/lib/phronomy/agent/lifecycle/default_persistence.rb +29 -0
- data/lib/phronomy/{engine/runtime/agent_ownership_registry.rb → agent/lifecycle/ownership_registry.rb} +24 -15
- data/lib/phronomy/agent/persistence/agent_repository.rb +69 -0
- data/lib/phronomy/agent/persistence/cancellation_repository.rb +51 -0
- data/lib/phronomy/agent/persistence/codec.rb +338 -0
- data/lib/phronomy/agent/persistence/execution_repository.rb +124 -0
- data/lib/phronomy/agent/persistence/journal_repository.rb +63 -0
- 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 +29 -0
- data/lib/phronomy/agent/persistence/watermark.rb +29 -0
- data/lib/phronomy/agent/recovery/invocation_restorer.rb +124 -0
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/continuation.rb +28 -51
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/installation.rb +50 -55
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/resolution.rb +38 -43
- data/lib/phronomy/agent/{recovery_coordinator.rb → recovery/recovery_coordinator.rb} +26 -23
- data/lib/phronomy/agent/recovery/recovery_rules.rb +119 -0
- data/lib/phronomy/agent/recovery/recovery_support.rb +224 -0
- 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/{ruby_llm_materializer.rb → runtime_input.rb} +19 -39
- data/lib/phronomy/agent/saved_context_reader.rb +55 -0
- data/lib/phronomy/agent/settings.rb +47 -0
- data/lib/phronomy/agent/state_writer.rb +166 -0
- data/lib/phronomy/agent/store.rb +253 -0
- data/lib/phronomy/{stream_callback_error.rb → agent/stream_callback_error.rb} +2 -0
- data/lib/phronomy/agent/{approval_evaluation_request.rb → tool_execution/approval_evaluation_request.rb} +1 -12
- data/lib/phronomy/agent/{tool_approval_request.rb → tool_execution/tool_approval_request.rb} +1 -10
- data/lib/phronomy/agent/tool_execution/tool_binding.rb +30 -0
- data/lib/phronomy/agent/{tool_invocation.rb → tool_execution/tool_invocation.rb} +114 -248
- 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/configuration_error.rb +7 -0
- data/lib/phronomy/common/error.rb +5 -0
- data/lib/phronomy/{agent → common/values}/immutable.rb +9 -1
- data/lib/phronomy/common/values/serializable.rb +32 -0
- data/lib/phronomy/configuration/runtime_settings.rb +49 -0
- data/lib/phronomy/content_store/storage_schema.rb +12 -0
- data/lib/phronomy/content_store/stored_contents.rb +44 -0
- data/lib/phronomy/context/assembly.rb +202 -0
- data/lib/phronomy/{context_budget_exceeded_error.rb → context/budget_exceeded_error.rb} +5 -1
- data/lib/phronomy/context/candidate.rb +51 -0
- data/lib/phronomy/context/constraint.rb +47 -0
- data/lib/phronomy/{agent → context}/context_plan.rb +2 -2
- data/lib/phronomy/{agent → context}/context_plan_validator.rb +3 -3
- data/lib/phronomy/{agent → context}/context_policy.rb +1 -1
- data/lib/phronomy/{agent → context}/context_policy_input.rb +7 -7
- data/lib/phronomy/{agent → 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}/llm_input_build_context.rb +2 -2
- data/lib/phronomy/{agent → context}/llm_input_manifest.rb +21 -21
- data/lib/phronomy/{agent → context}/llm_input_patch.rb +3 -3
- 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/backends/ruby_llm_embeddings.rb +48 -0
- data/lib/phronomy/embeddings/base.rb +43 -0
- data/lib/phronomy/embeddings/invalid_result_error.rb +11 -0
- data/lib/phronomy/embeddings/transport_error.rb +11 -0
- data/lib/phronomy/engine/concurrency/offload_pool.rb +39 -20
- data/lib/phronomy/engine/concurrency/pool_registry.rb +47 -12
- data/lib/phronomy/{diagnostics.rb → engine/diagnostics.rb} +1 -1
- data/lib/phronomy/engine/event_loop.rb +164 -630
- 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 +9 -0
- data/lib/phronomy/engine/fsm_session.rb +37 -30
- data/lib/phronomy/engine/metrics.rb +28 -0
- data/lib/phronomy/engine/runtime/timer_queue.rb +1 -1
- data/lib/phronomy/engine/runtime.rb +104 -95
- data/lib/phronomy/engine/terminal_decision.rb +7 -0
- data/lib/phronomy/execution/async_operation.rb +41 -0
- data/lib/phronomy/execution/backpressure_error.rb +7 -0
- data/lib/phronomy/{blocking.rb → execution/blocking.rb} +2 -2
- data/lib/phronomy/execution/cancellation_error.rb +7 -0
- data/lib/phronomy/{engine → execution}/concurrency/cancellation_token.rb +5 -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/execution/concurrency/worker_input_restricted.rb +14 -0
- data/lib/phronomy/execution/event_loop_reentrancy_error.rb +8 -0
- data/lib/phronomy/{execution.rb → execution/execution.rb} +57 -30
- data/lib/phronomy/{execution_cancellation_error.rb → execution/execution_cancellation_error.rb} +2 -0
- data/lib/phronomy/execution/execution_receiver.rb +78 -0
- data/lib/phronomy/execution/execution_scope_state.rb +35 -0
- data/lib/phronomy/{execution_timeout_error.rb → execution/execution_timeout_error.rb} +2 -0
- data/lib/phronomy/{invalid_async_entry_action_error.rb → execution/invalid_async_entry_action_error.rb} +2 -0
- data/lib/phronomy/{invalid_async_transition_action_error.rb → execution/invalid_async_transition_action_error.rb} +2 -0
- data/lib/phronomy/{invalid_async_workflow_action_error.rb → execution/invalid_async_workflow_action_error.rb} +2 -0
- data/lib/phronomy/{invocation_context.rb → execution/invocation_context.rb} +0 -15
- data/lib/phronomy/execution/invocation_controls.rb +73 -0
- data/lib/phronomy/execution/pool_shutdown_error.rb +7 -0
- data/lib/phronomy/execution/recursion_limit_error.rb +7 -0
- data/lib/phronomy/execution/runnable.rb +24 -0
- data/lib/phronomy/execution/runtime_shutdown_error.rb +7 -0
- data/lib/phronomy/execution/runtime_shutdown_reentrancy_error.rb +7 -0
- data/lib/phronomy/execution/scheduler_reentrancy_error.rb +9 -0
- data/lib/phronomy/{engine → execution}/task_result.rb +7 -3
- data/lib/phronomy/execution/timeout_error.rb +7 -0
- data/lib/phronomy/execution/wait_policy.rb +24 -0
- data/lib/phronomy/filter/filter_block_error.rb +14 -0
- data/lib/phronomy/generation/generator_verifier/agent_operation.rb +65 -0
- data/lib/phronomy/generation/generator_verifier/agent_result_receiver.rb +89 -0
- data/lib/phronomy/generation/generator_verifier/default_parser.rb +27 -0
- data/lib/phronomy/generation/generator_verifier/pipeline_state.rb +57 -0
- data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +112 -0
- data/lib/phronomy/generation/generator_verifier.rb +118 -0
- data/lib/phronomy/generation/low_confidence_error.rb +14 -0
- data/lib/phronomy/llm_adapter/async/async_client.rb +47 -0
- data/lib/phronomy/llm_adapter/authentication_error.rb +9 -0
- data/lib/phronomy/llm_adapter/backends/ruby_llm.rb +183 -0
- data/lib/phronomy/llm_adapter/base.rb +49 -68
- data/lib/phronomy/llm_adapter/context_length_error.rb +9 -0
- data/lib/phronomy/llm_adapter/invalid_result_error.rb +10 -0
- data/lib/phronomy/llm_adapter/message.rb +62 -0
- data/lib/phronomy/llm_adapter/rate_limit_error.rb +9 -0
- 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/llm_context_window/token_budget.rb +6 -7
- data/lib/phronomy/multi_agent/admission_registry.rb +15 -2
- 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 → multi_agent}/handoff.rb +2 -2
- data/lib/phronomy/{agent → multi_agent}/handoff_capability_factory.rb +21 -2
- data/lib/phronomy/multi_agent/handoff_participant.rb +79 -0
- data/lib/phronomy/{agent → multi_agent}/handoff_policy.rb +1 -1
- data/lib/phronomy/multi_agent/handoff_runner.rb +156 -0
- data/lib/phronomy/{agent → multi_agent}/handoff_state.rb +2 -2
- data/lib/phronomy/multi_agent/orchestrator.rb +61 -75
- data/lib/phronomy/multi_agent/persistence/codec.rb +75 -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 +124 -0
- data/lib/phronomy/multi_agent/persistence/team_repository.rb +69 -0
- 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/{agent → multi_agent}/shared_state.rb +56 -39
- data/lib/phronomy/multi_agent/store.rb +156 -0
- data/lib/phronomy/multi_agent/team_coordinator.rb +87 -75
- data/lib/phronomy/multi_agent/team_execution.rb +1 -1
- data/lib/phronomy/{engine/runtime → multi_agent}/team_ownership_registry.rb +5 -5
- data/lib/phronomy/multi_agent/team_root.rb +1 -1
- 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 +21 -21
- 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 +29 -0
- 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 +11 -0
- data/lib/phronomy/runtime_composition/configuration.rb +146 -0
- data/lib/phronomy/runtime_composition/configuration_defaults.rb +9 -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/global_runtime.rb +19 -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 +107 -0
- data/lib/phronomy/storage/backends/in_memory.rb +157 -0
- data/lib/phronomy/storage/blob_conflict_error.rb +10 -0
- data/lib/phronomy/storage/blobs.rb +31 -0
- data/lib/phronomy/storage/condition.rb +25 -0
- data/lib/phronomy/storage/condition_failed_error.rb +16 -0
- data/lib/phronomy/storage/conflict_error.rb +9 -0
- data/lib/phronomy/{persistence → storage}/durable_record.rb +12 -12
- data/lib/phronomy/storage/entry.rb +33 -0
- data/lib/phronomy/storage/guard_ref.rb +13 -0
- data/lib/phronomy/storage/not_found_error.rb +9 -0
- data/lib/phronomy/storage/record_codec.rb +177 -0
- data/lib/phronomy/storage/records.rb +61 -0
- data/lib/phronomy/storage/resource.rb +126 -0
- data/lib/phronomy/storage/scope.rb +25 -0
- data/lib/phronomy/storage/serialization_error.rb +9 -0
- data/lib/phronomy/storage/streams.rb +44 -0
- data/lib/phronomy/storage/transaction_error.rb +10 -0
- data/lib/phronomy/storage/unique_constraint_error.rb +17 -0
- data/lib/phronomy/storage/unsupported_backend_error.rb +9 -0
- data/lib/phronomy/storage/validation.rb +53 -0
- data/lib/phronomy/storage/view.rb +131 -0
- 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 +1 -1
- data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +2 -2
- data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +54 -48
- data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +1 -1
- data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +1 -1
- data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +5 -5
- data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +52 -46
- data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +263 -0
- data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +124 -0
- data/lib/phronomy/testing/persistence_contract.rb +4 -0
- data/lib/phronomy/tool/authorization.rb +77 -0
- data/lib/phronomy/tool/base.rb +549 -9
- data/lib/phronomy/tool/call_request.rb +40 -0
- data/lib/phronomy/{agent/tool_definition_set.rb → tool/definition_set.rb} +16 -12
- 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/{agent → tool}/tool_executor.rb +10 -7
- 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 +1 -1
- data/lib/phronomy/tracing/observation.rb +37 -0
- 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.rb → workflow/execution/workflow.rb} +10 -8
- data/lib/phronomy/workflow/execution/workflow_action_rules.rb +67 -0
- data/lib/phronomy/{workflow_context.rb → workflow/execution/workflow_context.rb} +5 -1
- data/lib/phronomy/workflow/execution/workflow_context_ownership_error.rb +7 -0
- data/lib/phronomy/workflow/execution/workflow_execution_environment.rb +27 -0
- data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +186 -0
- data/lib/phronomy/{workflow_runner.rb → workflow/execution/workflow_runner.rb} +145 -115
- data/lib/phronomy/workflow/execution/workflow_settings.rb +28 -0
- data/lib/phronomy/workflow/persistence/codec.rb +153 -0
- data/lib/phronomy/workflow/persistence/state_repository.rb +63 -0
- data/lib/phronomy/workflow/persistence/storage_schema.rb +13 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_engine_environment.rb +38 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb +156 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_terminal_policy.rb +40 -0
- data/lib/phronomy.rb +97 -98
- data/scripts/api_snapshot.rb +36 -16
- data/scripts/storage_spi_snapshot.rb +36 -0
- 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/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.rbs +14 -139
- 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 +17 -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 +0 -21
- data/sig/phronomy/handoff.rbs +12 -4
- 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.rbs +24 -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 +3 -185
- 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/contracts.rbs +160 -0
- data/sig/phronomy/tool.rbs +58 -5
- 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 +14 -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 +1 -1
- data/sig/phronomy.rbs +0 -49
- metadata +462 -130
- data/lib/phronomy/agent/agent_invocation_session_builder.rb +0 -584
- 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 -507
- data/lib/phronomy/agent/context/instruction/prompt_template.rb +0 -102
- 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_coordinator.rb +0 -3151
- data/lib/phronomy/agent/handoff_execution_coordinator.rb +0 -143
- data/lib/phronomy/agent/handoff_request.rb +0 -45
- data/lib/phronomy/agent/handoff_runner.rb +0 -178
- data/lib/phronomy/agent/phase_machine_builder.rb +0 -152
- data/lib/phronomy/agent/provider_call_outcome.rb +0 -106
- data/lib/phronomy/agent/recovery_support.rb +0 -504
- data/lib/phronomy/agent/selection/candidate.rb +0 -53
- data/lib/phronomy/agent/selection/constraint.rb +0 -49
- data/lib/phronomy/agent/token_budget_resolver.rb +0 -70
- data/lib/phronomy/agent/tool_call_intercepted.rb +0 -29
- data/lib/phronomy/agent/tool_invocation_session_builder.rb +0 -276
- data/lib/phronomy/configuration.rb +0 -59
- data/lib/phronomy/engine/concurrency/cancellation_scope.rb +0 -124
- data/lib/phronomy/engine/concurrency/operation_binding.rb +0 -43
- data/lib/phronomy/generator_verifier.rb +0 -369
- data/lib/phronomy/invalid_context_budget_configuration_error.rb +0 -8
- data/lib/phronomy/llm_adapter/ruby_llm.rb +0 -47
- data/lib/phronomy/metrics.rb +0 -26
- data/lib/phronomy/multi_agent/parallel_tool_chat.rb +0 -116
- data/lib/phronomy/persistence/durable_codec.rb +0 -706
- data/lib/phronomy/persistence/in_memory.rb +0 -690
- data/lib/phronomy/persistence/repository_facades.rb +0 -535
- data/lib/phronomy/persistence.rb +0 -276
- data/lib/phronomy/recovery.rb +0 -186
- data/lib/phronomy/ruby_llm_patches.rb +0 -24
- data/lib/phronomy/runnable.rb +0 -54
- data/lib/phronomy/token_usage.rb +0 -85
- 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_recovery.rb +0 -123
- data/sig/phronomy/llm_adapter.rbs +0 -11
- data/sig/phronomy/runtime.rbs +0 -76
- /data/lib/phronomy/agent/{llm_operation_result.rb → execution/llm_operation_result.rb} +0 -0
- /data/lib/phronomy/{canonical_json.rb → common/canonical_json.rb} +0 -0
- /data/lib/phronomy/{event.rb → engine/event.rb} +0 -0
|
@@ -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,
|
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "worker_input_restricted"
|
|
4
|
+
|
|
3
5
|
module Phronomy
|
|
4
6
|
module Concurrency
|
|
5
7
|
# Cooperative cancellation token for Agent/Tool work.
|
|
6
8
|
class CancellationToken
|
|
9
|
+
include Phronomy::Concurrency::WorkerInputRestricted
|
|
10
|
+
|
|
7
11
|
# Creates a token that expires after +seconds+ measured with the monotonic clock.
|
|
8
12
|
# @api public
|
|
9
13
|
def self.timeout_after(seconds)
|
|
@@ -94,7 +98,7 @@ module Phronomy
|
|
|
94
98
|
def deliver_cancel_callback(callback)
|
|
95
99
|
callback.call
|
|
96
100
|
rescue => error
|
|
97
|
-
Phronomy.
|
|
101
|
+
Phronomy::RuntimeSettings.current.logger&.error do
|
|
98
102
|
"[CancellationToken] on_cancel callback raised #{error.class}: #{error.message}"
|
|
99
103
|
end
|
|
100
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
|
|
@@ -6,21 +6,26 @@ module Phronomy
|
|
|
6
6
|
# boundary. Observing a result never mutates that result or its cancellation.
|
|
7
7
|
# @api private
|
|
8
8
|
class ResultComposition
|
|
9
|
-
def initialize(source, flatten:, &block)
|
|
9
|
+
def initialize(source, flatten:, name: nil, completion_only: false, recover: nil, &block)
|
|
10
10
|
@source = source
|
|
11
|
-
|
|
11
|
+
# Framework decorators also accept callback-only handles. They observe
|
|
12
|
+
# the source without inheriting application Execution controls or parents.
|
|
13
|
+
@completion_only = completion_only
|
|
14
|
+
@scope = completion_only ? nil : source.__execution_scope
|
|
12
15
|
@flatten = flatten
|
|
13
16
|
@block = block
|
|
14
|
-
|
|
17
|
+
@recover = recover
|
|
18
|
+
physical = completion_only || flatten || source.respond_to?(:on_physical_complete)
|
|
15
19
|
klass = physical ? PhysicalCompletionTask : TaskResult
|
|
16
|
-
|
|
17
|
-
|
|
20
|
+
result_name = name || "#{source.name}-#{flatten ? "flat-mapped" : "mapped"}"
|
|
21
|
+
@result = klass.deferred(name: result_name,
|
|
22
|
+
parent: completion_only ? nil : source.parent).__bind_execution(@scope)
|
|
18
23
|
@mutex = Mutex.new
|
|
19
24
|
@phase = :waiting
|
|
20
25
|
@source_physical = !source.respond_to?(:on_physical_complete)
|
|
21
26
|
@work_done = false
|
|
22
27
|
@inner_physical = true
|
|
23
|
-
@subscriptions =
|
|
28
|
+
@subscriptions = ResultSubscriptions.new
|
|
24
29
|
end
|
|
25
30
|
|
|
26
31
|
def start
|
|
@@ -30,7 +35,18 @@ module Phronomy
|
|
|
30
35
|
finish_physical
|
|
31
36
|
end
|
|
32
37
|
end
|
|
33
|
-
@
|
|
38
|
+
if @completion_only
|
|
39
|
+
@source.on_complete do |value, error|
|
|
40
|
+
status = if error
|
|
41
|
+
(@source.respond_to?(:status) && @source.status == :cancelled) ? :cancelled : :failed
|
|
42
|
+
else
|
|
43
|
+
:completed
|
|
44
|
+
end
|
|
45
|
+
receive_source(status, value, error)
|
|
46
|
+
end
|
|
47
|
+
else
|
|
48
|
+
@subscriptions.result(@source) { receive_source(*@source.__snapshot) }
|
|
49
|
+
end
|
|
34
50
|
if @scope
|
|
35
51
|
@subscriptions.cancellation(@scope.__cancellation_token) { suppress }
|
|
36
52
|
suppress unless @scope.__open?
|
|
@@ -40,9 +56,9 @@ module Phronomy
|
|
|
40
56
|
|
|
41
57
|
private
|
|
42
58
|
|
|
43
|
-
def receive_source
|
|
44
|
-
|
|
45
|
-
if
|
|
59
|
+
def receive_source(status, value, error)
|
|
60
|
+
recovering = status != :completed
|
|
61
|
+
if recovering && !@recover
|
|
46
62
|
started = @mutex.synchronize do
|
|
47
63
|
next false unless @phase == :waiting
|
|
48
64
|
@phase = :finished
|
|
@@ -66,12 +82,17 @@ module Phronomy
|
|
|
66
82
|
end
|
|
67
83
|
allowed = @scope ? @scope.__while_open(&claim) : claim.call
|
|
68
84
|
unless allowed
|
|
69
|
-
suppress
|
|
85
|
+
suppress if @scope
|
|
70
86
|
return
|
|
71
87
|
end
|
|
72
88
|
|
|
73
89
|
begin
|
|
74
|
-
transformed =
|
|
90
|
+
transformed = begin
|
|
91
|
+
recovering ? @recover.call(error) : @block.call(value)
|
|
92
|
+
rescue => transformation_error
|
|
93
|
+
raise if recovering || !@recover
|
|
94
|
+
@recover.call(transformation_error)
|
|
95
|
+
end
|
|
75
96
|
if @flatten && !transformed.is_a?(TaskResult)
|
|
76
97
|
raise TypeError, "flat_map block must return a Phronomy::TaskResult"
|
|
77
98
|
end
|
|
@@ -2,10 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
module Phronomy
|
|
4
4
|
module Concurrency
|
|
5
|
-
#
|
|
6
|
-
# registration: cleanup added after close is performed immediately.
|
|
5
|
+
# Completion and explicit-cancellation subscriptions. No timer acquisition.
|
|
7
6
|
# @api private
|
|
8
|
-
class
|
|
7
|
+
class ResultSubscriptions
|
|
9
8
|
def initialize
|
|
10
9
|
@mutex = Mutex.new
|
|
11
10
|
@closed = false
|
|
@@ -29,27 +28,24 @@ module Phronomy
|
|
|
29
28
|
add { result.__unsubscribe(callback) }
|
|
30
29
|
end
|
|
31
30
|
|
|
32
|
-
|
|
31
|
+
# Own the lifetime of explicit-cancellation notifications only. An elapsed
|
|
32
|
+
# deadline is not promoted to a notification here. close can race delivery:
|
|
33
|
+
# a callback already taken by cancel! may still run, as with the token API.
|
|
34
|
+
# Registration may deliver inline and close this collection before add;
|
|
35
|
+
# add then immediately disposes the completed registration.
|
|
36
|
+
# @api private
|
|
37
|
+
def explicit_cancellation(token, &callback)
|
|
33
38
|
return unless token
|
|
34
39
|
|
|
35
40
|
token.on_cancel(&callback)
|
|
36
41
|
add { token.send(:unregister_cancel_callback, callback) }
|
|
37
|
-
remaining = token.remaining_monotonic_seconds
|
|
38
|
-
if token.cancelled?
|
|
39
|
-
callback.call
|
|
40
|
-
elsif remaining
|
|
41
|
-
after(remaining, &callback)
|
|
42
|
-
end
|
|
43
42
|
end
|
|
44
43
|
|
|
45
|
-
def
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
timer = Phronomy::Runtime.instance.timer_queue
|
|
51
|
-
timer.schedule(seconds: seconds, &callback)
|
|
52
|
-
add { timer.cancel(callback) }
|
|
44
|
+
def cancellation(token, &callback)
|
|
45
|
+
return unless token
|
|
46
|
+
|
|
47
|
+
explicit_cancellation(token, &callback)
|
|
48
|
+
callback.call if token.cancelled?
|
|
53
49
|
end
|
|
54
50
|
|
|
55
51
|
def close
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Concurrency
|
|
5
|
+
# Services promotes deadlines into control notifications.
|
|
6
|
+
# @api private
|
|
7
|
+
class Subscriptions < ResultSubscriptions
|
|
8
|
+
def cancellation(token, &callback)
|
|
9
|
+
return unless token
|
|
10
|
+
|
|
11
|
+
super
|
|
12
|
+
remaining = token.remaining_monotonic_seconds
|
|
13
|
+
after(remaining, &callback) if !token.cancelled? && remaining
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def after(seconds, &callback)
|
|
17
|
+
if seconds <= 0
|
|
18
|
+
callback.call
|
|
19
|
+
return
|
|
20
|
+
end
|
|
21
|
+
add(&Phronomy::Execution.after(seconds, &callback))
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Concurrency
|
|
5
|
+
# Marks framework-managed objects excluded from authorization worker value
|
|
6
|
+
# snapshots and application behavior handles. Each owning type opts in.
|
|
7
|
+
# This methodless contract does not validate arbitrary object graphs or
|
|
8
|
+
# restrict every OffloadPool command. Application-owned opaque values retain
|
|
9
|
+
# their existing contract (ADR-024/045).
|
|
10
|
+
# @api private
|
|
11
|
+
module WorkerInputRestricted
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -5,6 +5,28 @@ module Phronomy
|
|
|
5
5
|
# that JOB; its application-defined intermediate work is not discovered here.
|
|
6
6
|
# @api public
|
|
7
7
|
class Execution
|
|
8
|
+
# Composition installs the mechanism. Public values and settled results do
|
|
9
|
+
# not access it; only scheduling/submission requires a configured backend.
|
|
10
|
+
class << self
|
|
11
|
+
attr_writer :backend_provider
|
|
12
|
+
|
|
13
|
+
def submit(**options, &operation)
|
|
14
|
+
raise ArgumentError, "Execution.submit requires a block" unless operation
|
|
15
|
+
backend.submit(**options, &operation)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def after(seconds, &callback)
|
|
19
|
+
raise ArgumentError, "Execution.after requires a block" unless callback
|
|
20
|
+
backend.after(seconds, &callback)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def backend
|
|
26
|
+
@backend_provider&.call || raise(ConfigurationError, "Execution backend has not been configured")
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
8
30
|
# @return [InvocationContext] this execution's explicit operation context
|
|
9
31
|
# @api public
|
|
10
32
|
attr_reader :invocation_context
|
|
@@ -14,32 +36,25 @@ module Phronomy
|
|
|
14
36
|
# @param cancellation_token [Concurrency::CancellationToken, nil]
|
|
15
37
|
# @param invocation_context [InvocationContext, nil] existing information/controls
|
|
16
38
|
# @yield [input, execution] starts one JOB and returns its final TaskResult
|
|
17
|
-
# @return [TaskResult<Array<TaskResult::Outcome>>]
|
|
18
|
-
# @api public
|
|
19
|
-
def self.run_async(inputs, timeout: nil, cancellation_token: nil,
|
|
20
|
-
invocation_context: nil, &block)
|
|
21
|
-
__run_async(inputs, timeout: timeout, cancellation_token: cancellation_token,
|
|
22
|
-
invocation_context: invocation_context, &block)
|
|
23
|
-
end
|
|
24
|
-
|
|
25
39
|
# Starts the same execution once and waits outside EventLoop.
|
|
26
40
|
# @return [Array<TaskResult::Outcome>]
|
|
27
41
|
# @see run_async
|
|
28
42
|
# @api public
|
|
29
43
|
def self.run(inputs, timeout: nil, cancellation_token: nil,
|
|
30
|
-
invocation_context: nil, &block)
|
|
31
|
-
if
|
|
44
|
+
invocation_context: nil, max_concurrency: nil, &block)
|
|
45
|
+
if WaitPolicy.blocking_forbidden?
|
|
32
46
|
raise EventLoopReentrancyError, "Execution.run cannot run on EventLoop; use run_async"
|
|
33
47
|
end
|
|
34
48
|
run_async(inputs, timeout: timeout, cancellation_token: cancellation_token,
|
|
35
|
-
invocation_context: invocation_context, &block).wait_result
|
|
49
|
+
invocation_context: invocation_context, max_concurrency: max_concurrency, &block).wait_result
|
|
36
50
|
end
|
|
37
51
|
|
|
38
|
-
#
|
|
39
|
-
#
|
|
40
|
-
# @
|
|
41
|
-
|
|
42
|
-
|
|
52
|
+
# Starts bounded fan-out without exposing collector or child bindings.
|
|
53
|
+
# @param max_concurrency [Integer, nil] maximum concurrently admitted JOBs
|
|
54
|
+
# @return [TaskResult<Array<TaskResult::Outcome>>]
|
|
55
|
+
# @api public
|
|
56
|
+
def self.run_async(inputs, timeout: nil, cancellation_token: nil,
|
|
57
|
+
invocation_context: nil, max_concurrency: nil, &block)
|
|
43
58
|
raise TypeError, "inputs must be an Array" unless inputs.is_a?(Array)
|
|
44
59
|
raise ArgumentError, "Execution.run_async requires a block" unless block
|
|
45
60
|
if !cancellation_token.nil? && !cancellation_token.is_a?(Concurrency::CancellationToken)
|
|
@@ -54,17 +69,29 @@ module Phronomy
|
|
|
54
69
|
raise ArgumentError, "timeout must be a finite real number"
|
|
55
70
|
end
|
|
56
71
|
end
|
|
72
|
+
if !max_concurrency.nil? && !(max_concurrency.is_a?(Integer) && max_concurrency.positive?)
|
|
73
|
+
raise ArgumentError, "max_concurrency must be a positive Integer"
|
|
74
|
+
end
|
|
57
75
|
new(inputs.dup, timeout: timeout, cancellation_token: cancellation_token,
|
|
58
|
-
invocation_context: invocation_context, concurrency_limit:
|
|
76
|
+
invocation_context: invocation_context, concurrency_limit: max_concurrency,
|
|
59
77
|
&block).send(:start)
|
|
60
78
|
end
|
|
61
|
-
private_class_method :__run_async
|
|
62
79
|
|
|
63
|
-
#
|
|
80
|
+
# Starts one child with this execution's controls and the child's explicit
|
|
81
|
+
# context. Subscriptions live until the child settles, including start errors.
|
|
82
|
+
# A child-specific context keeps its own controls; the supplied token adds
|
|
83
|
+
# this execution's cancellation without mutating either original token.
|
|
64
84
|
# @api private
|
|
65
|
-
def
|
|
66
|
-
|
|
85
|
+
def start_child(invocation_context: nil, cancellation_token: nil, parent_task_id: nil)
|
|
86
|
+
controls = InvocationControls.new(invocation_context: @invocation_context,
|
|
67
87
|
cancellation_token: cancellation_token)
|
|
88
|
+
controls.check_start!
|
|
89
|
+
child_context = invocation_context || @invocation_context.merge(parent_task_id: parent_task_id)
|
|
90
|
+
result = yield child_context, controls.token
|
|
91
|
+
controls.track(result)
|
|
92
|
+
rescue
|
|
93
|
+
controls&.close
|
|
94
|
+
raise
|
|
68
95
|
end
|
|
69
96
|
|
|
70
97
|
# A scoped observation copy. The source, its controls and its physical work
|
|
@@ -77,7 +104,7 @@ module Phronomy
|
|
|
77
104
|
unless source_result.is_a?(TaskResult)
|
|
78
105
|
raise TypeError, "source_result must be a Phronomy::TaskResult"
|
|
79
106
|
end
|
|
80
|
-
observed = TaskResult.deferred(name: "execution-observation").__bind_execution(
|
|
107
|
+
observed = TaskResult.deferred(name: "execution-observation").__bind_execution(@scope)
|
|
81
108
|
subscriptions = Concurrency::Subscriptions.new
|
|
82
109
|
subscriptions.result(observed) { subscriptions.close }
|
|
83
110
|
unless __open?
|
|
@@ -103,12 +130,12 @@ module Phronomy
|
|
|
103
130
|
# Scope/admission hooks shared with framework-owned operations.
|
|
104
131
|
# @api private
|
|
105
132
|
def __while_open(&block)
|
|
106
|
-
@
|
|
133
|
+
@scope.__while_open(&block)
|
|
107
134
|
end
|
|
108
135
|
|
|
109
136
|
# @api private
|
|
110
137
|
def __open?
|
|
111
|
-
@
|
|
138
|
+
@scope.__open?
|
|
112
139
|
end
|
|
113
140
|
|
|
114
141
|
# @api private
|
|
@@ -118,7 +145,7 @@ module Phronomy
|
|
|
118
145
|
|
|
119
146
|
# @api private
|
|
120
147
|
def __cancellation_error
|
|
121
|
-
@
|
|
148
|
+
@scope.__cancellation_error
|
|
122
149
|
end
|
|
123
150
|
|
|
124
151
|
private
|
|
@@ -133,9 +160,6 @@ module Phronomy
|
|
|
133
160
|
@parent_token = cancellation_token
|
|
134
161
|
@token = Concurrency::CancellationToken.new
|
|
135
162
|
@subscriptions = Concurrency::Subscriptions.new
|
|
136
|
-
@cancellation_error = CancellationError.new("Execution scope is closed")
|
|
137
|
-
@invocation_context = (invocation_context || InvocationContext.new)
|
|
138
|
-
.merge(cancellation_token: @token).__bind_execution(self)
|
|
139
163
|
@result = TaskResult.deferred(name: "execution")
|
|
140
164
|
@pump_mutex = Mutex.new
|
|
141
165
|
@next_index = 0
|
|
@@ -145,6 +169,9 @@ module Phronomy
|
|
|
145
169
|
@collector = Concurrency::ResultCollector.new(inputs.length) do |kind, outcomes|
|
|
146
170
|
finish(kind, outcomes)
|
|
147
171
|
end
|
|
172
|
+
@scope = ExecutionScopeState.new(collector: @collector, token: @token)
|
|
173
|
+
@invocation_context = (invocation_context || InvocationContext.new)
|
|
174
|
+
.merge(cancellation_token: @token).__bind_execution(@scope)
|
|
148
175
|
end
|
|
149
176
|
|
|
150
177
|
def start
|
|
@@ -221,12 +248,12 @@ module Phronomy
|
|
|
221
248
|
when :completed
|
|
222
249
|
@result.complete(outcomes)
|
|
223
250
|
when :timeout
|
|
224
|
-
@
|
|
251
|
+
@scope.__set_cancellation_error(CancellationError.new("Execution deadline exceeded"))
|
|
225
252
|
error = ExecutionTimeoutError.new(outcomes: outcomes)
|
|
226
253
|
@token.cancel!
|
|
227
254
|
@result.fail(error)
|
|
228
255
|
when :cancelled
|
|
229
|
-
@
|
|
256
|
+
@scope.__set_cancellation_error(CancellationError.new("Execution cancelled"))
|
|
230
257
|
error = ExecutionCancellationError.new(outcomes: outcomes)
|
|
231
258
|
@token.cancel!
|
|
232
259
|
@result.cancel!(error)
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
# Internal lifecycle/delivery contract for feature-owned execution state.
|
|
5
|
+
# A receiver has one serial owner supplied by composition, not an application
|
|
6
|
+
# callback SPI. Normal mutation and delivery run only on that owner. idle?
|
|
7
|
+
# runs under its lifecycle lock and must not acquire that lock again.
|
|
8
|
+
# shutdown runs on the failing owner or exclusively after it joins.
|
|
9
|
+
# @api private
|
|
10
|
+
class ExecutionReceiver
|
|
11
|
+
def self.install_binding(provider)
|
|
12
|
+
raise ArgumentError, "Execution receiver provider must be callable" unless provider.respond_to?(:call)
|
|
13
|
+
@binding_provider = provider
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.for(owner)
|
|
17
|
+
ExecutionReceiver.binding.receiver(self, owner)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.existing_for(owner)
|
|
21
|
+
ExecutionReceiver.binding.existing_receiver(self, owner)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.binding
|
|
25
|
+
@binding_provider&.call || raise(ConfigurationError, "Execution receiver binding has not been configured")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def initialize(channel:)
|
|
29
|
+
@channel = channel
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def __bound_to?(owner)
|
|
33
|
+
@channel.bound_to?(owner)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def idle?
|
|
37
|
+
raise NotImplementedError
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def deliver(message)
|
|
41
|
+
raise NotImplementedError
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def shutdown(error:)
|
|
45
|
+
raise NotImplementedError
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def session_retired(fsm_session_id, reason:)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
private
|
|
52
|
+
|
|
53
|
+
def synchronize(&block)
|
|
54
|
+
@channel.synchronize(&block)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def admit(&block)
|
|
58
|
+
@channel.admit(self, &block)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def post_message(message, admission: false, completion: nil)
|
|
62
|
+
@channel.post(self, message, admission: admission, completion: completion)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Resolve a live routing target atomically with delivery. The channel owns
|
|
66
|
+
# the transport envelope; domain receivers return only a target identity.
|
|
67
|
+
def route_event(type:, payload: nil, &block)
|
|
68
|
+
@channel.route(self, type: type, payload: payload, &block)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def assert_event_loop_thread!
|
|
72
|
+
return if @channel.current?
|
|
73
|
+
|
|
74
|
+
raise Phronomy::Error,
|
|
75
|
+
"Phronomy-managed live execution state may only be mutated on EventLoop"
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
# A scope gate shared by observations and framework operations. The same
|
|
5
|
+
# collector lock arbitrates admission and completion; no Execution is held.
|
|
6
|
+
# @api private
|
|
7
|
+
class ExecutionScopeState
|
|
8
|
+
def initialize(collector:, token:)
|
|
9
|
+
@collector = collector
|
|
10
|
+
@token = token
|
|
11
|
+
@error_mutex = Mutex.new
|
|
12
|
+
@error = CancellationError.new("Execution scope is closed")
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def __while_open(&block)
|
|
16
|
+
@collector.while_open(&block)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def __open?
|
|
20
|
+
@collector.open?
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def __cancellation_token
|
|
24
|
+
@token
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def __cancellation_error
|
|
28
|
+
@error_mutex.synchronize { @error }
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def __set_cancellation_error(error)
|
|
32
|
+
@error_mutex.synchronize { @error = error }
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -70,20 +70,5 @@ module Phronomy
|
|
|
70
70
|
def __execution_scope
|
|
71
71
|
@execution_scope
|
|
72
72
|
end
|
|
73
|
-
|
|
74
|
-
# @api private
|
|
75
|
-
def effective_cancellation_token
|
|
76
|
-
@cancellation_token || Phronomy::Concurrency::CancellationToken.new
|
|
77
|
-
end
|
|
78
|
-
|
|
79
|
-
# @api private
|
|
80
|
-
def effective_timeout_token
|
|
81
|
-
return @cancellation_token if @cancellation_token
|
|
82
|
-
return nil if @deadline.nil?
|
|
83
|
-
|
|
84
|
-
token = Phronomy::Concurrency::CancellationToken.new
|
|
85
|
-
@deadline.attach_to(token)
|
|
86
|
-
token
|
|
87
|
-
end
|
|
88
73
|
end
|
|
89
74
|
end
|