phronomy 0.27.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 +52 -0
- data/CONTRIBUTING.md +8 -8
- data/README.md +9 -3
- 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/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/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/features.md +8 -3
- data/docs/getting-started.md +9 -6
- 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/persistence-backends.md +38 -21
- data/docs/runtime-and-concurrency.md +26 -1
- 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/{lifecycle_contract/agent_purged_error.rb → agent_purged_error.rb} +1 -1
- data/lib/phronomy/{recovery → agent/api}/execution_rehydration_required_error.rb +1 -1
- data/lib/phronomy/agent/async_event_api.rb +37 -32
- data/lib/phronomy/agent/base.rb +74 -72
- data/lib/phronomy/agent/before_llm_input.rb +64 -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 +44 -23
- data/lib/phronomy/agent/execution/execution_cancellation.rb +8 -4
- data/lib/phronomy/agent/execution/execution_coordinator.rb +214 -395
- data/lib/phronomy/agent/execution/execution_metadata.rb +1 -0
- data/lib/phronomy/agent/execution/execution_outcome_committer.rb +70 -126
- 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 +18 -116
- 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 +7 -7
- 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/{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/ownership_registry.rb +18 -23
- 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 +10 -9
- data/lib/phronomy/agent/recovery/recovery_coordinator/installation.rb +27 -27
- data/lib/phronomy/agent/recovery/recovery_coordinator/resolution.rb +19 -21
- data/lib/phronomy/agent/recovery/recovery_coordinator.rb +23 -16
- data/lib/phronomy/agent/recovery/recovery_rules.rb +119 -0
- data/lib/phronomy/agent/recovery/recovery_support.rb +8 -11
- 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} +1 -1
- 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/configuration/runtime_settings.rb +49 -0
- 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 +202 -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/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 +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/execution/execution_receiver.rb +78 -0
- data/lib/phronomy/execution/execution_scope_state.rb +35 -0
- 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 +3 -3
- data/lib/phronomy/execution/wait_policy.rb +24 -0
- data/lib/phronomy/filter/{contract/filter_block_error.rb → filter_block_error.rb} +1 -1
- 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/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 +58 -42
- 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_coordinator.rb +83 -74
- 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/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 +549 -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 +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/execution/workflow.rb +7 -7
- 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 +27 -0
- data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +3 -5
- data/lib/phronomy/workflow/execution/workflow_runner.rb +58 -79
- data/lib/phronomy/workflow/execution/workflow_settings.rb +28 -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 +38 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb +156 -0
- data/lib/phronomy/workflow/{execution → runtime_binding}/workflow_terminal_policy.rb +4 -4
- data/lib/phronomy.rb +57 -16
- data/scripts/api_snapshot.rb +35 -15
- 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 -138
- 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 +10 -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 +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} +2 -16
- 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 +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 +318 -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}/execution_cancellation_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/execution_timeout_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
|
@@ -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
|
|
@@ -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,11 +69,30 @@ 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
|
-
|
|
79
|
+
|
|
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.
|
|
84
|
+
# @api private
|
|
85
|
+
def start_child(invocation_context: nil, cancellation_token: nil, parent_task_id: nil)
|
|
86
|
+
controls = InvocationControls.new(invocation_context: @invocation_context,
|
|
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
|
|
95
|
+
end
|
|
62
96
|
|
|
63
97
|
# A scoped observation copy. The source, its controls and its physical work
|
|
64
98
|
# keep their original ownership. Bind before adding this run's map/flat_map.
|
|
@@ -70,7 +104,7 @@ module Phronomy
|
|
|
70
104
|
unless source_result.is_a?(TaskResult)
|
|
71
105
|
raise TypeError, "source_result must be a Phronomy::TaskResult"
|
|
72
106
|
end
|
|
73
|
-
observed = TaskResult.deferred(name: "execution-observation").__bind_execution(
|
|
107
|
+
observed = TaskResult.deferred(name: "execution-observation").__bind_execution(@scope)
|
|
74
108
|
subscriptions = Concurrency::Subscriptions.new
|
|
75
109
|
subscriptions.result(observed) { subscriptions.close }
|
|
76
110
|
unless __open?
|
|
@@ -96,12 +130,12 @@ module Phronomy
|
|
|
96
130
|
# Scope/admission hooks shared with framework-owned operations.
|
|
97
131
|
# @api private
|
|
98
132
|
def __while_open(&block)
|
|
99
|
-
@
|
|
133
|
+
@scope.__while_open(&block)
|
|
100
134
|
end
|
|
101
135
|
|
|
102
136
|
# @api private
|
|
103
137
|
def __open?
|
|
104
|
-
@
|
|
138
|
+
@scope.__open?
|
|
105
139
|
end
|
|
106
140
|
|
|
107
141
|
# @api private
|
|
@@ -111,7 +145,7 @@ module Phronomy
|
|
|
111
145
|
|
|
112
146
|
# @api private
|
|
113
147
|
def __cancellation_error
|
|
114
|
-
@
|
|
148
|
+
@scope.__cancellation_error
|
|
115
149
|
end
|
|
116
150
|
|
|
117
151
|
private
|
|
@@ -126,9 +160,6 @@ module Phronomy
|
|
|
126
160
|
@parent_token = cancellation_token
|
|
127
161
|
@token = Concurrency::CancellationToken.new
|
|
128
162
|
@subscriptions = Concurrency::Subscriptions.new
|
|
129
|
-
@cancellation_error = CancellationError.new("Execution scope is closed")
|
|
130
|
-
@invocation_context = (invocation_context || InvocationContext.new)
|
|
131
|
-
.merge(cancellation_token: @token).__bind_execution(self)
|
|
132
163
|
@result = TaskResult.deferred(name: "execution")
|
|
133
164
|
@pump_mutex = Mutex.new
|
|
134
165
|
@next_index = 0
|
|
@@ -138,6 +169,9 @@ module Phronomy
|
|
|
138
169
|
@collector = Concurrency::ResultCollector.new(inputs.length) do |kind, outcomes|
|
|
139
170
|
finish(kind, outcomes)
|
|
140
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)
|
|
141
175
|
end
|
|
142
176
|
|
|
143
177
|
def start
|
|
@@ -214,12 +248,12 @@ module Phronomy
|
|
|
214
248
|
when :completed
|
|
215
249
|
@result.complete(outcomes)
|
|
216
250
|
when :timeout
|
|
217
|
-
@
|
|
251
|
+
@scope.__set_cancellation_error(CancellationError.new("Execution deadline exceeded"))
|
|
218
252
|
error = ExecutionTimeoutError.new(outcomes: outcomes)
|
|
219
253
|
@token.cancel!
|
|
220
254
|
@result.fail(error)
|
|
221
255
|
when :cancelled
|
|
222
|
-
@
|
|
256
|
+
@scope.__set_cancellation_error(CancellationError.new("Execution cancelled"))
|
|
223
257
|
error = ExecutionCancellationError.new(outcomes: outcomes)
|
|
224
258
|
@token.cancel!
|
|
225
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
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
# Owns invocation control attachment through the observed result's lifetime.
|
|
5
|
+
# Domain runners carry this operation control, not execution scope internals.
|
|
6
|
+
# @api private
|
|
7
|
+
class InvocationControls
|
|
8
|
+
attr_reader :token
|
|
9
|
+
|
|
10
|
+
def self.attach(result, invocation_context:, cancellation_token:)
|
|
11
|
+
controls = new(invocation_context: invocation_context, cancellation_token: cancellation_token)
|
|
12
|
+
controls.bind(result)
|
|
13
|
+
controls
|
|
14
|
+
rescue
|
|
15
|
+
controls&.close
|
|
16
|
+
raise
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def initialize(invocation_context:, cancellation_token:)
|
|
20
|
+
if !invocation_context.nil? && !invocation_context.is_a?(InvocationContext)
|
|
21
|
+
raise TypeError, "invocation_context must be a Phronomy::InvocationContext or nil"
|
|
22
|
+
end
|
|
23
|
+
@scope = invocation_context&.__execution_scope
|
|
24
|
+
@subscriptions = Concurrency::Subscriptions.new
|
|
25
|
+
@token = invocation_context ? Concurrency::CancellationToken.new : cancellation_token
|
|
26
|
+
return unless invocation_context
|
|
27
|
+
|
|
28
|
+
[invocation_context.cancellation_token, cancellation_token].compact.uniq.each do |source|
|
|
29
|
+
@subscriptions.cancellation(source) { @token.cancel! }
|
|
30
|
+
end
|
|
31
|
+
if !@scope && invocation_context.deadline
|
|
32
|
+
@subscriptions.after(invocation_context.deadline.remaining_seconds) { @token.cancel! }
|
|
33
|
+
end
|
|
34
|
+
@token.cancel! if @scope && !@scope.__open?
|
|
35
|
+
rescue
|
|
36
|
+
close
|
|
37
|
+
raise
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Check at the receiving runner, after asynchronous delivery. An already
|
|
41
|
+
# closed scope must not acquire the domain's admission slot or start I/O.
|
|
42
|
+
def check_start!
|
|
43
|
+
if @scope && (!@scope.__open? || @token&.cancelled?)
|
|
44
|
+
raise @scope.__cancellation_error
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def bind(result)
|
|
49
|
+
result.__bind_execution(@scope)
|
|
50
|
+
track(result)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def track(result)
|
|
54
|
+
@subscriptions.result(result) { close }
|
|
55
|
+
result
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def close
|
|
59
|
+
@subscriptions&.close
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def self.effective_timeout_token(context)
|
|
63
|
+
return context.cancellation_token if context.cancellation_token
|
|
64
|
+
return nil unless context.deadline
|
|
65
|
+
|
|
66
|
+
token = Concurrency::CancellationToken.new
|
|
67
|
+
subscriptions = Concurrency::Subscriptions.new
|
|
68
|
+
subscriptions.cancellation(token) { subscriptions.close }
|
|
69
|
+
subscriptions.after(context.deadline.remaining_seconds) { token.cancel! }
|
|
70
|
+
token
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
# Base interface for executable graph components.
|
|
5
|
+
# Provides invoke / stream / batch.
|
|
6
|
+
module Runnable
|
|
7
|
+
# Synchronous execution. Must be overridden in subclasses.
|
|
8
|
+
def invoke(input, config: {})
|
|
9
|
+
raise NotImplementedError, "#{self.class}#invoke is not implemented"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# Streaming execution. Default yields the invoke result as a single chunk.
|
|
13
|
+
def stream(input, config: {}, &block)
|
|
14
|
+
result = invoke(input, config: config)
|
|
15
|
+
yield result if block
|
|
16
|
+
result
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Batch execution. Default calls invoke sequentially.
|
|
20
|
+
def batch(inputs, config: {})
|
|
21
|
+
inputs.map { |input| invoke(input, config: config) }
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -130,7 +130,7 @@ module Phronomy
|
|
|
130
130
|
# @raise [Exception] the error that settled the TaskResult
|
|
131
131
|
# @api public
|
|
132
132
|
def wait_result(timeout: nil)
|
|
133
|
-
if Phronomy::
|
|
133
|
+
if Phronomy::WaitPolicy.blocking_forbidden? && !done?
|
|
134
134
|
raise Phronomy::EventLoopReentrancyError,
|
|
135
135
|
"TaskResult#wait_result cannot block the EventLoop thread; continue via an event"
|
|
136
136
|
end
|
|
@@ -160,7 +160,7 @@ module Phronomy
|
|
|
160
160
|
# Returns self when settled, nil on timeout.
|
|
161
161
|
# @api private
|
|
162
162
|
def join(limit = nil)
|
|
163
|
-
if Phronomy::
|
|
163
|
+
if Phronomy::WaitPolicy.blocking_forbidden? && !done?
|
|
164
164
|
raise Phronomy::EventLoopReentrancyError,
|
|
165
165
|
"TaskResult#join cannot block the EventLoop thread; continue via an event"
|
|
166
166
|
end
|
|
@@ -308,7 +308,7 @@ module Phronomy
|
|
|
308
308
|
def deliver_completion_callback(callback, value, error)
|
|
309
309
|
callback.call(value, error)
|
|
310
310
|
rescue => callback_error
|
|
311
|
-
Phronomy.
|
|
311
|
+
Phronomy::RuntimeSettings.current.logger&.error do
|
|
312
312
|
"[TaskResult] on_complete callback raised #{callback_error.class}: #{callback_error.message}"
|
|
313
313
|
end
|
|
314
314
|
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
# Neutral thread-local blocking policy. It owns no Runtime or scheduler.
|
|
5
|
+
# No invocation context is inferred from this marker.
|
|
6
|
+
# @api private
|
|
7
|
+
module WaitPolicy
|
|
8
|
+
KEY = :phronomy_control_thread_depth
|
|
9
|
+
private_constant :KEY
|
|
10
|
+
|
|
11
|
+
def self.blocking_forbidden?
|
|
12
|
+
Thread.current.thread_variable_get(KEY).to_i > 0
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def self.without_blocking
|
|
16
|
+
thread = Thread.current
|
|
17
|
+
previous = thread.thread_variable_get(KEY)
|
|
18
|
+
thread.thread_variable_set(KEY, previous.to_i + 1)
|
|
19
|
+
yield
|
|
20
|
+
ensure
|
|
21
|
+
thread.thread_variable_set(KEY, previous)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
class GeneratorVerifier
|
|
5
|
+
# Connects one reused Agent incarnation through its public operation and
|
|
6
|
+
# notification contracts. Terminal results belong to each returned TaskResult;
|
|
7
|
+
# only approval suspension needs the incarnation listener while that Task is
|
|
8
|
+
# still pending. Generation owns the request-to-Workflow correlation.
|
|
9
|
+
# @api private
|
|
10
|
+
class AgentOperation
|
|
11
|
+
def initialize(agent_class)
|
|
12
|
+
@mutex = Mutex.new
|
|
13
|
+
@pending = []
|
|
14
|
+
@agent = agent_class.new(on_event: method(:receive_event))
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def start(input, listener:)
|
|
18
|
+
request = Object.new
|
|
19
|
+
# Workflow entry calls are serial. Keep submitted requests until their
|
|
20
|
+
# own result settles: Agent admission itself may run after an earlier
|
|
21
|
+
# request completes, so a pending submission is not necessarily busy.
|
|
22
|
+
@mutex.synchronize { @pending << [request, listener] }
|
|
23
|
+
task = @agent.invoke_async(input)
|
|
24
|
+
task.on_complete do |result, error|
|
|
25
|
+
release(request)
|
|
26
|
+
# Handoff was not a generation completion in the event-based path.
|
|
27
|
+
next if !error && result.is_a?(Hash) && result[:handoff_request]
|
|
28
|
+
|
|
29
|
+
event = if error
|
|
30
|
+
type = if error.is_a?(Phronomy::TimeoutError)
|
|
31
|
+
:timeout
|
|
32
|
+
elsif error.is_a?(Phronomy::CancellationError)
|
|
33
|
+
:cancelled
|
|
34
|
+
else
|
|
35
|
+
:error
|
|
36
|
+
end
|
|
37
|
+
Phronomy::Agent::StreamEvent.new(type: type, payload: {error: error})
|
|
38
|
+
else
|
|
39
|
+
Phronomy::Agent::StreamEvent.new(type: :done, payload: result)
|
|
40
|
+
end
|
|
41
|
+
listener.call(event)
|
|
42
|
+
end
|
|
43
|
+
task
|
|
44
|
+
rescue
|
|
45
|
+
release(request)
|
|
46
|
+
raise
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def receive_event(event)
|
|
52
|
+
return unless event.type == :approval_required
|
|
53
|
+
|
|
54
|
+
listener = @mutex.synchronize { @pending.first&.last }
|
|
55
|
+
listener&.call(event)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def release(request)
|
|
59
|
+
@mutex.synchronize do
|
|
60
|
+
@pending.delete_if { |entry| entry.first.equal?(request) }
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
class GeneratorVerifier
|
|
5
|
+
# Boot supplies the default concrete parser. The generation algorithm owns
|
|
6
|
+
# its output interpretation and fallback, not the concrete parser selection.
|
|
7
|
+
# @api private
|
|
8
|
+
module DefaultParser
|
|
9
|
+
FACTORIES = {}
|
|
10
|
+
private_constant :FACTORIES
|
|
11
|
+
|
|
12
|
+
# Private one-time composition binding; not an application registry.
|
|
13
|
+
# @api private
|
|
14
|
+
def self.install_factory(factory)
|
|
15
|
+
raise ArgumentError, "factory must respond to call" unless factory.respond_to?(:call)
|
|
16
|
+
|
|
17
|
+
FACTORIES.replace(parser: factory).freeze
|
|
18
|
+
nil
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# @api private
|
|
22
|
+
def self.build
|
|
23
|
+
FACTORIES.fetch(:parser).call
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -71,7 +71,7 @@ module Phronomy
|
|
|
71
71
|
workflow_instance_id: next_state.workflow_instance_id,
|
|
72
72
|
request_id: request_id
|
|
73
73
|
)
|
|
74
|
-
@draft_agent.
|
|
74
|
+
@draft_agent.start(prompt, listener: listener)
|
|
75
75
|
next_state
|
|
76
76
|
end
|
|
77
77
|
|
|
@@ -86,7 +86,7 @@ module Phronomy
|
|
|
86
86
|
workflow_instance_id: next_state.workflow_instance_id,
|
|
87
87
|
request_id: request_id
|
|
88
88
|
)
|
|
89
|
-
@review_agent.
|
|
89
|
+
@review_agent.start(prompt, listener: listener)
|
|
90
90
|
next_state
|
|
91
91
|
end
|
|
92
92
|
|