phronomy 0.25.0 → 0.27.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/CHANGELOG.md +71 -0
- data/CONTRIBUTING.md +4 -4
- data/README.md +14 -7
- data/VERIFY.sh +27 -27
- data/benchmark/bench_agent_invoke.rb +26 -22
- data/benchmark/bench_context_assembler.rb +4 -5
- data/benchmark/bench_regression.rb +1 -1
- data/docs/architecture/agent-chat-and-state-ownership.md +147 -0
- data/docs/architecture/agent-configuration-and-tool-binding.md +139 -0
- data/docs/architecture/agent-context.md +5 -3
- data/docs/architecture/agent-transition-ownership.md +89 -0
- data/docs/architecture/before-llm-input.md +6 -0
- data/docs/architecture/context-management.md +30 -0
- data/docs/architecture/context-preparation-steps.md +90 -0
- data/docs/architecture/entry-action-and-team-wording.md +72 -0
- data/docs/architecture/execution-metadata-and-values.md +80 -0
- data/docs/architecture/generator-verifier-ownership.md +111 -0
- data/docs/architecture/multi-agent-handoff.md +8 -2
- data/docs/architecture/persistence-refactoring-plan.md +50 -0
- data/docs/architecture/persistence.md +29 -6
- data/docs/architecture/refactoring-closure.md +334 -0
- data/docs/architecture/remaining-refactoring-plan.md +374 -0
- data/docs/architecture/rubyllm-2-token-ownership.md +82 -0
- data/docs/architecture/tool-schema-recording-gap.md +50 -0
- data/docs/architecture/tracing.md +4 -4
- data/docs/architecture/workflow-terminal-ownership-design.md +104 -0
- data/docs/architecture.md +209 -0
- data/docs/async-composition.md +205 -0
- data/docs/decisions/010-cooperative-first-concurrency.md +23 -23
- data/docs/decisions/014-unified-persistence-durable-state.md +11 -0
- 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/030-agent-handoff-domain-and-durable-responsibility.md +5 -0
- data/docs/decisions/031-durable-multi-agent-coordination.md +5 -0
- data/docs/decisions/032-storage-backend-composition.md +80 -0
- data/docs/decisions/033-domain-persistence-ownership.md +79 -0
- data/docs/decisions/034-handoff-runner-coordination-ownership.md +76 -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 +131 -0
- data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +74 -0
- data/docs/decisions/040-configuration-default-composition.md +94 -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 +100 -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 +151 -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 +78 -0
- data/docs/decisions/README.md +35 -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/features.md +21 -12
- data/docs/getting-started.md +10 -10
- data/docs/migrations/0.15.md +5 -0
- data/docs/migrations/durable-semantic-coordination-v2.md +7 -1
- data/docs/migrations/handoff-runner-multi-agent.md +35 -0
- data/docs/migrations/neutral-storage-spi.md +43 -0
- data/docs/migrations/parallel-tool-chat-removal.md +49 -0
- data/docs/migrations/shared-state-multi-agent.md +44 -0
- data/docs/migrations/storage-backend-composition.md +117 -0
- data/docs/migrations/storage-transaction-boundaries.md +74 -0
- data/docs/persistence-backends.md +162 -204
- data/docs/runtime-and-concurrency.md +205 -60
- data/lib/phronomy/agent/api/agent.rb +17 -0
- data/lib/phronomy/agent/async_event_api.rb +2 -2
- data/lib/phronomy/agent/base.rb +55 -251
- data/lib/phronomy/{agent.rb → agent/composition/run_once.rb} +4 -13
- data/lib/phronomy/agent/context/capability/base.rb +67 -26
- data/lib/phronomy/agent/context/capability/tool_executor.rb +62 -0
- data/lib/phronomy/agent/{context_assembler.rb → context_assembly/context_assembler.rb} +143 -88
- data/lib/phronomy/agent/{context_importer.rb → context_assembly/context_importer.rb} +2 -2
- data/lib/phronomy/agent/{ruby_llm_materializer.rb → context_assembly/ruby_llm_materializer.rb} +4 -7
- data/lib/phronomy/agent/context_assembly/runtime_chat_builder.rb +36 -0
- data/lib/phronomy/agent/context_assembly/saved_context_reader.rb +53 -0
- data/lib/phronomy/agent/context_assembly/state_writer.rb +165 -0
- data/lib/phronomy/agent/context_assembly/token_budget_resolver.rb +22 -0
- data/lib/phronomy/agent/{context_plan.rb → context_contract/context_plan.rb} +1 -1
- data/lib/phronomy/agent/{context_policy_input.rb → context_contract/context_policy_input.rb} +6 -6
- data/lib/phronomy/agent/{llm_input_build_context.rb → context_contract/llm_input_build_context.rb} +1 -1
- data/lib/phronomy/agent/{llm_input_manifest.rb → context_contract/llm_input_manifest.rb} +20 -20
- data/lib/phronomy/agent/{llm_input_patch.rb → context_contract/llm_input_patch.rb} +2 -2
- data/lib/phronomy/agent/{agent_execution.rb → execution/agent_execution.rb} +8 -4
- data/lib/phronomy/agent/{agent_invocation.rb → execution/agent_invocation.rb} +8 -13
- data/lib/phronomy/agent/{agent_invocation_session_builder.rb → execution/agent_invocation_session_builder.rb} +20 -96
- data/lib/phronomy/agent/execution/approval_resume_commit.rb +108 -0
- data/lib/phronomy/agent/execution/dispatch_preparation.rb +305 -0
- data/lib/phronomy/agent/{exact_execution.rb → execution/exact_execution.rb} +12 -13
- data/lib/phronomy/agent/{execution_cancellation.rb → execution/execution_cancellation.rb} +2 -3
- data/lib/phronomy/agent/execution/execution_coordinator.rb +1925 -0
- data/lib/phronomy/agent/execution/execution_failure.rb +30 -0
- data/lib/phronomy/agent/execution/execution_metadata.rb +53 -0
- data/lib/phronomy/agent/execution/execution_outcome_committer.rb +344 -0
- data/lib/phronomy/agent/execution/execution_registry.rb +459 -0
- data/lib/phronomy/agent/execution/execution_session_runner.rb +118 -0
- data/lib/phronomy/agent/execution/initial_preparation.rb +421 -0
- data/lib/phronomy/agent/execution/invocation_transitions.rb +86 -0
- data/lib/phronomy/agent/{phase_machine_builder.rb → execution/phase_machine_builder.rb} +22 -73
- data/lib/phronomy/agent/{provider_call_outcome.rb → execution/provider_call_outcome.rb} +9 -9
- data/lib/phronomy/agent/execution/runtime_record_encoder.rb +210 -0
- data/lib/phronomy/agent/{handoff_context.rb → handoff/handoff_context.rb} +2 -2
- data/lib/phronomy/agent/handoff/handoff_execution_coordinator.rb +15 -0
- data/lib/phronomy/agent/handoff/handoff_outcome_committer.rb +131 -0
- data/lib/phronomy/agent/{handoff_state.rb → handoff/handoff_state.rb} +1 -1
- 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/{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 -10
- data/lib/phronomy/{agent_already_exists_error.rb → agent/lifecycle_contract/agent_already_exists_error.rb} +2 -0
- data/lib/phronomy/{agent_busy_error.rb → agent/lifecycle_contract/agent_busy_error.rb} +2 -0
- data/lib/phronomy/{agent_purged_error.rb → agent/lifecycle_contract/agent_purged_error.rb} +2 -0
- data/lib/phronomy/agent/lifecycle_contract/handoff_error.rb +7 -0
- data/lib/phronomy/{stream_callback_error.rb → agent/lifecycle_contract/stream_callback_error.rb} +2 -0
- data/lib/phronomy/agent/persistence/agent_repository.rb +61 -0
- data/lib/phronomy/agent/persistence/codec.rb +358 -0
- data/lib/phronomy/agent/persistence/execution_repository.rb +108 -0
- data/lib/phronomy/agent/persistence/handoff_state_repository.rb +58 -0
- data/lib/phronomy/agent/persistence/journal_repository.rb +54 -0
- data/lib/phronomy/agent/persistence/queries.rb +61 -0
- data/lib/phronomy/agent/persistence/storage_schema.rb +24 -0
- data/lib/phronomy/agent/persistence/watermark.rb +27 -0
- data/lib/phronomy/agent/recovery/invocation_restorer.rb +132 -0
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/continuation.rb +21 -45
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/installation.rb +36 -41
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/resolution.rb +37 -40
- data/lib/phronomy/agent/{recovery_coordinator.rb → recovery/recovery_coordinator.rb} +9 -13
- data/lib/phronomy/agent/recovery/recovery_support.rb +227 -0
- data/lib/phronomy/agent/selection/candidate.rb +1 -1
- 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 +90 -0
- data/lib/phronomy/agent/{tool_call_intercepted.rb → tool_execution/tool_call_intercepted.rb} +2 -2
- data/lib/phronomy/agent/{tool_definition_set.rb → tool_execution/tool_definition_set.rb} +9 -4
- data/lib/phronomy/agent/{tool_invocation.rb → tool_execution/tool_invocation.rb} +69 -34
- data/lib/phronomy/agent/{tool_invocation_session_builder.rb → tool_execution/tool_invocation_session_builder.rb} +2 -2
- 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.rb → configuration/configuration.rb} +14 -5
- data/lib/phronomy/configuration/global_configuration.rb +26 -0
- data/lib/phronomy/content_store/storage_schema.rb +11 -0
- data/lib/phronomy/content_store/stored_contents.rb +43 -0
- data/lib/phronomy/engine/backpressure_error.rb +7 -0
- data/lib/phronomy/{blocking.rb → engine/blocking.rb} +15 -8
- data/lib/phronomy/engine/cancellation_error.rb +7 -0
- data/lib/phronomy/engine/concurrency/cancellation_token.rb +4 -0
- data/lib/phronomy/engine/concurrency/offload_pool.rb +29 -16
- data/lib/phronomy/engine/concurrency/operation_binding.rb +43 -0
- data/lib/phronomy/engine/concurrency/physical_completion_task.rb +4 -62
- data/lib/phronomy/engine/concurrency/result_collector.rb +99 -0
- data/lib/phronomy/engine/concurrency/result_composition.rb +145 -0
- data/lib/phronomy/engine/concurrency/subscriptions.rb +68 -0
- data/lib/phronomy/engine/concurrency/worker_input_restricted.rb +14 -0
- data/lib/phronomy/engine/event_loop.rb +160 -626
- data/lib/phronomy/engine/event_loop_reentrancy_error.rb +8 -0
- data/lib/phronomy/engine/execution.rb +229 -0
- data/lib/phronomy/engine/execution_cancellation_error.rb +14 -0
- data/lib/phronomy/engine/execution_receiver.rb +65 -0
- data/lib/phronomy/engine/execution_timeout_error.rb +14 -0
- data/lib/phronomy/engine/fsm_protocol.rb +14 -0
- data/lib/phronomy/engine/fsm_session.rb +38 -31
- data/lib/phronomy/{invalid_async_entry_action_error.rb → engine/invalid_async_entry_action_error.rb} +3 -1
- data/lib/phronomy/{invalid_async_transition_action_error.rb → engine/invalid_async_transition_action_error.rb} +3 -1
- data/lib/phronomy/{invalid_async_workflow_action_error.rb → engine/invalid_async_workflow_action_error.rb} +3 -1
- data/lib/phronomy/{invocation_context.rb → engine/invocation_context.rb} +13 -1
- data/lib/phronomy/engine/pool_shutdown_error.rb +7 -0
- data/lib/phronomy/engine/recursion_limit_error.rb +7 -0
- data/lib/phronomy/engine/runtime/timer_queue.rb +11 -0
- data/lib/phronomy/engine/runtime.rb +80 -84
- data/lib/phronomy/engine/runtime_shutdown_error.rb +7 -0
- data/lib/phronomy/engine/runtime_shutdown_reentrancy_error.rb +7 -0
- data/lib/phronomy/engine/scheduler_reentrancy_error.rb +9 -0
- data/lib/phronomy/engine/{task.rb → task_result.rb} +101 -42
- data/lib/phronomy/engine/timeout_error.rb +7 -0
- data/lib/phronomy/filter/contract/filter_block_error.rb +14 -0
- data/lib/phronomy/generation/generator_verifier/agent_result_receiver.rb +89 -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/base.rb +2 -2
- data/lib/phronomy/llm_context_window/token_budget.rb +6 -7
- data/lib/phronomy/llm_contract/authentication_error.rb +7 -0
- data/lib/phronomy/{context_budget_exceeded_error.rb → llm_contract/context_budget_exceeded_error.rb} +2 -0
- data/lib/phronomy/llm_contract/context_length_error.rb +7 -0
- data/lib/phronomy/llm_contract/rate_limit_error.rb +7 -0
- data/lib/phronomy/{token_usage.rb → llm_contract/token_usage.rb} +2 -2
- data/lib/phronomy/llm_contract/transport_error.rb +7 -0
- data/lib/phronomy/multi_agent/admission_registry.rb +22 -2
- data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +8 -8
- data/lib/phronomy/{agent → multi_agent}/handoff_runner.rb +18 -17
- data/lib/phronomy/multi_agent/orchestrator.rb +36 -68
- data/lib/phronomy/multi_agent/persistence/codec.rb +55 -0
- data/lib/phronomy/multi_agent/persistence/queries.rb +30 -0
- data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +108 -0
- data/lib/phronomy/multi_agent/persistence/team_repository.rb +61 -0
- data/lib/phronomy/{agent → multi_agent}/shared_state.rb +56 -39
- data/lib/phronomy/multi_agent/storage_contract/team_storage_schema.rb +15 -0
- data/lib/phronomy/multi_agent/team_coordinator.rb +21 -18
- data/lib/phronomy/multi_agent/team_execution.rb +1 -1
- data/lib/phronomy/{engine/runtime → multi_agent}/team_ownership_registry.rb +14 -4
- data/lib/phronomy/multi_agent/team_root.rb +1 -1
- data/lib/phronomy/output_parser/contract/parse_error.rb +7 -0
- data/lib/phronomy/persistence/api/persistence.rb +140 -0
- data/lib/phronomy/persistence/migration/initial_format_migration.rb +19 -19
- data/lib/phronomy/persistence_composition/repositories.rb +77 -0
- data/lib/phronomy/persistence_composition/storage_schema.rb +24 -0
- data/lib/phronomy/{execution_rehydration_required_error.rb → recovery/execution_rehydration_required_error.rb} +2 -0
- data/lib/phronomy/{recovery.rb → recovery/recovery.rb} +1 -1
- data/lib/phronomy/runtime_composition/agent_defaults.rb +7 -0
- data/lib/phronomy/runtime_composition/configuration_defaults.rb +9 -0
- data/lib/phronomy/runtime_composition/global_runtime.rb +19 -0
- data/lib/phronomy/storage/backend.rb +101 -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/scorer/llm_judge.rb +5 -3
- data/lib/phronomy/testing/fake_clock.rb +13 -9
- data/lib/phronomy/testing/persistence_contract/a_content_store.rb +1 -1
- data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +4 -4
- data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +10 -7
- data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +2 -2
- data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +6 -6
- data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +6 -6
- data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +8 -8
- data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +263 -0
- data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +123 -0
- data/lib/phronomy/testing/persistence_contract.rb +4 -0
- data/lib/phronomy/tool/contract/tool_error.rb +7 -0
- data/lib/phronomy/tools/agent.rb +6 -6
- data/lib/phronomy/vector_store/async_backend.rb +5 -5
- data/lib/phronomy/vector_store/embeddings/base.rb +2 -2
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/{workflow.rb → workflow/execution/workflow.rb} +10 -8
- data/lib/phronomy/{workflow_context.rb → workflow/execution/workflow_context.rb} +4 -0
- data/lib/phronomy/workflow/execution/workflow_context_ownership_error.rb +7 -0
- data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +188 -0
- data/lib/phronomy/{workflow_runner.rb → workflow/execution/workflow_runner.rb} +126 -75
- data/lib/phronomy/workflow/execution/workflow_terminal_policy.rb +40 -0
- data/lib/phronomy/workflow/persistence/codec.rb +153 -0
- data/lib/phronomy/workflow/persistence/state_repository.rb +57 -0
- data/lib/phronomy/workflow/phase_machine_builder.rb +8 -8
- data/lib/phronomy/workflow/storage_contract/workflow_storage_schema.rb +9 -0
- data/lib/phronomy.rb +52 -94
- data/scripts/api_snapshot.rb +4 -2
- data/scripts/storage_spi_snapshot.rb +36 -0
- data/sig/phronomy/agent.rbs +4 -5
- data/sig/phronomy/execution_receiver.rbs +34 -0
- data/sig/phronomy/extensions.rbs +5 -5
- data/sig/phronomy/handoff.rbs +4 -2
- data/sig/phronomy/multi_agent.rbs +19 -3
- data/sig/phronomy/persistence.rbs +7 -91
- data/sig/phronomy/runtime.rbs +34 -7
- data/sig/phronomy/storage.rbs +174 -0
- data/sig/phronomy/tool.rbs +11 -2
- data/sig/phronomy/workflow.rbs +1 -1
- metadata +236 -99
- data/lib/phronomy/agent/execution_coordinator.rb +0 -3138
- data/lib/phronomy/agent/handoff_execution_coordinator.rb +0 -143
- data/lib/phronomy/agent/recovery_support.rb +0 -504
- data/lib/phronomy/agent/token_budget_resolver.rb +0 -70
- data/lib/phronomy/agent/tool_executor.rb +0 -55
- data/lib/phronomy/generator_verifier.rb +0 -369
- data/lib/phronomy/invalid_context_budget_configuration_error.rb +0 -8
- data/lib/phronomy/multi_agent/fan_out_invocation.rb +0 -137
- data/lib/phronomy/multi_agent/fan_out_session_builder.rb +0 -118
- 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/ruby_llm_patches.rb +0 -24
- data/lib/phronomy/workflow_recovery.rb +0 -123
- /data/lib/phronomy/agent/{context_candidate_resolver.rb → context_assembly/context_candidate_resolver.rb} +0 -0
- /data/lib/phronomy/agent/{context_policy_input_builder.rb → context_assembly/context_policy_input_builder.rb} +0 -0
- /data/lib/phronomy/agent/{context_plan_validator.rb → context_contract/context_plan_validator.rb} +0 -0
- /data/lib/phronomy/agent/{context_policy.rb → context_contract/context_policy.rb} +0 -0
- /data/lib/phronomy/agent/{llm_operation_result.rb → execution/llm_operation_result.rb} +0 -0
- /data/lib/phronomy/agent/{handoff.rb → handoff/handoff.rb} +0 -0
- /data/lib/phronomy/agent/{handoff_capability_factory.rb → handoff/handoff_capability_factory.rb} +0 -0
- /data/lib/phronomy/agent/{handoff_policy.rb → handoff/handoff_policy.rb} +0 -0
- /data/lib/phronomy/agent/{handoff_projection.rb → handoff/handoff_projection.rb} +0 -0
- /data/lib/phronomy/agent/{handoff_request.rb → handoff/handoff_request.rb} +0 -0
- /data/lib/phronomy/{canonical_json.rb → common/canonical_json.rb} +0 -0
- /data/lib/phronomy/{diagnostics.rb → engine/diagnostics.rb} +0 -0
- /data/lib/phronomy/{event.rb → engine/event.rb} +0 -0
- /data/lib/phronomy/{metrics.rb → engine/metrics.rb} +0 -0
- /data/lib/phronomy/{runnable.rb → engine/runnable.rb} +0 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Concurrency
|
|
5
|
+
# Connects explicit invocation controls before operation admission. Its token
|
|
6
|
+
# is private: cancelling it never cancels the application's shared tokens.
|
|
7
|
+
# Used by Agent, Blocking and Orchestrator admission; it owns no worker.
|
|
8
|
+
# @api private
|
|
9
|
+
class OperationBinding
|
|
10
|
+
attr_reader :token, :scope
|
|
11
|
+
|
|
12
|
+
def initialize(invocation_context:, cancellation_token:)
|
|
13
|
+
if !invocation_context.nil? && !invocation_context.is_a?(InvocationContext)
|
|
14
|
+
raise TypeError, "invocation_context must be a Phronomy::InvocationContext or nil"
|
|
15
|
+
end
|
|
16
|
+
@scope = invocation_context&.__execution_scope
|
|
17
|
+
@token = CancellationToken.new
|
|
18
|
+
@subscriptions = Subscriptions.new
|
|
19
|
+
[invocation_context&.cancellation_token, cancellation_token].compact.uniq.each do |source|
|
|
20
|
+
@subscriptions.cancellation(source) { @token.cancel! }
|
|
21
|
+
end
|
|
22
|
+
if !@scope && invocation_context&.deadline
|
|
23
|
+
@subscriptions.after(invocation_context.deadline.remaining_seconds) { @token.cancel! }
|
|
24
|
+
end
|
|
25
|
+
@token.cancel! if @scope && !@scope.__open?
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def bind(result)
|
|
29
|
+
result.__bind_execution(@scope)
|
|
30
|
+
track(result)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def track(result)
|
|
34
|
+
@subscriptions.result(result) { @subscriptions.close }
|
|
35
|
+
result
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def close
|
|
39
|
+
@subscriptions.close
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -2,16 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
module Phronomy
|
|
4
4
|
module Concurrency
|
|
5
|
-
# Private
|
|
5
|
+
# Private TaskResult specialization used when logical completion and physical
|
|
6
6
|
# completion are intentionally different boundaries.
|
|
7
7
|
#
|
|
8
|
-
# Offload cancellation/timeout may settle the caller-facing
|
|
8
|
+
# Offload cancellation/timeout may settle the caller-facing TaskResult while a
|
|
9
9
|
# worker is still executing. EventLoop supervision must observe the later
|
|
10
10
|
# physical-completion boundary without adding domain-specific state to the
|
|
11
|
-
# public Phronomy::
|
|
11
|
+
# public Phronomy::TaskResult contract.
|
|
12
12
|
#
|
|
13
13
|
# @api private
|
|
14
|
-
class PhysicalCompletionTask < Phronomy::
|
|
14
|
+
class PhysicalCompletionTask < Phronomy::TaskResult
|
|
15
15
|
# @api private
|
|
16
16
|
def initialize(name: nil, parent: nil)
|
|
17
17
|
super
|
|
@@ -63,64 +63,6 @@ module Phronomy
|
|
|
63
63
|
self
|
|
64
64
|
end
|
|
65
65
|
|
|
66
|
-
# Preserves the physical-completion boundary across Task#map.
|
|
67
|
-
#
|
|
68
|
-
# A mapped PhysicalCompletionTask is physically complete only after both:
|
|
69
|
-
# 1. the source Task's underlying physical work is complete; and
|
|
70
|
-
# 2. the mapping callback itself has finished running.
|
|
71
|
-
#
|
|
72
|
-
# The second condition is required because Task completion callbacks may run
|
|
73
|
-
# on the OffloadPool worker that settles the source Task. Propagating the
|
|
74
|
-
# source physical signal immediately could otherwise let EventLoop declare
|
|
75
|
-
# the owning Execution quiescent while the mapping callback is still active.
|
|
76
|
-
#
|
|
77
|
-
# Logical success/failure semantics intentionally remain the same as
|
|
78
|
-
# Phronomy::Task#map.
|
|
79
|
-
#
|
|
80
|
-
# @api public
|
|
81
|
-
def map(&block)
|
|
82
|
-
raise ArgumentError, "map requires a block" unless block
|
|
83
|
-
|
|
84
|
-
mapped = self.class.deferred(name: "#{name}-mapped", parent: parent)
|
|
85
|
-
propagation_mutex = Mutex.new
|
|
86
|
-
source_physical_complete = physical_complete?
|
|
87
|
-
mapping_complete = false
|
|
88
|
-
|
|
89
|
-
mark_mapped_physical_if_ready = lambda do
|
|
90
|
-
ready = propagation_mutex.synchronize do
|
|
91
|
-
source_physical_complete && mapping_complete
|
|
92
|
-
end
|
|
93
|
-
mapped.mark_physical_complete! if ready
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
on_physical_complete do
|
|
97
|
-
propagation_mutex.synchronize { source_physical_complete = true }
|
|
98
|
-
mark_mapped_physical_if_ready.call
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
on_complete do |value, error|
|
|
102
|
-
if error
|
|
103
|
-
propagation_mutex.synchronize { mapping_complete = true }
|
|
104
|
-
mark_mapped_physical_if_ready.call
|
|
105
|
-
mapped.fail(error)
|
|
106
|
-
next
|
|
107
|
-
end
|
|
108
|
-
|
|
109
|
-
begin
|
|
110
|
-
transformed = block.call(value)
|
|
111
|
-
propagation_mutex.synchronize { mapping_complete = true }
|
|
112
|
-
mark_mapped_physical_if_ready.call
|
|
113
|
-
mapped.complete(transformed)
|
|
114
|
-
rescue => mapped_error
|
|
115
|
-
propagation_mutex.synchronize { mapping_complete = true }
|
|
116
|
-
mark_mapped_physical_if_ready.call
|
|
117
|
-
mapped.fail(mapped_error)
|
|
118
|
-
end
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
mapped
|
|
122
|
-
end
|
|
123
|
-
|
|
124
66
|
private
|
|
125
67
|
|
|
126
68
|
def deliver_physical_callback(callback)
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Concurrency
|
|
5
|
+
# Shared input-order fan-in and terminal arbitration. No application code or
|
|
6
|
+
# completion callback runs while this collector's lock is held.
|
|
7
|
+
# @api private
|
|
8
|
+
class ResultCollector
|
|
9
|
+
def initialize(size, &finished)
|
|
10
|
+
@mutex = Mutex.new
|
|
11
|
+
@outcomes = Array.new(size)
|
|
12
|
+
@remaining = size
|
|
13
|
+
@closed = false
|
|
14
|
+
@sources = {}.compare_by_identity
|
|
15
|
+
@subscriptions = Subscriptions.new
|
|
16
|
+
@finished = finished
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def while_open
|
|
20
|
+
@mutex.synchronize { yield unless @closed }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def open?
|
|
24
|
+
@mutex.synchronize { !@closed }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def watch(index, result, &recorded)
|
|
28
|
+
subscribe = @mutex.synchronize do
|
|
29
|
+
return if @closed
|
|
30
|
+
|
|
31
|
+
first = !@sources.key?(result)
|
|
32
|
+
(@sources[result] ||= {})[index] = recorded
|
|
33
|
+
first
|
|
34
|
+
end
|
|
35
|
+
if subscribe
|
|
36
|
+
@subscriptions.result(result) { receive(result) }
|
|
37
|
+
elsif result.done?
|
|
38
|
+
receive(result)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def finish(kind)
|
|
43
|
+
snapshot = @mutex.synchronize do
|
|
44
|
+
return false if @closed
|
|
45
|
+
return false if kind == :completed && @remaining > 0
|
|
46
|
+
|
|
47
|
+
@closed = true
|
|
48
|
+
snapshot_locked
|
|
49
|
+
end
|
|
50
|
+
deliver(kind, snapshot)
|
|
51
|
+
true
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def receive(result)
|
|
57
|
+
status, value, error = result.__snapshot
|
|
58
|
+
callbacks = []
|
|
59
|
+
snapshot = @mutex.synchronize do
|
|
60
|
+
return if @closed
|
|
61
|
+
|
|
62
|
+
@sources.fetch(result).each do |index, callback|
|
|
63
|
+
next if @outcomes[index]
|
|
64
|
+
|
|
65
|
+
@outcomes[index] = TaskResult::Outcome.new(index: index,
|
|
66
|
+
status: status, value: value, error: error)
|
|
67
|
+
@remaining -= 1
|
|
68
|
+
callbacks << callback if callback
|
|
69
|
+
end
|
|
70
|
+
if @remaining.zero?
|
|
71
|
+
@closed = true
|
|
72
|
+
snapshot_locked
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
deliver(:completed, snapshot) if snapshot
|
|
76
|
+
callbacks.each(&:call)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def snapshot_locked
|
|
80
|
+
@outcomes.each_with_index.map do |outcome, index|
|
|
81
|
+
outcome || TaskResult::Outcome.new(index: index,
|
|
82
|
+
status: :unfinished, value: nil, error: nil)
|
|
83
|
+
end.freeze
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def deliver(kind, snapshot)
|
|
87
|
+
finished = @mutex.synchronize do
|
|
88
|
+
@sources.clear
|
|
89
|
+
@outcomes.clear
|
|
90
|
+
callback = @finished
|
|
91
|
+
@finished = nil
|
|
92
|
+
callback
|
|
93
|
+
end
|
|
94
|
+
@subscriptions.close
|
|
95
|
+
finished.call(kind, snapshot)
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Concurrency
|
|
5
|
+
# Shared map/flat_map implementation, including the private physical-work
|
|
6
|
+
# boundary. Observing a result never mutates that result or its cancellation.
|
|
7
|
+
# @api private
|
|
8
|
+
class ResultComposition
|
|
9
|
+
def initialize(source, flatten:, &block)
|
|
10
|
+
@source = source
|
|
11
|
+
@scope = source.__execution_scope
|
|
12
|
+
@flatten = flatten
|
|
13
|
+
@block = block
|
|
14
|
+
physical = flatten || source.respond_to?(:on_physical_complete)
|
|
15
|
+
klass = physical ? PhysicalCompletionTask : TaskResult
|
|
16
|
+
@result = klass.deferred(name: "#{source.name}-#{flatten ? "flat-mapped" : "mapped"}",
|
|
17
|
+
parent: source.parent).__bind_execution(@scope)
|
|
18
|
+
@mutex = Mutex.new
|
|
19
|
+
@phase = :waiting
|
|
20
|
+
@source_physical = !source.respond_to?(:on_physical_complete)
|
|
21
|
+
@work_done = false
|
|
22
|
+
@inner_physical = true
|
|
23
|
+
@subscriptions = Subscriptions.new
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def start
|
|
27
|
+
unless @source_physical
|
|
28
|
+
@source.on_physical_complete do
|
|
29
|
+
@mutex.synchronize { @source_physical = true }
|
|
30
|
+
finish_physical
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
@subscriptions.result(@source) { receive_source }
|
|
34
|
+
if @scope
|
|
35
|
+
@subscriptions.cancellation(@scope.__cancellation_token) { suppress }
|
|
36
|
+
suppress unless @scope.__open?
|
|
37
|
+
end
|
|
38
|
+
@result
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def receive_source
|
|
44
|
+
status, value, error = @source.__snapshot
|
|
45
|
+
if status != :completed
|
|
46
|
+
started = @mutex.synchronize do
|
|
47
|
+
next false unless @phase == :waiting
|
|
48
|
+
@phase = :finished
|
|
49
|
+
@work_done = true
|
|
50
|
+
true
|
|
51
|
+
end
|
|
52
|
+
if started
|
|
53
|
+
finish_physical
|
|
54
|
+
propagate(status, value, error)
|
|
55
|
+
@subscriptions.close
|
|
56
|
+
end
|
|
57
|
+
return
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
claim = lambda do
|
|
61
|
+
@mutex.synchronize do
|
|
62
|
+
next false unless @phase == :waiting
|
|
63
|
+
@phase = :running
|
|
64
|
+
true
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
allowed = @scope ? @scope.__while_open(&claim) : claim.call
|
|
68
|
+
unless allowed
|
|
69
|
+
suppress
|
|
70
|
+
return
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
begin
|
|
74
|
+
transformed = @block.call(value)
|
|
75
|
+
if @flatten && !transformed.is_a?(TaskResult)
|
|
76
|
+
raise TypeError, "flat_map block must return a Phronomy::TaskResult"
|
|
77
|
+
end
|
|
78
|
+
track_inner_physical(transformed) if @flatten
|
|
79
|
+
@mutex.synchronize do
|
|
80
|
+
@work_done = true
|
|
81
|
+
@phase = :inner unless @phase == :suppressed
|
|
82
|
+
end
|
|
83
|
+
finish_physical
|
|
84
|
+
if @flatten
|
|
85
|
+
unless @result.done?
|
|
86
|
+
@subscriptions.result(transformed) do
|
|
87
|
+
propagate(*transformed.__snapshot)
|
|
88
|
+
@subscriptions.close
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
else
|
|
92
|
+
@result.complete(transformed)
|
|
93
|
+
@subscriptions.close
|
|
94
|
+
end
|
|
95
|
+
rescue => error
|
|
96
|
+
@mutex.synchronize { @work_done = true }
|
|
97
|
+
finish_physical
|
|
98
|
+
@result.fail(error)
|
|
99
|
+
@subscriptions.close
|
|
100
|
+
ensure
|
|
101
|
+
# Even an exception deliberately outside StandardError cannot leave
|
|
102
|
+
# already-finished Ruby work looking physically active.
|
|
103
|
+
@mutex.synchronize { @work_done = true }
|
|
104
|
+
finish_physical
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def suppress
|
|
109
|
+
@mutex.synchronize do
|
|
110
|
+
@work_done = true if @phase == :waiting
|
|
111
|
+
@phase = :suppressed
|
|
112
|
+
end
|
|
113
|
+
finish_physical
|
|
114
|
+
@result.cancel!(@scope.__cancellation_error)
|
|
115
|
+
@subscriptions.close
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def propagate(status, value, error)
|
|
119
|
+
case status
|
|
120
|
+
when :completed then @result.complete(value)
|
|
121
|
+
when :cancelled then @result.cancel!(error)
|
|
122
|
+
when :failed then @result.fail(error)
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def track_inner_physical(inner)
|
|
127
|
+
return unless inner.respond_to?(:on_physical_complete)
|
|
128
|
+
return unless inner.__execution_scope.equal?(@scope)
|
|
129
|
+
|
|
130
|
+
@mutex.synchronize { @inner_physical = false }
|
|
131
|
+
inner.on_physical_complete do
|
|
132
|
+
@mutex.synchronize { @inner_physical = true }
|
|
133
|
+
finish_physical
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def finish_physical
|
|
138
|
+
return unless @result.respond_to?(:mark_physical_complete!)
|
|
139
|
+
|
|
140
|
+
done = @mutex.synchronize { @source_physical && @work_done && @inner_physical }
|
|
141
|
+
@result.mark_physical_complete! if done
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Concurrency
|
|
5
|
+
# Disposable registrations. add also handles synchronous notification during
|
|
6
|
+
# registration: cleanup added after close is performed immediately.
|
|
7
|
+
# @api private
|
|
8
|
+
class Subscriptions
|
|
9
|
+
def initialize
|
|
10
|
+
@mutex = Mutex.new
|
|
11
|
+
@closed = false
|
|
12
|
+
@cleanup = []
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def add(&cleanup)
|
|
16
|
+
dispose = @mutex.synchronize do
|
|
17
|
+
if @closed
|
|
18
|
+
true
|
|
19
|
+
else
|
|
20
|
+
@cleanup << cleanup
|
|
21
|
+
false
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
cleanup.call if dispose
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def result(result, &callback)
|
|
28
|
+
result.on_complete(&callback)
|
|
29
|
+
add { result.__unsubscribe(callback) }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def cancellation(token, &callback)
|
|
33
|
+
return unless token
|
|
34
|
+
|
|
35
|
+
token.on_cancel(&callback)
|
|
36
|
+
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
|
+
end
|
|
44
|
+
|
|
45
|
+
def after(seconds, &callback)
|
|
46
|
+
if seconds <= 0
|
|
47
|
+
callback.call
|
|
48
|
+
return
|
|
49
|
+
end
|
|
50
|
+
timer = Phronomy::Runtime.instance.timer_queue
|
|
51
|
+
timer.schedule(seconds: seconds, &callback)
|
|
52
|
+
add { timer.cancel(callback) }
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def close
|
|
56
|
+
cleanup = @mutex.synchronize do
|
|
57
|
+
return if @closed
|
|
58
|
+
|
|
59
|
+
@closed = true
|
|
60
|
+
current = @cleanup
|
|
61
|
+
@cleanup = []
|
|
62
|
+
current
|
|
63
|
+
end
|
|
64
|
+
cleanup.each(&:call)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
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
|