phronomy 0.26.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.mutant.yml +9 -9
- data/CHANGELOG.md +92 -0
- data/CONTRIBUTING.md +8 -8
- data/README.md +13 -3
- data/VERIFY.sh +27 -27
- data/benchmark/bench_agent_invoke.rb +26 -47
- data/benchmark/bench_context_assembler.rb +17 -15
- data/benchmark/bench_regression.rb +1 -1
- data/benchmark/bench_tool_schema.rb +1 -1
- data/docs/architecture/agent-chat-and-state-ownership.md +153 -0
- data/docs/architecture/agent-configuration-and-tool-binding.md +139 -0
- data/docs/architecture/agent-context.md +4 -2
- data/docs/architecture/agent-transition-ownership.md +13 -0
- data/docs/architecture/backend-groups.md +152 -0
- data/docs/architecture/before-llm-input.md +9 -3
- data/docs/architecture/context-management.md +31 -1
- data/docs/architecture/context-preparation-steps.md +90 -0
- data/docs/architecture/entry-action-and-team-wording.md +72 -0
- data/docs/architecture/execution-abstraction-review.md +88 -0
- data/docs/architecture/execution-boundaries.md +28 -0
- data/docs/architecture/execution-metadata-and-values.md +86 -0
- data/docs/architecture/generator-verifier-ownership.md +111 -0
- data/docs/architecture/multi-agent-handoff.md +11 -4
- data/docs/architecture/persistence-refactoring-plan.md +50 -0
- data/docs/architecture/persistence.md +45 -4
- data/docs/architecture/r8-unit1.md +104 -0
- data/docs/architecture/r8-unit10.md +72 -0
- data/docs/architecture/r8-unit11.md +71 -0
- data/docs/architecture/r8-unit12.md +67 -0
- data/docs/architecture/r8-unit13.md +61 -0
- data/docs/architecture/r8-unit14.md +72 -0
- data/docs/architecture/r8-unit15.md +65 -0
- data/docs/architecture/r8-unit16.md +54 -0
- data/docs/architecture/r8-unit17.md +73 -0
- data/docs/architecture/r8-unit18.md +92 -0
- data/docs/architecture/r8-unit19.md +61 -0
- data/docs/architecture/r8-unit2.md +55 -0
- data/docs/architecture/r8-unit20.md +51 -0
- data/docs/architecture/r8-unit21.md +79 -0
- data/docs/architecture/r8-unit22.md +74 -0
- data/docs/architecture/r8-unit23.md +81 -0
- data/docs/architecture/r8-unit3.md +130 -0
- data/docs/architecture/r8-unit4.md +79 -0
- data/docs/architecture/r8-unit5.md +115 -0
- data/docs/architecture/r8-unit6.md +71 -0
- data/docs/architecture/r8-unit7.md +70 -0
- data/docs/architecture/r8-unit8.md +78 -0
- data/docs/architecture/r8-unit9.md +73 -0
- data/docs/architecture/refactoring-closure.md +343 -0
- data/docs/architecture/remaining-refactoring-plan.md +390 -0
- data/docs/architecture/rubyllm-2-token-ownership.md +82 -0
- data/docs/architecture/storage-execution-boundaries.md +54 -0
- data/docs/architecture/tool-schema-recording-gap.md +56 -0
- data/docs/architecture/tracing.md +2 -2
- data/docs/architecture/workflow-terminal-ownership-design.md +104 -0
- data/docs/architecture.md +222 -0
- data/docs/decisions/014-unified-persistence-durable-state.md +11 -0
- data/docs/decisions/015-tool-public-facade-and-rbs-boundary.md +3 -3
- data/docs/decisions/024-event-loop-single-writer-agent-runtime.md +5 -1
- data/docs/decisions/025-process-local-agent-ownership-and-runtime-admission.md +13 -3
- data/docs/decisions/026-workflow-runtime-admission-and-durable-terminal-barrier.md +5 -1
- data/docs/decisions/027-llm-adapter-provider-boundary.md +2 -0
- data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +7 -0
- data/docs/decisions/031-durable-multi-agent-coordination.md +7 -0
- data/docs/decisions/032-storage-backend-composition.md +85 -0
- data/docs/decisions/033-domain-persistence-ownership.md +79 -0
- data/docs/decisions/034-handoff-runner-coordination-ownership.md +78 -0
- data/docs/decisions/035-tool-executor-capability-ownership.md +68 -0
- data/docs/decisions/036-context-contract-ownership.md +86 -0
- data/docs/decisions/037-common-definition-ownership.md +62 -0
- data/docs/decisions/038-responsibility-based-source-layout.md +145 -0
- data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +82 -0
- data/docs/decisions/040-configuration-default-composition.md +105 -0
- data/docs/decisions/041-feature-owned-identity-registries.md +100 -0
- data/docs/decisions/042-feature-owned-execution-state.md +106 -0
- data/docs/decisions/043-storage-execution-constraint-notifications.md +75 -0
- data/docs/decisions/044-agent-default-and-one-shot-composition.md +106 -0
- data/docs/decisions/045-worker-input-restriction-ownership.md +104 -0
- data/docs/decisions/046-agent-responsibility-layout-and-shared-records.md +110 -0
- data/docs/decisions/047-recovered-execution-continuation-contract.md +116 -0
- data/docs/decisions/048-dispatch-preparation-worker-ownership.md +120 -0
- data/docs/decisions/049-initial-preparation-worker-ownership.md +111 -0
- data/docs/decisions/050-approval-resume-snapshot-and-commit-ownership.md +98 -0
- data/docs/decisions/051-execution-outcome-worker-ownership.md +153 -0
- data/docs/decisions/052-tool-invocation-restoration-ownership.md +92 -0
- data/docs/decisions/053-shared-state-coordination-ownership.md +71 -0
- data/docs/decisions/054-workflow-terminal-save-single-owner.md +76 -0
- data/docs/decisions/055-terminal-observer-failure-settlement.md +60 -0
- data/docs/decisions/056-workflow-terminal-policy-ownership.md +87 -0
- data/docs/decisions/057-storage-transaction-boundaries.md +73 -0
- data/docs/decisions/058-neutral-storage-primitives.md +83 -0
- data/docs/decisions/059-backend-contracts-and-async-clients.md +74 -0
- data/docs/decisions/060-runtime-settings-and-application-configuration.md +86 -0
- data/docs/decisions/061-persistence-failure-contracts.md +89 -0
- data/docs/decisions/062-content-store-service.md +52 -0
- data/docs/decisions/063-execution-contracts-and-services.md +88 -0
- data/docs/decisions/064-agent-execution-and-multi-agent-responsibility.md +36 -0
- data/docs/decisions/065-vector-and-embeddings-common-operations.md +38 -0
- data/docs/decisions/066-llm-and-tool-operation-contracts.md +20 -0
- data/docs/decisions/067-agent-progress-and-engine-connection.md +29 -0
- data/docs/decisions/068-agent-tool-child-progress-and-tool-operation.md +36 -0
- data/docs/decisions/README.md +52 -6
- data/docs/design/durable-semantic-coordination/IMPLEMENTATION_DESIGN_V2.md +6 -0
- data/docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md +6 -0
- data/docs/execution-boundaries-preview.md +45 -0
- data/docs/features.md +16 -5
- data/docs/getting-started.md +11 -8
- data/docs/migrations/0.15.md +5 -0
- data/docs/migrations/0.22.md +2 -2
- data/docs/migrations/durable-semantic-coordination-v2.md +8 -0
- data/docs/migrations/handoff-runner-multi-agent.md +37 -0
- data/docs/migrations/neutral-storage-spi.md +43 -0
- data/docs/migrations/parallel-tool-chat-removal.md +49 -0
- data/docs/migrations/persistence-failure-contracts.md +67 -0
- data/docs/migrations/r8-unit10.md +36 -0
- data/docs/migrations/r8-unit11.md +33 -0
- data/docs/migrations/r8-unit12.md +31 -0
- data/docs/migrations/r8-unit13.md +36 -0
- data/docs/migrations/r8-unit14.md +31 -0
- data/docs/migrations/r8-unit15.md +31 -0
- data/docs/migrations/r8-unit16.md +26 -0
- data/docs/migrations/r8-unit17.md +28 -0
- data/docs/migrations/r8-unit18.md +29 -0
- data/docs/migrations/r8-unit19.md +28 -0
- data/docs/migrations/r8-unit20.md +24 -0
- data/docs/migrations/r8-unit21.md +28 -0
- data/docs/migrations/r8-unit22.md +29 -0
- data/docs/migrations/r8-unit23.md +20 -0
- data/docs/migrations/r8-unit4.md +67 -0
- data/docs/migrations/r8-unit5.md +75 -0
- data/docs/migrations/r8-unit6.md +109 -0
- data/docs/migrations/r8-unit7.md +31 -0
- data/docs/migrations/r8-unit8.md +33 -0
- data/docs/migrations/r8-unit9.md +28 -0
- data/docs/migrations/shared-state-multi-agent.md +44 -0
- data/docs/migrations/storage-async-client.md +103 -0
- data/docs/migrations/storage-backend-composition.md +117 -0
- data/docs/migrations/storage-transaction-boundaries.md +74 -0
- data/docs/migrations/vector-async-clients.md +110 -0
- data/docs/persistence-backends.md +190 -215
- data/docs/runtime-and-concurrency.md +142 -32
- data/lib/phronomy/agent/admission.rb +78 -0
- data/lib/phronomy/{agent_already_exists_error.rb → agent/agent_already_exists_error.rb} +2 -0
- data/lib/phronomy/{agent_busy_error.rb → agent/agent_busy_error.rb} +2 -0
- data/lib/phronomy/{agent_purged_error.rb → agent/agent_purged_error.rb} +2 -0
- data/lib/phronomy/agent/api/agent.rb +17 -0
- data/lib/phronomy/{execution_rehydration_required_error.rb → agent/api/execution_rehydration_required_error.rb} +2 -0
- data/lib/phronomy/agent/async_event_api.rb +38 -33
- data/lib/phronomy/agent/base.rb +106 -294
- data/lib/phronomy/agent/before_llm_input.rb +64 -0
- data/lib/phronomy/{agent.rb → agent/composition/run_once.rb} +4 -13
- data/lib/phronomy/agent/context_candidate_resolver.rb +2 -2
- data/lib/phronomy/agent/context_importer.rb +2 -2
- data/lib/phronomy/agent/{context_assembler.rb → context_preparation.rb} +157 -288
- data/lib/phronomy/agent/control_request.rb +21 -0
- data/lib/phronomy/agent/{agent_execution.rb → execution/agent_execution.rb} +8 -4
- data/lib/phronomy/agent/{agent_invocation.rb → execution/agent_invocation.rb} +32 -63
- data/lib/phronomy/agent/execution/approval_resume_commit.rb +108 -0
- data/lib/phronomy/agent/execution/dispatch_preparation.rb +195 -0
- data/lib/phronomy/agent/{exact_execution.rb → execution/exact_execution.rb} +43 -23
- data/lib/phronomy/agent/{execution_cancellation.rb → execution/execution_cancellation.rb} +8 -5
- data/lib/phronomy/agent/execution/execution_coordinator.rb +1744 -0
- data/lib/phronomy/agent/execution/execution_failure.rb +30 -0
- data/lib/phronomy/agent/execution/execution_metadata.rb +54 -0
- data/lib/phronomy/agent/execution/execution_outcome_committer.rb +288 -0
- data/lib/phronomy/agent/execution/execution_registry.rb +463 -0
- data/lib/phronomy/agent/execution/execution_session_runner.rb +113 -0
- data/lib/phronomy/agent/execution/initial_preparation.rb +323 -0
- data/lib/phronomy/agent/execution/invocation_actions.rb +368 -0
- data/lib/phronomy/agent/execution/invocation_transitions.rb +124 -0
- data/lib/phronomy/agent/execution/runtime_record_encoder.rb +210 -0
- data/lib/phronomy/agent/execution_change.rb +161 -0
- data/lib/phronomy/agent/execution_environment.rb +36 -0
- data/lib/phronomy/agent/execution_extension_state.rb +21 -0
- data/lib/phronomy/agent/execution_observation.rb +35 -0
- data/lib/phronomy/agent/filterable.rb +182 -0
- data/lib/phronomy/agent/{handoff_context.rb → handoff/transfer_context.rb} +6 -6
- data/lib/phronomy/agent/{handoff_projection.rb → handoff/transfer_projection.rb} +14 -33
- data/lib/phronomy/agent/handoff_error.rb +7 -0
- data/lib/phronomy/agent/{journal_projection.rb → journal/journal_projection.rb} +4 -0
- data/lib/phronomy/agent/{journal_record.rb → journal/journal_record.rb} +3 -3
- data/lib/phronomy/agent/{llm_call_record.rb → journal/llm_call_record.rb} +2 -2
- data/lib/phronomy/agent/knowledge_item.rb +13 -0
- data/lib/phronomy/agent/{agent_root.rb → lifecycle/agent_root.rb} +6 -2
- data/lib/phronomy/agent/lifecycle/default_persistence.rb +29 -0
- data/lib/phronomy/{engine/runtime/agent_ownership_registry.rb → agent/lifecycle/ownership_registry.rb} +24 -15
- data/lib/phronomy/agent/persistence/agent_repository.rb +69 -0
- data/lib/phronomy/agent/persistence/cancellation_repository.rb +51 -0
- data/lib/phronomy/agent/persistence/codec.rb +338 -0
- data/lib/phronomy/agent/persistence/execution_repository.rb +124 -0
- data/lib/phronomy/agent/persistence/journal_repository.rb +63 -0
- data/lib/phronomy/agent/persistence/records.rb +33 -0
- data/lib/phronomy/agent/persistence/retention_repository.rb +70 -0
- data/lib/phronomy/agent/persistence/storage_schema.rb +29 -0
- data/lib/phronomy/agent/persistence/watermark.rb +29 -0
- data/lib/phronomy/agent/recovery/invocation_restorer.rb +124 -0
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/continuation.rb +28 -51
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/installation.rb +50 -55
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/resolution.rb +38 -43
- data/lib/phronomy/agent/{recovery_coordinator.rb → recovery/recovery_coordinator.rb} +26 -23
- data/lib/phronomy/agent/recovery/recovery_rules.rb +119 -0
- data/lib/phronomy/agent/recovery/recovery_support.rb +224 -0
- data/lib/phronomy/agent/reserved_execution.rb +16 -0
- data/lib/phronomy/agent/retention.rb +17 -0
- data/lib/phronomy/agent/runtime_binding/engine_environment.rb +60 -0
- data/lib/phronomy/agent/runtime_binding/engine_session_builder.rb +34 -0
- data/lib/phronomy/agent/runtime_binding/phase_machine_builder.rb +64 -0
- data/lib/phronomy/agent/runtime_binding/tool_session_builder.rb +34 -0
- data/lib/phronomy/agent/{ruby_llm_materializer.rb → runtime_input.rb} +19 -39
- data/lib/phronomy/agent/saved_context_reader.rb +55 -0
- data/lib/phronomy/agent/settings.rb +47 -0
- data/lib/phronomy/agent/state_writer.rb +166 -0
- data/lib/phronomy/agent/store.rb +253 -0
- data/lib/phronomy/{stream_callback_error.rb → agent/stream_callback_error.rb} +2 -0
- data/lib/phronomy/agent/{approval_evaluation_request.rb → tool_execution/approval_evaluation_request.rb} +1 -12
- data/lib/phronomy/agent/{tool_approval_request.rb → tool_execution/tool_approval_request.rb} +1 -10
- data/lib/phronomy/agent/tool_execution/tool_binding.rb +30 -0
- data/lib/phronomy/agent/{tool_invocation.rb → tool_execution/tool_invocation.rb} +114 -248
- data/lib/phronomy/agent/tool_execution/tool_invocation_actions.rb +107 -0
- data/lib/phronomy/agent/tool_execution/tool_invocation_transitions.rb +70 -0
- data/lib/phronomy/common/configuration_error.rb +7 -0
- data/lib/phronomy/common/error.rb +5 -0
- data/lib/phronomy/{agent → common/values}/immutable.rb +9 -1
- data/lib/phronomy/common/values/serializable.rb +32 -0
- data/lib/phronomy/configuration/runtime_settings.rb +49 -0
- data/lib/phronomy/content_store/storage_schema.rb +12 -0
- data/lib/phronomy/content_store/stored_contents.rb +44 -0
- data/lib/phronomy/context/assembly.rb +202 -0
- data/lib/phronomy/{context_budget_exceeded_error.rb → context/budget_exceeded_error.rb} +5 -1
- data/lib/phronomy/context/candidate.rb +51 -0
- data/lib/phronomy/context/constraint.rb +47 -0
- data/lib/phronomy/{agent → context}/context_plan.rb +2 -2
- data/lib/phronomy/{agent → context}/context_plan_validator.rb +3 -3
- data/lib/phronomy/{agent → context}/context_policy.rb +1 -1
- data/lib/phronomy/{agent → context}/context_policy_input.rb +7 -7
- data/lib/phronomy/{agent → context}/context_policy_input_builder.rb +1 -1
- data/lib/phronomy/context/default_policy.rb +140 -0
- data/lib/phronomy/context/final_budget_validator.rb +33 -0
- data/lib/phronomy/{agent → context}/llm_input_build_context.rb +2 -2
- data/lib/phronomy/{agent → context}/llm_input_manifest.rb +21 -21
- data/lib/phronomy/{agent → context}/llm_input_patch.rb +3 -3
- data/lib/phronomy/context/manifest_error.rb +10 -0
- data/lib/phronomy/context/prompt_template.rb +98 -0
- data/lib/phronomy/{vector_store → documents}/loader/base.rb +1 -1
- data/lib/phronomy/{vector_store → documents}/loader/csv_loader.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/loader/markdown_loader.rb +3 -3
- data/lib/phronomy/{vector_store → documents}/loader/plain_text_loader.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/splitter/base.rb +1 -1
- data/lib/phronomy/{vector_store → documents}/splitter/fixed_size_splitter.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/splitter/recursive_splitter.rb +2 -2
- data/lib/phronomy/embeddings/async/async_client.rb +44 -0
- data/lib/phronomy/embeddings/backends/ruby_llm_embeddings.rb +48 -0
- data/lib/phronomy/embeddings/base.rb +43 -0
- data/lib/phronomy/embeddings/invalid_result_error.rb +11 -0
- data/lib/phronomy/embeddings/transport_error.rb +11 -0
- data/lib/phronomy/engine/concurrency/offload_pool.rb +39 -20
- data/lib/phronomy/engine/concurrency/pool_registry.rb +47 -12
- data/lib/phronomy/{diagnostics.rb → engine/diagnostics.rb} +1 -1
- data/lib/phronomy/engine/event_loop.rb +164 -630
- data/lib/phronomy/engine/execution_binding.rb +23 -0
- data/lib/phronomy/engine/execution_receiver_binding.rb +43 -0
- data/lib/phronomy/engine/fsm_protocol.rb +9 -0
- data/lib/phronomy/engine/fsm_session.rb +37 -30
- data/lib/phronomy/engine/metrics.rb +28 -0
- data/lib/phronomy/engine/runtime/timer_queue.rb +1 -1
- data/lib/phronomy/engine/runtime.rb +104 -95
- data/lib/phronomy/engine/terminal_decision.rb +7 -0
- data/lib/phronomy/execution/async_operation.rb +41 -0
- data/lib/phronomy/execution/backpressure_error.rb +7 -0
- data/lib/phronomy/{blocking.rb → execution/blocking.rb} +2 -2
- data/lib/phronomy/execution/cancellation_error.rb +7 -0
- data/lib/phronomy/{engine → execution}/concurrency/cancellation_token.rb +5 -1
- data/lib/phronomy/{engine → execution}/concurrency/deadline.rb +0 -17
- data/lib/phronomy/{engine → execution}/concurrency/physical_completion_task.rb +1 -1
- data/lib/phronomy/{engine → execution}/concurrency/result_collector.rb +1 -1
- data/lib/phronomy/{engine → execution}/concurrency/result_composition.rb +33 -12
- data/lib/phronomy/{engine/concurrency/subscriptions.rb → execution/concurrency/result_subscriptions.rb} +14 -18
- data/lib/phronomy/execution/concurrency/subscriptions.rb +25 -0
- data/lib/phronomy/execution/concurrency/worker_input_restricted.rb +14 -0
- data/lib/phronomy/execution/event_loop_reentrancy_error.rb +8 -0
- data/lib/phronomy/{execution.rb → execution/execution.rb} +57 -30
- data/lib/phronomy/{execution_cancellation_error.rb → execution/execution_cancellation_error.rb} +2 -0
- data/lib/phronomy/execution/execution_receiver.rb +78 -0
- data/lib/phronomy/execution/execution_scope_state.rb +35 -0
- data/lib/phronomy/{execution_timeout_error.rb → execution/execution_timeout_error.rb} +2 -0
- data/lib/phronomy/{invalid_async_entry_action_error.rb → execution/invalid_async_entry_action_error.rb} +2 -0
- data/lib/phronomy/{invalid_async_transition_action_error.rb → execution/invalid_async_transition_action_error.rb} +2 -0
- data/lib/phronomy/{invalid_async_workflow_action_error.rb → execution/invalid_async_workflow_action_error.rb} +2 -0
- data/lib/phronomy/{invocation_context.rb → execution/invocation_context.rb} +0 -15
- data/lib/phronomy/execution/invocation_controls.rb +73 -0
- data/lib/phronomy/execution/pool_shutdown_error.rb +7 -0
- data/lib/phronomy/execution/recursion_limit_error.rb +7 -0
- data/lib/phronomy/execution/runnable.rb +24 -0
- data/lib/phronomy/execution/runtime_shutdown_error.rb +7 -0
- data/lib/phronomy/execution/runtime_shutdown_reentrancy_error.rb +7 -0
- data/lib/phronomy/execution/scheduler_reentrancy_error.rb +9 -0
- data/lib/phronomy/{engine → execution}/task_result.rb +7 -3
- data/lib/phronomy/execution/timeout_error.rb +7 -0
- data/lib/phronomy/execution/wait_policy.rb +24 -0
- data/lib/phronomy/filter/filter_block_error.rb +14 -0
- data/lib/phronomy/generation/generator_verifier/agent_operation.rb +65 -0
- data/lib/phronomy/generation/generator_verifier/agent_result_receiver.rb +89 -0
- data/lib/phronomy/generation/generator_verifier/default_parser.rb +27 -0
- data/lib/phronomy/generation/generator_verifier/pipeline_state.rb +57 -0
- data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +112 -0
- data/lib/phronomy/generation/generator_verifier.rb +118 -0
- data/lib/phronomy/generation/low_confidence_error.rb +14 -0
- data/lib/phronomy/llm_adapter/async/async_client.rb +47 -0
- data/lib/phronomy/llm_adapter/authentication_error.rb +9 -0
- data/lib/phronomy/llm_adapter/backends/ruby_llm.rb +183 -0
- data/lib/phronomy/llm_adapter/base.rb +49 -68
- data/lib/phronomy/llm_adapter/context_length_error.rb +9 -0
- data/lib/phronomy/llm_adapter/invalid_result_error.rb +10 -0
- data/lib/phronomy/llm_adapter/message.rb +62 -0
- data/lib/phronomy/llm_adapter/rate_limit_error.rb +9 -0
- data/lib/phronomy/llm_adapter/request.rb +79 -0
- data/lib/phronomy/llm_adapter/response.rb +36 -0
- data/lib/phronomy/llm_adapter/stream_chunk.rb +18 -0
- data/lib/phronomy/llm_adapter/token_usage.rb +64 -0
- data/lib/phronomy/llm_adapter/transport_error.rb +9 -0
- data/lib/phronomy/llm_adapter.rb +3 -16
- data/lib/phronomy/llm_context_window/token_budget.rb +6 -7
- data/lib/phronomy/multi_agent/admission_registry.rb +15 -2
- data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +108 -84
- data/lib/phronomy/multi_agent/execution_environment.rb +29 -0
- data/lib/phronomy/{agent → multi_agent}/handoff.rb +2 -2
- data/lib/phronomy/{agent → multi_agent}/handoff_capability_factory.rb +21 -2
- data/lib/phronomy/multi_agent/handoff_participant.rb +79 -0
- data/lib/phronomy/{agent → multi_agent}/handoff_policy.rb +1 -1
- data/lib/phronomy/multi_agent/handoff_runner.rb +156 -0
- data/lib/phronomy/{agent → multi_agent}/handoff_state.rb +2 -2
- data/lib/phronomy/multi_agent/orchestrator.rb +61 -75
- data/lib/phronomy/multi_agent/persistence/codec.rb +75 -0
- data/lib/phronomy/multi_agent/persistence/handoff_state_repository.rb +77 -0
- data/lib/phronomy/multi_agent/persistence/records.rb +27 -0
- data/lib/phronomy/multi_agent/persistence/storage_schema.rb +21 -0
- data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +124 -0
- data/lib/phronomy/multi_agent/persistence/team_repository.rb +69 -0
- data/lib/phronomy/multi_agent/reserved_child_admission.rb +88 -0
- data/lib/phronomy/multi_agent/runtime_binding/engine_environment.rb +35 -0
- data/lib/phronomy/{agent → multi_agent}/shared_state.rb +56 -39
- data/lib/phronomy/multi_agent/store.rb +156 -0
- data/lib/phronomy/multi_agent/team_coordinator.rb +87 -75
- data/lib/phronomy/multi_agent/team_execution.rb +1 -1
- data/lib/phronomy/{engine/runtime → multi_agent}/team_ownership_registry.rb +5 -5
- data/lib/phronomy/multi_agent/team_root.rb +1 -1
- data/lib/phronomy/output_parser/parse_error.rb +7 -0
- data/lib/phronomy/persistence/content_repository.rb +26 -0
- data/lib/phronomy/persistence/errors.rb +16 -0
- data/lib/phronomy/persistence/migration/initial_format_migration.rb +21 -21
- data/lib/phronomy/persistence/persistence.rb +23 -0
- data/lib/phronomy/persistence/save_outcome.rb +24 -0
- data/lib/phronomy/persistence/snapshot_comparison.rb +81 -0
- data/lib/phronomy/persistence/storage_boundary.rb +32 -0
- data/lib/phronomy/persistence/transaction.rb +86 -0
- data/lib/phronomy/persistence_composition/storage_schema.rb +29 -0
- data/lib/phronomy/persistence_composition/stores.rb +34 -0
- data/lib/phronomy/persistence_composition/unit4_migration.rb +276 -0
- data/lib/phronomy/runtime_composition/agent_defaults.rb +11 -0
- data/lib/phronomy/runtime_composition/configuration.rb +146 -0
- data/lib/phronomy/runtime_composition/configuration_defaults.rb +9 -0
- data/lib/phronomy/runtime_composition/evaluation_defaults.rb +7 -0
- data/lib/phronomy/runtime_composition/execution_defaults.rb +5 -0
- data/lib/phronomy/runtime_composition/generation_defaults.rb +6 -0
- data/lib/phronomy/runtime_composition/global_configuration.rb +38 -0
- data/lib/phronomy/runtime_composition/global_runtime.rb +19 -0
- data/lib/phronomy/runtime_composition/multi_agent_defaults.rb +9 -0
- data/lib/phronomy/runtime_composition/workflow_defaults.rb +4 -0
- data/lib/phronomy/storage/async/async_client.rb +60 -0
- data/lib/phronomy/storage/backend.rb +107 -0
- data/lib/phronomy/storage/backends/in_memory.rb +157 -0
- data/lib/phronomy/storage/blob_conflict_error.rb +10 -0
- data/lib/phronomy/storage/blobs.rb +31 -0
- data/lib/phronomy/storage/condition.rb +25 -0
- data/lib/phronomy/storage/condition_failed_error.rb +16 -0
- data/lib/phronomy/storage/conflict_error.rb +9 -0
- data/lib/phronomy/{persistence → storage}/durable_record.rb +12 -12
- data/lib/phronomy/storage/entry.rb +33 -0
- data/lib/phronomy/storage/guard_ref.rb +13 -0
- data/lib/phronomy/storage/not_found_error.rb +9 -0
- data/lib/phronomy/storage/record_codec.rb +177 -0
- data/lib/phronomy/storage/records.rb +61 -0
- data/lib/phronomy/storage/resource.rb +126 -0
- data/lib/phronomy/storage/scope.rb +25 -0
- data/lib/phronomy/storage/serialization_error.rb +9 -0
- data/lib/phronomy/storage/streams.rb +44 -0
- data/lib/phronomy/storage/transaction_error.rb +10 -0
- data/lib/phronomy/storage/unique_constraint_error.rb +17 -0
- data/lib/phronomy/storage/unsupported_backend_error.rb +9 -0
- data/lib/phronomy/storage/validation.rb +53 -0
- data/lib/phronomy/storage/view.rb +131 -0
- data/lib/phronomy/testing/eval/metrics.rb +1 -1
- data/lib/phronomy/testing/eval/scorer/llm_judge.rb +19 -3
- data/lib/phronomy/testing/persistence_contract/a_content_store.rb +1 -1
- data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +2 -2
- data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +54 -48
- data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +1 -1
- data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +1 -1
- data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +5 -5
- data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +52 -46
- data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +263 -0
- data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +124 -0
- data/lib/phronomy/testing/persistence_contract.rb +4 -0
- data/lib/phronomy/tool/authorization.rb +77 -0
- data/lib/phronomy/tool/base.rb +549 -9
- data/lib/phronomy/tool/call_request.rb +40 -0
- data/lib/phronomy/{agent/tool_definition_set.rb → tool/definition_set.rb} +16 -12
- data/lib/phronomy/tool/operation.rb +88 -0
- data/lib/phronomy/tool/schema.rb +214 -0
- data/lib/phronomy/tool/settings.rb +31 -0
- data/lib/phronomy/tool/tool_error.rb +7 -0
- data/lib/phronomy/{agent → tool}/tool_executor.rb +10 -7
- data/lib/phronomy/tools/agent.rb +22 -90
- data/lib/phronomy/tools/mcp.rb +21 -14
- data/lib/phronomy/tools/vector_search.rb +4 -4
- data/lib/phronomy/tracing/automatic.rb +11 -6
- data/lib/phronomy/tracing/base.rb +1 -1
- data/lib/phronomy/tracing/observation.rb +37 -0
- data/lib/phronomy/tracing/settings.rb +36 -0
- data/lib/phronomy/vector_store/{async_backend.rb → async/async_client.rb} +35 -29
- data/lib/phronomy/vector_store/{in_memory.rb → backends/in_memory.rb} +12 -10
- data/lib/phronomy/vector_store/{pgvector.rb → backends/pgvector.rb} +44 -16
- data/lib/phronomy/vector_store/{redis_search.rb → backends/redis_search.rb} +74 -37
- data/lib/phronomy/vector_store/base.rb +118 -49
- data/lib/phronomy/vector_store/invalid_result_error.rb +12 -0
- data/lib/phronomy/vector_store.rb +2 -17
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/workflow/completion.rb +11 -0
- data/lib/phronomy/{workflow.rb → workflow/execution/workflow.rb} +10 -8
- data/lib/phronomy/workflow/execution/workflow_action_rules.rb +67 -0
- data/lib/phronomy/{workflow_context.rb → workflow/execution/workflow_context.rb} +5 -1
- data/lib/phronomy/workflow/execution/workflow_context_ownership_error.rb +7 -0
- data/lib/phronomy/workflow/execution/workflow_execution_environment.rb +27 -0
- data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +186 -0
- data/lib/phronomy/{workflow_runner.rb → workflow/execution/workflow_runner.rb} +145 -115
- data/lib/phronomy/workflow/execution/workflow_settings.rb +28 -0
- data/lib/phronomy/workflow/persistence/codec.rb +153 -0
- data/lib/phronomy/workflow/persistence/state_repository.rb +63 -0
- data/lib/phronomy/workflow/persistence/storage_schema.rb +13 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_engine_environment.rb +38 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb +156 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_terminal_policy.rb +40 -0
- data/lib/phronomy.rb +97 -98
- data/scripts/api_snapshot.rb +36 -16
- data/scripts/storage_spi_snapshot.rb +36 -0
- data/sig/_private/phronomy/agent/settings.rbs +19 -0
- data/sig/_private/phronomy/configuration/runtime_settings.rbs +20 -0
- data/sig/_private/phronomy/llm_adapter/async/async_client.rbs +10 -0
- data/sig/_private/phronomy/multi_agent/store.rbs +8 -0
- data/sig/_private/phronomy/persistence/storage_boundary.rbs +7 -0
- data/sig/_private/phronomy/tool/settings.rbs +13 -0
- data/sig/_private/phronomy/tracing/settings.rbs +15 -0
- data/sig/_private/phronomy/workflow/execution/workflow_settings.rbs +13 -0
- data/sig/phronomy/agent/admission.rbs +19 -0
- data/sig/phronomy/agent/execution_contract.rbs +132 -0
- data/sig/phronomy/agent/execution_environment.rbs +20 -0
- data/sig/phronomy/agent/store.rbs +73 -0
- data/sig/phronomy/agent.rbs +14 -139
- data/sig/phronomy/common.rbs +7 -0
- data/sig/phronomy/content_store/contracts.rbs +13 -0
- data/sig/phronomy/context.rbs +152 -0
- data/sig/phronomy/embeddings/async/async_client.rbs +9 -0
- data/sig/phronomy/embeddings/base.rbs +17 -0
- data/sig/phronomy/engine/concurrency/timer_bindings.rbs +22 -0
- data/sig/phronomy/engine/control_bindings.rbs +11 -0
- data/sig/phronomy/engine/control_protocols.rbs +23 -0
- data/sig/phronomy/engine/execution_binding.rbs +6 -0
- data/sig/phronomy/engine/execution_receiver.rbs +15 -0
- data/sig/phronomy/engine/observability.rbs +13 -0
- data/sig/phronomy/execution/async_operation.rbs +7 -0
- data/sig/phronomy/execution/blocking.rbs +5 -0
- data/sig/phronomy/execution/concurrency/cancellation_token.rbs +13 -0
- data/sig/phronomy/execution/concurrency/deadline.rbs +10 -0
- data/sig/phronomy/execution/concurrency/result_composition.rbs +7 -0
- data/sig/phronomy/execution/control_protocols.rbs +18 -0
- data/sig/phronomy/execution/errors.rbs +7 -0
- data/sig/phronomy/execution/execution.rbs +41 -0
- data/sig/phronomy/execution/execution_receiver.rbs +40 -0
- data/sig/phronomy/execution/execution_scope_state.rbs +9 -0
- data/sig/phronomy/execution/invocation_context.rbs +18 -0
- data/sig/phronomy/execution/invocation_controls.rbs +13 -0
- data/sig/phronomy/execution/task_result.rbs +29 -0
- data/sig/phronomy/extensions.rbs +0 -21
- data/sig/phronomy/handoff.rbs +12 -4
- data/sig/phronomy/llm_adapter/backends/ruby_llm.rbs +6 -0
- data/sig/phronomy/llm_adapter/base.rbs +14 -0
- data/sig/phronomy/llm_adapter/usage.rbs +14 -0
- data/sig/phronomy/llm_adapter/values.rbs +43 -0
- data/sig/phronomy/llm_context_window.rbs +10 -0
- data/sig/phronomy/multi_agent/reserved_child_admission.rbs +8 -0
- data/sig/phronomy/multi_agent/store.rbs +54 -0
- data/sig/phronomy/multi_agent.rbs +24 -3
- data/sig/phronomy/persistence/errors.rbs +18 -0
- data/sig/phronomy/persistence/migration.rbs +13 -0
- data/sig/phronomy/persistence/transaction.rbs +19 -0
- data/sig/phronomy/persistence.rbs +3 -185
- data/sig/phronomy/persistence_composition.rbs +19 -0
- data/sig/phronomy/runtime_composition/configuration.rbs +36 -0
- data/sig/phronomy/storage/async/async_client.rbs +8 -0
- data/sig/phronomy/storage/backends/in_memory.rbs +9 -0
- data/sig/phronomy/storage/contracts.rbs +160 -0
- data/sig/phronomy/tool.rbs +58 -5
- data/sig/phronomy/tracing/observation.rbs +7 -0
- data/sig/phronomy/vector_store/async/async_client.rbs +13 -0
- data/sig/phronomy/vector_store/base.rbs +23 -0
- data/sig/phronomy/workflow/execution/workflow_execution_environment.rbs +14 -0
- data/sig/phronomy/workflow/runtime_binding/workflow_terminal_policy.rbs +7 -0
- data/sig/phronomy/workflow/store.rbs +8 -0
- data/sig/phronomy/workflow.rbs +1 -1
- data/sig/phronomy.rbs +0 -49
- metadata +462 -130
- data/lib/phronomy/agent/agent_invocation_session_builder.rb +0 -584
- data/lib/phronomy/agent/concerns/before_llm_input.rb +0 -66
- data/lib/phronomy/agent/concerns/error_translation.rb +0 -45
- data/lib/phronomy/agent/concerns/filterable.rb +0 -184
- data/lib/phronomy/agent/context/capability/base.rb +0 -507
- data/lib/phronomy/agent/context/instruction/prompt_template.rb +0 -102
- data/lib/phronomy/agent/context_parts/validators/final_budget_validator.rb +0 -37
- data/lib/phronomy/agent/context_policies/default.rb +0 -142
- data/lib/phronomy/agent/execution_coordinator.rb +0 -3151
- data/lib/phronomy/agent/handoff_execution_coordinator.rb +0 -143
- data/lib/phronomy/agent/handoff_request.rb +0 -45
- data/lib/phronomy/agent/handoff_runner.rb +0 -178
- data/lib/phronomy/agent/phase_machine_builder.rb +0 -152
- data/lib/phronomy/agent/provider_call_outcome.rb +0 -106
- data/lib/phronomy/agent/recovery_support.rb +0 -504
- data/lib/phronomy/agent/selection/candidate.rb +0 -53
- data/lib/phronomy/agent/selection/constraint.rb +0 -49
- data/lib/phronomy/agent/token_budget_resolver.rb +0 -70
- data/lib/phronomy/agent/tool_call_intercepted.rb +0 -29
- data/lib/phronomy/agent/tool_invocation_session_builder.rb +0 -276
- data/lib/phronomy/configuration.rb +0 -59
- data/lib/phronomy/engine/concurrency/cancellation_scope.rb +0 -124
- data/lib/phronomy/engine/concurrency/operation_binding.rb +0 -43
- data/lib/phronomy/generator_verifier.rb +0 -369
- data/lib/phronomy/invalid_context_budget_configuration_error.rb +0 -8
- data/lib/phronomy/llm_adapter/ruby_llm.rb +0 -47
- data/lib/phronomy/metrics.rb +0 -26
- data/lib/phronomy/multi_agent/parallel_tool_chat.rb +0 -116
- data/lib/phronomy/persistence/durable_codec.rb +0 -706
- data/lib/phronomy/persistence/in_memory.rb +0 -690
- data/lib/phronomy/persistence/repository_facades.rb +0 -535
- data/lib/phronomy/persistence.rb +0 -276
- data/lib/phronomy/recovery.rb +0 -186
- data/lib/phronomy/ruby_llm_patches.rb +0 -24
- data/lib/phronomy/runnable.rb +0 -54
- data/lib/phronomy/token_usage.rb +0 -85
- data/lib/phronomy/vector_store/embeddings/base.rb +0 -44
- data/lib/phronomy/vector_store/embeddings/ruby_llm_embeddings.rb +0 -47
- data/lib/phronomy/workflow/phase_machine_builder.rb +0 -205
- data/lib/phronomy/workflow_recovery.rb +0 -123
- data/sig/phronomy/llm_adapter.rbs +0 -11
- data/sig/phronomy/runtime.rbs +0 -76
- /data/lib/phronomy/agent/{llm_operation_result.rb → execution/llm_operation_result.rb} +0 -0
- /data/lib/phronomy/{canonical_json.rb → common/canonical_json.rb} +0 -0
- /data/lib/phronomy/{event.rb → engine/event.rb} +0 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "forwardable"
|
|
4
|
+
require_relative "../configuration/runtime_settings"
|
|
5
|
+
require_relative "../agent/settings"
|
|
6
|
+
require_relative "../workflow/execution/workflow_settings"
|
|
7
|
+
require_relative "../tool/settings"
|
|
8
|
+
require_relative "../tracing/settings"
|
|
9
|
+
|
|
10
|
+
module Phronomy
|
|
11
|
+
# Application-facing configuration composes domain options and neutral runtime
|
|
12
|
+
# settings. Public accessors and constructor behavior stay unchanged.
|
|
13
|
+
class Configuration
|
|
14
|
+
extend Forwardable
|
|
15
|
+
|
|
16
|
+
DEFAULT_FACTORIES = {}
|
|
17
|
+
private_constant :DEFAULT_FACTORIES
|
|
18
|
+
|
|
19
|
+
# Bind fresh-instance factories once during application loading. Concrete
|
|
20
|
+
# component selection belongs to runtime_composition, not configuration.
|
|
21
|
+
# Applications replace instances through the existing configuration writers.
|
|
22
|
+
# @api private
|
|
23
|
+
def self.install_default_factories(tracer:, llm_adapter:)
|
|
24
|
+
DEFAULT_FACTORIES.replace(tracer: tracer, llm_adapter: llm_adapter).freeze
|
|
25
|
+
nil
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
attr_accessor :default_embedding_model, :multi_agent_store
|
|
29
|
+
|
|
30
|
+
# Preserve the public settings facade while each domain owns its values.
|
|
31
|
+
def_delegators :@__agent_settings, :default_model, :default_model=
|
|
32
|
+
def_delegators :@__agent_settings, :before_llm_input, :before_llm_input=
|
|
33
|
+
def_delegators :@__agent_settings, :agent_store, :agent_store=
|
|
34
|
+
def_delegators :@__agent_settings, :llm_adapter, :llm_adapter=
|
|
35
|
+
def_delegators :@__agent_settings, :stream_callback_error_policy, :stream_callback_error_policy=
|
|
36
|
+
def_delegators :@__agent_settings, :authorization_timeout, :authorization_timeout=
|
|
37
|
+
def_delegators :@__workflow_settings, :recursion_limit, :recursion_limit=
|
|
38
|
+
def_delegators :@__workflow_settings, :workflow_store, :workflow_store=
|
|
39
|
+
def_delegators :@__runtime_settings, :authorization_pool_size, :authorization_pool_size=
|
|
40
|
+
def_delegators :@__runtime_settings, :authorization_queue_size, :authorization_queue_size=
|
|
41
|
+
|
|
42
|
+
# @api private
|
|
43
|
+
attr_reader :__agent_settings, :__workflow_settings, :__tool_settings, :__tracing_settings
|
|
44
|
+
|
|
45
|
+
# Engine access is bound by the composition root, not by Engine itself.
|
|
46
|
+
# @api private
|
|
47
|
+
attr_reader :__runtime_settings
|
|
48
|
+
|
|
49
|
+
def tracer
|
|
50
|
+
@__tracing_settings.tracer
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def tracer=(value)
|
|
54
|
+
@__tracing_settings.tracer = value
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def trace_pii
|
|
58
|
+
@__tracing_settings.trace_pii
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def trace_pii=(value)
|
|
62
|
+
@__tracing_settings.trace_pii = value
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def logger
|
|
66
|
+
@__runtime_settings.logger
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def logger=(value)
|
|
70
|
+
@__runtime_settings.logger = value
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def tool_result_max_size
|
|
74
|
+
@__tool_settings.max_result_size
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def tool_result_max_size=(value)
|
|
78
|
+
@__tool_settings.max_result_size = value
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def event_loop_stop_grace_seconds
|
|
82
|
+
@__runtime_settings.event_loop_stop_grace_seconds
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def event_loop_stop_grace_seconds=(value)
|
|
86
|
+
@__runtime_settings.event_loop_stop_grace_seconds = value
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def event_loop_starvation_threshold_seconds
|
|
90
|
+
@__runtime_settings.event_loop_starvation_threshold_seconds
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def event_loop_starvation_threshold_seconds=(value)
|
|
94
|
+
@__runtime_settings.event_loop_starvation_threshold_seconds = value
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def event_loop_dispatch_threshold_seconds
|
|
98
|
+
@__runtime_settings.event_loop_dispatch_threshold_seconds
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def event_loop_dispatch_threshold_seconds=(value)
|
|
102
|
+
@__runtime_settings.event_loop_dispatch_threshold_seconds = value
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def offload_pool_size
|
|
106
|
+
@__runtime_settings.offload_pool_size
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def offload_pool_size=(value)
|
|
110
|
+
@__runtime_settings.offload_pool_size = value
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def offload_queue_size
|
|
114
|
+
@__runtime_settings.offload_queue_size
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def offload_queue_size=(value)
|
|
118
|
+
@__runtime_settings.offload_queue_size = value
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def initialize
|
|
122
|
+
@__workflow_settings = WorkflowSettings.new(recursion_limit: 25)
|
|
123
|
+
@__runtime_settings = RuntimeSettings.new
|
|
124
|
+
@__tool_settings = Tool::Settings.new
|
|
125
|
+
@__tracing_settings = Tracing::Settings.new(tracer: DEFAULT_FACTORIES.fetch(:tracer).call)
|
|
126
|
+
@__agent_settings = Agent::Settings.new(
|
|
127
|
+
llm_adapter: DEFAULT_FACTORIES.fetch(:llm_adapter).call,
|
|
128
|
+
stream_callback_error_policy: :report, authorization_timeout: 5
|
|
129
|
+
)
|
|
130
|
+
@multi_agent_store = nil
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
private
|
|
134
|
+
|
|
135
|
+
# A scoped configuration copy owns its scalar settings while retaining the
|
|
136
|
+
# same injected tracer/logger identities, just like the previous flat object.
|
|
137
|
+
def initialize_copy(original)
|
|
138
|
+
super
|
|
139
|
+
@__runtime_settings = original.__runtime_settings.dup
|
|
140
|
+
@__agent_settings = original.__agent_settings.dup
|
|
141
|
+
@__workflow_settings = original.__workflow_settings.dup
|
|
142
|
+
@__tool_settings = original.__tool_settings.dup
|
|
143
|
+
@__tracing_settings = original.__tracing_settings.dup
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Select concrete defaults at the application composition boundary. Each factory
|
|
4
|
+
# runs during Configuration.new, not during binding; no Runtime is started here.
|
|
5
|
+
# Explicitly loaded after Zeitwerk setup and before global configuration access.
|
|
6
|
+
Phronomy::Configuration.install_default_factories(
|
|
7
|
+
tracer: -> { Phronomy::Tracing::NullTracer.new },
|
|
8
|
+
llm_adapter: -> { Phronomy::LLMAdapter::RubyLLM.new }
|
|
9
|
+
)
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Binding creates no client, Configuration or Runtime. Resolve the configured
|
|
4
|
+
# adapter for every score at the existing read point; do not cache the client.
|
|
5
|
+
Phronomy::Testing::Eval::Scorer::LlmJudge.install_client_factory(
|
|
6
|
+
-> { Phronomy::LLMAdapter::AsyncClient.new(adapter: Phronomy.configuration.llm_adapter) }
|
|
7
|
+
)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Global configuration access, replacement, and scoped overrides.
|
|
4
|
+
# Explicitly loaded by the application entry point; not a common definition.
|
|
5
|
+
module Phronomy
|
|
6
|
+
class << self
|
|
7
|
+
def configuration
|
|
8
|
+
@configuration ||= Configuration.new
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def configure
|
|
12
|
+
yield configuration
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def reset_configuration!
|
|
16
|
+
@configuration = Configuration.new
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def with_configuration
|
|
20
|
+
original = @configuration&.dup
|
|
21
|
+
yield configuration
|
|
22
|
+
ensure
|
|
23
|
+
@configuration = original
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# The provider yields only the Engine-facing value object, never Configuration.
|
|
29
|
+
# It is intentionally lazy and follows reset_configuration!/with_configuration.
|
|
30
|
+
Phronomy::RuntimeSettings.install_provider { Phronomy.configuration.__runtime_settings }
|
|
31
|
+
|
|
32
|
+
# Domain accessors are lazy too. Each returns only the owning domain's values
|
|
33
|
+
# or operation, never the application Configuration or a captured snapshot.
|
|
34
|
+
Phronomy::Agent::Settings.install_provider { Phronomy.configuration.__agent_settings }
|
|
35
|
+
Phronomy::WorkflowSettings.install_provider { Phronomy.configuration.__workflow_settings }
|
|
36
|
+
Phronomy::Tool::Settings.install_provider { Phronomy.configuration.__tool_settings }
|
|
37
|
+
Phronomy::Tracing::Settings.install_provider { Phronomy.configuration.__tracing_settings }
|
|
38
|
+
Phronomy::MultiAgent::Store.install_configured_provider { Phronomy.configuration.multi_agent_store }
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Application lifecycle coordination across Runtime and global configuration.
|
|
4
|
+
# Explicitly loaded by the application entry point; not an Engine primitive.
|
|
5
|
+
module Phronomy
|
|
6
|
+
class << self
|
|
7
|
+
def reset_runtime!(timeout: configuration.event_loop_stop_grace_seconds)
|
|
8
|
+
previous_grace = @configuration&.event_loop_stop_grace_seconds
|
|
9
|
+
result = Runtime.reset_default!(timeout: timeout)
|
|
10
|
+
|
|
11
|
+
new_configuration = Configuration.new
|
|
12
|
+
if previous_grace
|
|
13
|
+
new_configuration.event_loop_stop_grace_seconds = previous_grace
|
|
14
|
+
end
|
|
15
|
+
@configuration = new_configuration
|
|
16
|
+
result
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# Select stores and execution connections at the application composition
|
|
4
|
+
# boundary, without constructing either during library loading.
|
|
5
|
+
Phronomy::MultiAgent::TeamCoordinator.install_persistence_factory(
|
|
6
|
+
-> { Phronomy::PersistenceComposition.in_memory.multi_agent }
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
Phronomy::MultiAgent::ExecutionEnvironment.install_provider(-> { Phronomy::MultiAgent::EngineEnvironment.new })
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Storage
|
|
5
|
+
# Asynchronous execution of a synchronous raw storage backend.
|
|
6
|
+
#
|
|
7
|
+
# A transaction opens, runs and closes on one worker. Return values rather
|
|
8
|
+
# than transaction views: a bound view is closed when the block finishes.
|
|
9
|
+
# Timeout includes queue wait. Timeout/cancellation settles TaskResult but
|
|
10
|
+
# does not interrupt running I/O or guarantee rollback of a started write.
|
|
11
|
+
# Construction starts no Runtime; the default pool is resolved per operation.
|
|
12
|
+
# An explicitly injected pool remains owned by its caller.
|
|
13
|
+
#
|
|
14
|
+
# @api public
|
|
15
|
+
class AsyncClient
|
|
16
|
+
# @param backend [Storage::Backend] synchronous raw SPI 2 backend
|
|
17
|
+
# @param pool [Concurrency::OffloadPool, nil] optional execution pool
|
|
18
|
+
# @api public
|
|
19
|
+
def initialize(backend:, pool: nil)
|
|
20
|
+
Backend.validate_capabilities!(backend)
|
|
21
|
+
@backend = backend
|
|
22
|
+
@pool = pool
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Executes the entire transaction on one worker without waiting for queue
|
|
26
|
+
# capacity. Explicit nested backend transactions retain savepoint semantics.
|
|
27
|
+
#
|
|
28
|
+
# @param cancellation_token [Concurrency::CancellationToken, nil]
|
|
29
|
+
# @param timeout [Numeric, nil] operation-wide submit timeout
|
|
30
|
+
# @yieldparam view [Storage::View] view valid only within this transaction
|
|
31
|
+
# @return [Phronomy::TaskResult] the pool's original completion handle
|
|
32
|
+
# @api public
|
|
33
|
+
def transaction_async(cancellation_token: nil, timeout: nil, &operation)
|
|
34
|
+
raise ArgumentError, "transaction_async requires a block" unless operation
|
|
35
|
+
|
|
36
|
+
Phronomy::Execution.submit(pool: default_pool, timeout: timeout, cancellation_token: cancellation_token, on_full: :raise) do
|
|
37
|
+
@backend.transaction(&operation)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Submit an existing domain-owned synchronous storage unit unchanged.
|
|
42
|
+
# It may already open transactions or perform reads/reconciliation. Do not
|
|
43
|
+
# add a transaction, timeout or cancellation condition around that unit.
|
|
44
|
+
# The owner supplies its current runtime's pool and applies the result.
|
|
45
|
+
#
|
|
46
|
+
# @api private
|
|
47
|
+
def self.submit(pool:, &operation)
|
|
48
|
+
raise ArgumentError, "storage submit requires a block" unless operation
|
|
49
|
+
|
|
50
|
+
Phronomy::Execution.submit(pool: pool, on_full: :raise, &operation)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def default_pool
|
|
56
|
+
@pool
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Storage
|
|
5
|
+
# Neutral storage SPI. Domain resource declarations are supplied by composition.
|
|
6
|
+
# @api public
|
|
7
|
+
class Backend
|
|
8
|
+
REQUIRED_CAPABILITIES = {spi_version: 2, atomic_resources: true,
|
|
9
|
+
record_cas: true, stream_cas: true, conditional_unique: true,
|
|
10
|
+
guarded_checks: true, nested_savepoints: true}.freeze
|
|
11
|
+
attr_reader :resources, :view
|
|
12
|
+
|
|
13
|
+
# @api public
|
|
14
|
+
def initialize(resources:)
|
|
15
|
+
unless resources.is_a?(Array) && resources.all? { |resource| resource.is_a?(Resource) }
|
|
16
|
+
raise UnsupportedBackendError, "SPI 2 requires Resource declarations"
|
|
17
|
+
end
|
|
18
|
+
@resources = resources.to_h { |resource| [resource.id, resource] }.freeze
|
|
19
|
+
raise UnsupportedBackendError, "duplicate resource identity" unless @resources.length == resources.length
|
|
20
|
+
@resources.each_value do |resource|
|
|
21
|
+
next unless resource.guard
|
|
22
|
+
anchor = @resources[resource.guard.fetch(:resource)]
|
|
23
|
+
raise UnsupportedBackendError, "guard must name a registered records resource" unless anchor && anchor.kind == :records
|
|
24
|
+
end
|
|
25
|
+
@view = View.new(self)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Reject the previous eight-repository SPI before constructing domain wrappers.
|
|
29
|
+
# @api public
|
|
30
|
+
def self.validate_capabilities!(backend)
|
|
31
|
+
capabilities = backend.respond_to?(:capabilities) ? backend.capabilities : {}
|
|
32
|
+
missing = REQUIRED_CAPABILITIES.reject { |key, value| capabilities.is_a?(Hash) && capabilities[key] == value }
|
|
33
|
+
missing_methods = %i[view transaction transaction_open?].reject { |name| backend.respond_to?(name) }
|
|
34
|
+
unless missing.empty? && missing_methods.empty?
|
|
35
|
+
raise UnsupportedBackendError, "Persistence requires Storage SPI 2: #{(missing.keys + missing_methods).join(", ")}"
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# @api public
|
|
40
|
+
def capabilities
|
|
41
|
+
REQUIRED_CAPABILITIES.transform_values { |value| (value == true) ? false : value }.freeze
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Whether this synchronous context is already inside a backend transaction.
|
|
45
|
+
# Lets higher-level scopes reject claiming ownership of somebody else's commit.
|
|
46
|
+
# @api public
|
|
47
|
+
def transaction_open? = !current_view.nil?
|
|
48
|
+
|
|
49
|
+
# All declared resources participate in one atomic transaction domain.
|
|
50
|
+
# Record and stream revisions provide compare-and-swap conflict detection.
|
|
51
|
+
# This does not
|
|
52
|
+
# mean cross-process Workflow admission or distributed locking.
|
|
53
|
+
# Storage failures whose commit outcome is fundamentally unknown remain
|
|
54
|
+
# database failures; Phronomy does not claim exactly-once semantics.
|
|
55
|
+
# Explicit nesting uses savepoints. Non-local block exits raise and roll back.
|
|
56
|
+
# @api public
|
|
57
|
+
def transaction
|
|
58
|
+
current_view&.validate!
|
|
59
|
+
storage_transaction do |context|
|
|
60
|
+
scope = Scope.new
|
|
61
|
+
bound = View.new(self, scope: scope, context: context)
|
|
62
|
+
views = stack
|
|
63
|
+
views.push(bound)
|
|
64
|
+
completed = false
|
|
65
|
+
begin
|
|
66
|
+
result = yield bound
|
|
67
|
+
scope.check!
|
|
68
|
+
completed = true
|
|
69
|
+
result
|
|
70
|
+
ensure
|
|
71
|
+
scope.close!
|
|
72
|
+
views.pop
|
|
73
|
+
Thread.current.thread_variable_get(:phronomy_storage_scopes)&.delete(self) if views.empty?
|
|
74
|
+
if !completed && $!.nil?
|
|
75
|
+
raise TransactionError, "transaction block must complete normally; non-local exit rolled back"
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# @api private
|
|
82
|
+
def current_view
|
|
83
|
+
Thread.current.thread_variable_get(:phronomy_storage_scopes)&.dig(self)&.last.tap { |bound| bound&.validate! }
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# @api private
|
|
87
|
+
def execute(context, operation, resource, **arguments)
|
|
88
|
+
send(operation, context, resource, **arguments)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
private
|
|
92
|
+
|
|
93
|
+
def stack
|
|
94
|
+
scopes = Thread.current.thread_variable_get(:phronomy_storage_scopes)
|
|
95
|
+
unless scopes
|
|
96
|
+
scopes = {}
|
|
97
|
+
Thread.current.thread_variable_set(:phronomy_storage_scopes, scopes)
|
|
98
|
+
end
|
|
99
|
+
scopes[self] ||= []
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def storage_transaction
|
|
103
|
+
raise UnsupportedBackendError, "backend must implement an atomic storage transaction"
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "monitor"
|
|
4
|
+
|
|
5
|
+
module Phronomy
|
|
6
|
+
module Storage
|
|
7
|
+
module Backends
|
|
8
|
+
# One Monitor and snapshot cover every declared resource and nested scope.
|
|
9
|
+
# @api public
|
|
10
|
+
class InMemory < Backend
|
|
11
|
+
# @api public
|
|
12
|
+
def initialize(resources: [])
|
|
13
|
+
super
|
|
14
|
+
@monitor = Monitor.new
|
|
15
|
+
@state = resources.to_h { |resource| [resource.id, {}] }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# @api public
|
|
19
|
+
def capabilities = REQUIRED_CAPABILITIES
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def storage_transaction
|
|
24
|
+
@monitor.synchronize do
|
|
25
|
+
snapshot = Marshal.load(Marshal.dump(@state))
|
|
26
|
+
completed = false
|
|
27
|
+
begin
|
|
28
|
+
result = yield @state
|
|
29
|
+
completed = true
|
|
30
|
+
result
|
|
31
|
+
ensure
|
|
32
|
+
@state.replace(snapshot) unless completed
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def bucket(state, resource) = state.fetch(resource.id)
|
|
38
|
+
|
|
39
|
+
def read_record(state, resource, key:)
|
|
40
|
+
value = bucket(state, resource)[key]
|
|
41
|
+
value && Entry::Record.new(**value.to_h)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def lock_guard(state, resource, key:)
|
|
45
|
+
raise NotFoundError, "guard record not found: #{resource.id}/#{key}" unless bucket(state, resource).key?(key)
|
|
46
|
+
true
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def guard_record(state, resource, key:, attributes: {}, creating: false)
|
|
50
|
+
guard = resource.guard
|
|
51
|
+
return unless guard
|
|
52
|
+
anchor = resources.fetch(guard.fetch(:resource))
|
|
53
|
+
return if creating && anchor.equal?(resource) && guard[:via] == :key
|
|
54
|
+
guard_key = [:key, :stream].include?(guard[:via]) ? key : attributes.fetch(guard[:via])
|
|
55
|
+
lock_guard(state, anchor, key: guard_key)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def check_unique(state, resource, entry)
|
|
59
|
+
resource.unique.each do |constraint|
|
|
60
|
+
next if constraint[:fields].any? { |key| entry.attributes[key].nil? }
|
|
61
|
+
next unless constraint.fetch(:where).all? { |key, value| entry.attributes[key] == value }
|
|
62
|
+
conflict = bucket(state, resource).values.any? do |other|
|
|
63
|
+
other.key != entry.key && constraint[:where].all? { |k, val| other.attributes[k] == val } && constraint[:fields].all? { |k| other.attributes[k] == entry.attributes[k] }
|
|
64
|
+
end
|
|
65
|
+
raise UniqueConstraintError.new(resource: resource, constraint: constraint[:name]) if conflict
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def insert_record(state, resource, entry:)
|
|
70
|
+
raise ConflictError, "duplicate record identity" if bucket(state, resource).key?(entry.key)
|
|
71
|
+
guard_record(state, resource, key: entry.key, attributes: entry.attributes, creating: true)
|
|
72
|
+
check_unique(state, resource, entry)
|
|
73
|
+
bucket(state, resource)[entry.key] = entry
|
|
74
|
+
Entry::Record.new(**entry.to_h)
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def replace_record(state, resource, entry:, expected_revision:, expected_attributes:)
|
|
78
|
+
current = bucket(state, resource)[entry.key] || raise(NotFoundError, "record not found: #{entry.key}")
|
|
79
|
+
guard_record(state, resource, key: entry.key, attributes: current.attributes)
|
|
80
|
+
raise ConflictError, "record revision conflict" unless current.revision == expected_revision
|
|
81
|
+
unless expected_attributes.all? { |key, value| current.attributes[key] == value } && resource.immutable_attributes.all? { |key| current.attributes[key] == entry.attributes[key] }
|
|
82
|
+
raise ConflictError, "record attribute precondition failed"
|
|
83
|
+
end
|
|
84
|
+
check_unique(state, resource, entry)
|
|
85
|
+
bucket(state, resource)[entry.key] = entry
|
|
86
|
+
Entry::Record.new(**entry.to_h)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def delete_record(state, resource, key:, expected_revision:)
|
|
90
|
+
current = bucket(state, resource)[key]
|
|
91
|
+
if !expected_revision.equal?(Records::UNCHECKED) && (!current || current.revision != expected_revision)
|
|
92
|
+
raise ConflictError, "record deletion revision conflict"
|
|
93
|
+
end
|
|
94
|
+
return nil unless current
|
|
95
|
+
guard_record(state, resource, key: key, attributes: current.attributes)
|
|
96
|
+
bucket(state, resource).delete(key)
|
|
97
|
+
nil
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def scan_records(state, resource, equals:, after:, limit:)
|
|
101
|
+
entries = bucket(state, resource).values.select { |entry| (!after || entry.key.b > after.b) && equals.all? { |key, value| entry.attributes[key] == value } }.sort_by { |entry| entry.key.b }
|
|
102
|
+
entries = entries.first(limit) if limit
|
|
103
|
+
entries.map { |entry| Entry::Record.new(**entry.to_h) }.freeze
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def delete_matching_records(state, resource, equals:)
|
|
107
|
+
if resource.guard && ![:key, :stream].include?(resource.guard[:via])
|
|
108
|
+
raise ArgumentError, "deletion must constrain its guard attribute" unless equals.key?(resource.guard[:via])
|
|
109
|
+
guard_record(state, resource, key: "unused", attributes: equals)
|
|
110
|
+
end
|
|
111
|
+
scan_records(state, resource, equals: equals, after: nil, limit: nil).each do |entry|
|
|
112
|
+
delete_record(state, resource, key: entry.key, expected_revision: Records::UNCHECKED)
|
|
113
|
+
end
|
|
114
|
+
nil
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def append_stream(state, resource, stream:, expected_head:, entries:)
|
|
118
|
+
guard_record(state, resource, key: stream)
|
|
119
|
+
stored = bucket(state, resource).fetch(stream, [])
|
|
120
|
+
raise ConflictError, "stream head conflict" unless stored.length == expected_head
|
|
121
|
+
raise ConflictError, "duplicate stream entry identity" unless (stored.map(&:id) & entries.map(&:id)).empty?
|
|
122
|
+
appended = entries.each_with_index.map { |entry, index| Entry::Stream.new(position: expected_head + index + 1, id: entry.id, record: entry.record) }
|
|
123
|
+
bucket(state, resource)[stream] = stored + appended unless appended.empty?
|
|
124
|
+
appended.map { |entry| Entry::Stream.new(**entry.to_h) }.freeze
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def read_stream(state, resource, stream:, after:, limit:)
|
|
128
|
+
selected = bucket(state, resource).fetch(stream, []).select { |entry| entry.position > after }
|
|
129
|
+
selected = selected.first(limit) if limit
|
|
130
|
+
selected.map { |entry| Entry::Stream.new(**entry.to_h) }.freeze
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def stream_head(state, resource, stream:) = bucket(state, resource).fetch(stream, []).length
|
|
134
|
+
|
|
135
|
+
def delete_stream(state, resource, stream:)
|
|
136
|
+
guard_record(state, resource, key: stream)
|
|
137
|
+
bucket(state, resource).delete(stream)
|
|
138
|
+
nil
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def put_blob(state, resource, blob:)
|
|
142
|
+
stored = bucket(state, resource)[blob.key]
|
|
143
|
+
raise BlobConflictError, "blob bytes are immutable" if stored && stored.bytes != blob.bytes
|
|
144
|
+
stored ||= bucket(state, resource)[blob.key] = blob
|
|
145
|
+
Entry::Blob.new(**stored.to_h)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def fetch_blob(state, resource, key:)
|
|
149
|
+
stored = bucket(state, resource)[key] || raise(NotFoundError, "blob not found: #{key}")
|
|
150
|
+
Entry::Blob.new(**stored.to_h)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def blob_exists(state, resource, key:) = bucket(state, resource).key?(key)
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Storage
|
|
5
|
+
# Immutable byte values sharing the same transaction domain as other resources.
|
|
6
|
+
# @api public
|
|
7
|
+
class Blobs
|
|
8
|
+
attr_reader :resource
|
|
9
|
+
def initialize(view, resource) = (@view, @resource = view, resource)
|
|
10
|
+
|
|
11
|
+
# @api public
|
|
12
|
+
def put_if_absent(key:, bytes:, attributes:)
|
|
13
|
+
@view.validate!
|
|
14
|
+
blob = Entry::Blob.new(key: key, bytes: bytes, attributes: resource.validate_attributes(attributes))
|
|
15
|
+
@view.perform(:put_blob, resource, blob: blob)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# @api public
|
|
19
|
+
def fetch(key)
|
|
20
|
+
@view.validate!
|
|
21
|
+
@view.perform(:fetch_blob, resource, key: Validation.key(key))
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# @api public
|
|
25
|
+
def exist?(key)
|
|
26
|
+
@view.validate!
|
|
27
|
+
@view.perform(:blob_exists, resource, key: Validation.key(key))
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Storage
|
|
5
|
+
# A deliberately closed set of conditions; no payload predicates or callbacks.
|
|
6
|
+
# @api public
|
|
7
|
+
module Condition
|
|
8
|
+
RevisionIs = Data.define(:resource, :key, :expected) do
|
|
9
|
+
def initialize(resource:, key:, expected:)
|
|
10
|
+
super(resource: Resource.normalize_reference(resource), key: Validation.key(key), expected: Validation.revision(expected))
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
StreamHeadIs = Data.define(:resource, :stream, :expected) do
|
|
14
|
+
def initialize(resource:, stream:, expected:)
|
|
15
|
+
super(resource: Resource.normalize_reference(resource), stream: Validation.key(stream), expected: Validation.revision(expected))
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
NoRows = Data.define(:resource, :index, :equals) do
|
|
19
|
+
def initialize(resource:, index:, equals:)
|
|
20
|
+
super(resource: Resource.normalize_reference(resource), index: index, equals: Validation.immutable(equals))
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Storage
|
|
5
|
+
# A neutral storage contract failure.
|
|
6
|
+
# @api public
|
|
7
|
+
class ConditionFailedError < ConflictError
|
|
8
|
+
attr_reader :condition
|
|
9
|
+
# @api public
|
|
10
|
+
def initialize(condition)
|
|
11
|
+
@condition = condition
|
|
12
|
+
super("storage condition failed: #{condition.class.name}")
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|