phronomy 0.27.0 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.mutant.yml +9 -9
- data/CHANGELOG.md +52 -0
- data/CONTRIBUTING.md +8 -8
- data/README.md +28 -7
- data/benchmark/bench_agent_invoke.rb +25 -50
- data/benchmark/bench_context_assembler.rb +14 -11
- data/benchmark/bench_regression.rb +1 -1
- data/benchmark/bench_tool_schema.rb +1 -1
- data/docs/application-recipes.md +229 -0
- data/docs/architecture/agent-chat-and-state-ownership.md +7 -1
- data/docs/architecture/agent-transition-ownership.md +13 -89
- data/docs/architecture/backend-groups.md +152 -0
- data/docs/architecture/before-llm-input.md +3 -3
- data/docs/architecture/context-management.md +1 -1
- data/docs/architecture/dx05-agent-scope.md +119 -0
- data/docs/architecture/dx05-workflow-task-state.md +134 -0
- data/docs/architecture/error-api-review.md +245 -0
- data/docs/architecture/execution-abstraction-review.md +88 -0
- data/docs/architecture/execution-boundaries.md +28 -0
- data/docs/architecture/execution-metadata-and-values.md +6 -0
- data/docs/architecture/multi-agent-handoff.md +6 -5
- data/docs/architecture/persistence.md +27 -9
- data/docs/architecture/r8-unit1.md +104 -0
- data/docs/architecture/r8-unit10.md +72 -0
- data/docs/architecture/r8-unit11.md +71 -0
- data/docs/architecture/r8-unit12.md +67 -0
- data/docs/architecture/r8-unit13.md +61 -0
- data/docs/architecture/r8-unit14.md +72 -0
- data/docs/architecture/r8-unit15.md +65 -0
- data/docs/architecture/r8-unit16.md +54 -0
- data/docs/architecture/r8-unit17.md +73 -0
- data/docs/architecture/r8-unit18.md +92 -0
- data/docs/architecture/r8-unit19.md +61 -0
- data/docs/architecture/r8-unit2.md +55 -0
- data/docs/architecture/r8-unit20.md +51 -0
- data/docs/architecture/r8-unit21.md +79 -0
- data/docs/architecture/r8-unit22.md +74 -0
- data/docs/architecture/r8-unit23.md +81 -0
- data/docs/architecture/r8-unit3.md +130 -0
- data/docs/architecture/r8-unit4.md +79 -0
- data/docs/architecture/r8-unit5.md +115 -0
- data/docs/architecture/r8-unit6.md +71 -0
- data/docs/architecture/r8-unit7.md +70 -0
- data/docs/architecture/r8-unit8.md +78 -0
- data/docs/architecture/r8-unit9.md +73 -0
- data/docs/architecture/refactoring-closure.md +10 -1
- data/docs/architecture/remaining-refactoring-plan.md +16 -0
- data/docs/architecture/storage-execution-boundaries.md +54 -0
- data/docs/architecture/tool-schema-recording-gap.md +6 -0
- data/docs/architecture/tracing.md +1 -1
- data/docs/architecture/workflow-terminal-ownership-design.md +2 -2
- data/docs/architecture.md +24 -11
- data/docs/decisions/015-tool-public-facade-and-rbs-boundary.md +3 -3
- data/docs/decisions/027-llm-adapter-provider-boundary.md +2 -0
- data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +2 -0
- data/docs/decisions/031-durable-multi-agent-coordination.md +2 -0
- data/docs/decisions/032-storage-backend-composition.md +5 -0
- data/docs/decisions/034-handoff-runner-coordination-ownership.md +2 -0
- data/docs/decisions/035-tool-executor-capability-ownership.md +1 -1
- data/docs/decisions/038-responsibility-based-source-layout.md +14 -0
- data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +8 -0
- data/docs/decisions/040-configuration-default-composition.md +12 -1
- data/docs/decisions/045-worker-input-restriction-ownership.md +4 -0
- data/docs/decisions/051-execution-outcome-worker-ownership.md +2 -0
- data/docs/decisions/056-workflow-terminal-policy-ownership.md +1 -1
- data/docs/decisions/058-neutral-storage-primitives.md +5 -0
- data/docs/decisions/059-backend-contracts-and-async-clients.md +74 -0
- data/docs/decisions/060-runtime-settings-and-application-configuration.md +86 -0
- data/docs/decisions/061-persistence-failure-contracts.md +89 -0
- data/docs/decisions/062-content-store-service.md +52 -0
- data/docs/decisions/063-execution-contracts-and-services.md +88 -0
- data/docs/decisions/064-agent-execution-and-multi-agent-responsibility.md +36 -0
- data/docs/decisions/065-vector-and-embeddings-common-operations.md +38 -0
- data/docs/decisions/066-llm-and-tool-operation-contracts.md +20 -0
- data/docs/decisions/067-agent-progress-and-engine-connection.md +29 -0
- data/docs/decisions/068-agent-tool-child-progress-and-tool-operation.md +36 -0
- data/docs/decisions/README.md +18 -1
- data/docs/execution-boundaries-preview.md +45 -0
- data/docs/failure-reason-codes.md +45 -0
- data/docs/features.md +12 -4
- data/docs/getting-started.md +52 -48
- data/docs/migrations/0.22.md +2 -2
- data/docs/migrations/durable-semantic-coordination-v2.md +2 -0
- data/docs/migrations/handoff-runner-multi-agent.md +2 -0
- data/docs/migrations/persistence-failure-contracts.md +67 -0
- data/docs/migrations/r8-unit10.md +36 -0
- data/docs/migrations/r8-unit11.md +33 -0
- data/docs/migrations/r8-unit12.md +31 -0
- data/docs/migrations/r8-unit13.md +36 -0
- data/docs/migrations/r8-unit14.md +31 -0
- data/docs/migrations/r8-unit15.md +31 -0
- data/docs/migrations/r8-unit16.md +26 -0
- data/docs/migrations/r8-unit17.md +28 -0
- data/docs/migrations/r8-unit18.md +29 -0
- data/docs/migrations/r8-unit19.md +28 -0
- data/docs/migrations/r8-unit20.md +24 -0
- data/docs/migrations/r8-unit21.md +28 -0
- data/docs/migrations/r8-unit22.md +29 -0
- data/docs/migrations/r8-unit23.md +20 -0
- data/docs/migrations/r8-unit4.md +67 -0
- data/docs/migrations/r8-unit5.md +75 -0
- data/docs/migrations/r8-unit6.md +109 -0
- data/docs/migrations/r8-unit7.md +31 -0
- data/docs/migrations/r8-unit8.md +33 -0
- data/docs/migrations/r8-unit9.md +28 -0
- data/docs/migrations/storage-async-client.md +103 -0
- data/docs/migrations/vector-async-clients.md +110 -0
- data/docs/ordered-event-delivery.md +54 -0
- data/docs/persistence-backends.md +38 -21
- data/docs/runtime-and-concurrency.md +26 -1
- data/docs/workflow-completion.md +92 -0
- data/gemfiles/http_minimum.gemfile +5 -0
- data/lib/phronomy/agent/admission.rb +78 -0
- data/lib/phronomy/agent/{lifecycle_contract/agent_already_exists_error.rb → agent_already_exists_error.rb} +1 -1
- data/lib/phronomy/agent/{lifecycle_contract/agent_busy_error.rb → agent_busy_error.rb} +1 -1
- data/lib/phronomy/agent/agent_not_found_error.rb +16 -0
- data/lib/phronomy/agent/{lifecycle_contract/agent_purged_error.rb → agent_purged_error.rb} +1 -1
- data/lib/phronomy/agent/agent_unloaded_error.rb +14 -0
- data/lib/phronomy/agent/api/agent.rb +13 -0
- data/lib/phronomy/{recovery → agent/api}/execution_rehydration_required_error.rb +1 -1
- data/lib/phronomy/agent/async_event_api.rb +132 -103
- data/lib/phronomy/agent/base.rb +146 -113
- data/lib/phronomy/agent/before_llm_input.rb +70 -0
- data/lib/phronomy/agent/composition/run_once.rb +1 -1
- data/lib/phronomy/agent/{context_assembly/context_candidate_resolver.rb → context_candidate_resolver.rb} +2 -2
- data/lib/phronomy/agent/{context_assembly/context_assembler.rb → context_preparation.rb} +35 -221
- data/lib/phronomy/agent/control_request.rb +21 -0
- data/lib/phronomy/agent/execution/agent_execution.rb +1 -1
- data/lib/phronomy/agent/execution/agent_invocation.rb +27 -53
- data/lib/phronomy/agent/execution/approval_resume_commit.rb +1 -1
- data/lib/phronomy/agent/execution/dispatch_preparation.rb +17 -127
- data/lib/phronomy/agent/execution/exact_execution.rb +55 -32
- data/lib/phronomy/agent/execution/execution_cancellation.rb +8 -4
- data/lib/phronomy/agent/execution/execution_coordinator.rb +229 -407
- data/lib/phronomy/agent/execution/execution_metadata.rb +1 -0
- data/lib/phronomy/agent/execution/execution_outcome_committer.rb +71 -127
- data/lib/phronomy/agent/execution/execution_registry.rb +6 -2
- data/lib/phronomy/agent/execution/execution_session_runner.rb +22 -27
- data/lib/phronomy/agent/execution/initial_preparation.rb +19 -120
- data/lib/phronomy/agent/execution/{agent_invocation_session_builder.rb → invocation_actions.rb} +54 -194
- data/lib/phronomy/agent/execution/invocation_transitions.rb +39 -1
- data/lib/phronomy/agent/execution/runtime_record_encoder.rb +8 -11
- data/lib/phronomy/agent/execution_change.rb +161 -0
- data/lib/phronomy/agent/execution_environment.rb +36 -0
- data/lib/phronomy/agent/execution_extension_state.rb +21 -0
- data/lib/phronomy/agent/execution_observation.rb +35 -0
- data/lib/phronomy/agent/filterable.rb +188 -0
- data/lib/phronomy/agent/handoff/{handoff_context.rb → transfer_context.rb} +4 -4
- data/lib/phronomy/agent/handoff/{handoff_projection.rb → transfer_projection.rb} +14 -33
- data/lib/phronomy/{llm_contract/transport_error.rb → agent/handoff_error.rb} +1 -1
- data/lib/phronomy/agent/journal/journal_projection.rb +1 -1
- data/lib/phronomy/agent/knowledge_item.rb +13 -0
- data/lib/phronomy/agent/lifecycle/agent_root.rb +1 -1
- data/lib/phronomy/agent/lifecycle/lifetime_api.rb +76 -0
- data/lib/phronomy/agent/lifecycle/operation_lease.rb +33 -0
- data/lib/phronomy/agent/lifecycle/operation_result.rb +53 -0
- data/lib/phronomy/agent/lifecycle/ownership_registry.rb +117 -31
- data/lib/phronomy/agent/lifecycle/scope.rb +198 -0
- data/lib/phronomy/agent/lifecycle/scope_cleanup_error.rb +19 -0
- data/lib/phronomy/agent/lifecycle/unload_transition.rb +78 -0
- data/lib/phronomy/agent/persistence/agent_repository.rb +29 -21
- data/lib/phronomy/agent/persistence/cancellation_repository.rb +51 -0
- data/lib/phronomy/agent/persistence/codec.rb +1 -21
- data/lib/phronomy/agent/persistence/execution_repository.rb +57 -41
- data/lib/phronomy/agent/persistence/journal_repository.rb +27 -18
- data/lib/phronomy/agent/persistence/records.rb +33 -0
- data/lib/phronomy/agent/persistence/retention_repository.rb +70 -0
- data/lib/phronomy/agent/persistence/storage_schema.rb +7 -2
- data/lib/phronomy/agent/persistence/watermark.rb +13 -11
- data/lib/phronomy/agent/recovery/invocation_restorer.rb +13 -21
- data/lib/phronomy/agent/recovery/recovery_coordinator/continuation.rb +13 -12
- data/lib/phronomy/agent/recovery/recovery_coordinator/installation.rb +34 -34
- data/lib/phronomy/agent/recovery/recovery_coordinator/resolution.rb +33 -39
- data/lib/phronomy/agent/recovery/recovery_coordinator.rb +28 -25
- data/lib/phronomy/agent/recovery/recovery_rules.rb +119 -0
- data/lib/phronomy/agent/recovery/recovery_support.rb +10 -19
- data/lib/phronomy/agent/reserved_execution.rb +16 -0
- data/lib/phronomy/agent/retention.rb +17 -0
- data/lib/phronomy/agent/runtime_binding/engine_environment.rb +60 -0
- data/lib/phronomy/agent/runtime_binding/engine_session_builder.rb +34 -0
- data/lib/phronomy/agent/runtime_binding/phase_machine_builder.rb +64 -0
- data/lib/phronomy/agent/runtime_binding/tool_session_builder.rb +34 -0
- data/lib/phronomy/agent/{context_assembly/ruby_llm_materializer.rb → runtime_input.rb} +18 -35
- data/lib/phronomy/agent/{context_assembly/saved_context_reader.rb → saved_context_reader.rb} +6 -4
- data/lib/phronomy/agent/settings.rb +47 -0
- data/lib/phronomy/agent/{context_assembly/state_writer.rb → state_writer.rb} +4 -3
- data/lib/phronomy/agent/store.rb +253 -0
- data/lib/phronomy/agent/{lifecycle_contract/stream_callback_error.rb → stream_callback_error.rb} +3 -3
- data/lib/phronomy/agent/tool_execution/tool_binding.rb +4 -64
- data/lib/phronomy/agent/tool_execution/tool_invocation.rb +50 -219
- data/lib/phronomy/agent/tool_execution/tool_invocation_actions.rb +107 -0
- data/lib/phronomy/agent/tool_execution/tool_invocation_transitions.rb +70 -0
- data/lib/phronomy/common/error.rb +65 -1
- data/lib/phronomy/configuration/runtime_settings.rb +49 -0
- data/lib/phronomy/content_store/base.rb +7 -2
- data/lib/phronomy/content_store/storage_schema.rb +1 -0
- data/lib/phronomy/content_store/stored_contents.rb +2 -1
- data/lib/phronomy/context/assembly.rb +195 -0
- data/lib/phronomy/{llm_contract/context_budget_exceeded_error.rb → context/budget_exceeded_error.rb} +3 -1
- data/lib/phronomy/context/candidate.rb +51 -0
- data/lib/phronomy/context/constraint.rb +47 -0
- data/lib/phronomy/{agent/context_contract → context}/context_plan.rb +1 -1
- data/lib/phronomy/{agent/context_contract → context}/context_plan_validator.rb +3 -3
- data/lib/phronomy/{agent/context_contract → context}/context_policy.rb +1 -1
- data/lib/phronomy/{agent/context_contract → context}/context_policy_input.rb +1 -1
- data/lib/phronomy/{agent/context_assembly → context}/context_policy_input_builder.rb +1 -1
- data/lib/phronomy/context/default_policy.rb +140 -0
- data/lib/phronomy/context/final_budget_validator.rb +33 -0
- data/lib/phronomy/{agent/context_contract → context}/llm_input_build_context.rb +1 -1
- data/lib/phronomy/{agent/context_contract → context}/llm_input_manifest.rb +19 -19
- data/lib/phronomy/{agent/context_contract → context}/llm_input_patch.rb +1 -1
- data/lib/phronomy/context/manifest_error.rb +10 -0
- data/lib/phronomy/context/prompt_template.rb +98 -0
- data/lib/phronomy/{vector_store → documents}/loader/base.rb +1 -1
- data/lib/phronomy/{vector_store → documents}/loader/csv_loader.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/loader/markdown_loader.rb +3 -3
- data/lib/phronomy/{vector_store → documents}/loader/plain_text_loader.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/splitter/base.rb +1 -1
- data/lib/phronomy/{vector_store → documents}/splitter/fixed_size_splitter.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/splitter/recursive_splitter.rb +2 -2
- data/lib/phronomy/embeddings/async/async_client.rb +44 -0
- data/lib/phronomy/embeddings/authentication_error.rb +11 -0
- data/lib/phronomy/embeddings/backends/ruby_llm_embeddings.rb +52 -0
- data/lib/phronomy/embeddings/base.rb +43 -0
- data/lib/phronomy/embeddings/invalid_result_error.rb +11 -0
- data/lib/phronomy/embeddings/rate_limit_error.rb +11 -0
- data/lib/phronomy/embeddings/transport_error.rb +11 -0
- data/lib/phronomy/engine/concurrency/offload_pool.rb +36 -21
- data/lib/phronomy/engine/concurrency/pool_registry.rb +47 -12
- data/lib/phronomy/engine/diagnostics.rb +1 -1
- data/lib/phronomy/engine/event_loop.rb +7 -7
- data/lib/phronomy/engine/execution_binding.rb +23 -0
- data/lib/phronomy/engine/execution_receiver_binding.rb +43 -0
- data/lib/phronomy/engine/fsm_protocol.rb +1 -6
- data/lib/phronomy/engine/fsm_session.rb +3 -3
- data/lib/phronomy/engine/metrics.rb +16 -14
- data/lib/phronomy/engine/runtime/timer_queue.rb +1 -1
- data/lib/phronomy/engine/runtime.rb +25 -12
- data/lib/phronomy/engine/terminal_decision.rb +7 -0
- data/lib/phronomy/execution/async_operation.rb +41 -0
- data/lib/phronomy/{engine → execution}/blocking.rb +2 -2
- data/lib/phronomy/{engine → execution}/concurrency/cancellation_token.rb +1 -1
- data/lib/phronomy/{engine → execution}/concurrency/deadline.rb +0 -17
- data/lib/phronomy/{engine → execution}/concurrency/physical_completion_task.rb +1 -1
- data/lib/phronomy/{engine → execution}/concurrency/result_collector.rb +1 -1
- data/lib/phronomy/{engine → execution}/concurrency/result_composition.rb +33 -12
- data/lib/phronomy/{engine/concurrency/subscriptions.rb → execution/concurrency/result_subscriptions.rb} +14 -18
- data/lib/phronomy/execution/concurrency/subscriptions.rb +25 -0
- data/lib/phronomy/{engine → execution}/execution.rb +61 -27
- data/lib/phronomy/{engine → execution}/execution_cancellation_error.rb +2 -2
- data/lib/phronomy/execution/execution_receiver.rb +78 -0
- data/lib/phronomy/execution/execution_scope_state.rb +35 -0
- data/lib/phronomy/{engine → execution}/execution_timeout_error.rb +2 -2
- data/lib/phronomy/{engine → execution}/invocation_context.rb +0 -15
- data/lib/phronomy/execution/invocation_controls.rb +73 -0
- data/lib/phronomy/execution/runnable.rb +24 -0
- data/lib/phronomy/{engine → execution}/task_result.rb +9 -4
- data/lib/phronomy/execution/wait_policy.rb +24 -0
- data/lib/phronomy/filter/{contract/filter_block_error.rb → filter_block_error.rb} +3 -3
- data/lib/phronomy/generation/generator_verifier/agent_operation.rb +65 -0
- data/lib/phronomy/generation/generator_verifier/default_parser.rb +27 -0
- data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +2 -2
- data/lib/phronomy/generation/generator_verifier.rb +7 -7
- data/lib/phronomy/generation/low_confidence_error.rb +2 -2
- data/lib/phronomy/integrations/ordered_event_delivery.rb +208 -0
- data/lib/phronomy/llm_adapter/async/async_client.rb +47 -0
- data/lib/phronomy/{llm_contract → llm_adapter}/authentication_error.rb +3 -1
- data/lib/phronomy/llm_adapter/backends/ruby_llm.rb +183 -0
- data/lib/phronomy/llm_adapter/base.rb +49 -68
- data/lib/phronomy/{llm_contract → llm_adapter}/context_length_error.rb +3 -1
- data/lib/phronomy/llm_adapter/invalid_result_error.rb +10 -0
- data/lib/phronomy/llm_adapter/message.rb +62 -0
- data/lib/phronomy/{llm_contract → llm_adapter}/rate_limit_error.rb +3 -1
- data/lib/phronomy/llm_adapter/request.rb +79 -0
- data/lib/phronomy/llm_adapter/response.rb +36 -0
- data/lib/phronomy/llm_adapter/stream_chunk.rb +18 -0
- data/lib/phronomy/llm_adapter/token_usage.rb +64 -0
- data/lib/phronomy/llm_adapter/transport_error.rb +9 -0
- data/lib/phronomy/llm_adapter.rb +3 -16
- data/lib/phronomy/multi_agent/admission_registry.rb +1 -8
- data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +108 -84
- data/lib/phronomy/multi_agent/execution_environment.rb +29 -0
- data/lib/phronomy/{agent/handoff → multi_agent}/handoff.rb +2 -2
- data/lib/phronomy/{agent/handoff → multi_agent}/handoff_capability_factory.rb +21 -2
- data/lib/phronomy/multi_agent/handoff_participant.rb +79 -0
- data/lib/phronomy/{agent/handoff → multi_agent}/handoff_policy.rb +1 -1
- data/lib/phronomy/multi_agent/handoff_runner.rb +77 -100
- data/lib/phronomy/{agent/handoff → multi_agent}/handoff_state.rb +1 -1
- data/lib/phronomy/multi_agent/orchestrator.rb +61 -75
- data/lib/phronomy/multi_agent/persistence/codec.rb +20 -0
- data/lib/phronomy/multi_agent/persistence/handoff_state_repository.rb +77 -0
- data/lib/phronomy/multi_agent/persistence/records.rb +27 -0
- data/lib/phronomy/multi_agent/persistence/storage_schema.rb +21 -0
- data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +60 -44
- data/lib/phronomy/multi_agent/persistence/team_repository.rb +29 -21
- data/lib/phronomy/multi_agent/reserved_child_admission.rb +88 -0
- data/lib/phronomy/multi_agent/runtime_binding/engine_environment.rb +35 -0
- data/lib/phronomy/multi_agent/shared_state.rb +2 -2
- data/lib/phronomy/multi_agent/store.rb +156 -0
- data/lib/phronomy/multi_agent/team_busy_error.rb +18 -0
- data/lib/phronomy/multi_agent/team_coordinator.rb +99 -77
- data/lib/phronomy/multi_agent/team_not_found_error.rb +18 -0
- data/lib/phronomy/multi_agent/team_ownership_registry.rb +2 -12
- data/lib/phronomy/output_parser/parse_error.rb +7 -0
- data/lib/phronomy/persistence/content_repository.rb +26 -0
- data/lib/phronomy/persistence/errors.rb +16 -0
- data/lib/phronomy/persistence/migration/initial_format_migration.rb +2 -2
- data/lib/phronomy/persistence/persistence.rb +23 -0
- data/lib/phronomy/persistence/save_outcome.rb +24 -0
- data/lib/phronomy/persistence/snapshot_comparison.rb +81 -0
- data/lib/phronomy/persistence/storage_boundary.rb +32 -0
- data/lib/phronomy/persistence/transaction.rb +86 -0
- data/lib/phronomy/persistence_composition/storage_schema.rb +9 -4
- data/lib/phronomy/persistence_composition/stores.rb +34 -0
- data/lib/phronomy/persistence_composition/unit4_migration.rb +276 -0
- data/lib/phronomy/runtime_composition/agent_defaults.rb +5 -1
- data/lib/phronomy/runtime_composition/configuration.rb +146 -0
- data/lib/phronomy/runtime_composition/evaluation_defaults.rb +7 -0
- data/lib/phronomy/runtime_composition/execution_defaults.rb +5 -0
- data/lib/phronomy/runtime_composition/generation_defaults.rb +6 -0
- data/lib/phronomy/runtime_composition/global_configuration.rb +38 -0
- data/lib/phronomy/runtime_composition/multi_agent_defaults.rb +9 -0
- data/lib/phronomy/runtime_composition/workflow_defaults.rb +4 -0
- data/lib/phronomy/storage/async/async_client.rb +60 -0
- data/lib/phronomy/storage/backend.rb +8 -2
- data/lib/phronomy/storage/condition_failed_error.rb +2 -2
- data/lib/phronomy/storage/unique_constraint_error.rb +2 -2
- data/lib/phronomy/testing/eval/metrics.rb +1 -1
- data/lib/phronomy/testing/eval/scorer/llm_judge.rb +19 -3
- data/lib/phronomy/testing/persistence_contract/a_content_store.rb +2 -2
- data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +6 -6
- data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +55 -52
- data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +3 -3
- data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +7 -7
- data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +11 -11
- data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +57 -51
- data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +1 -1
- data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +23 -22
- data/lib/phronomy/tool/authorization.rb +77 -0
- data/lib/phronomy/tool/base.rb +551 -9
- data/lib/phronomy/tool/call_request.rb +40 -0
- data/lib/phronomy/{agent/tool_execution/tool_definition_set.rb → tool/definition_set.rb} +7 -8
- data/lib/phronomy/tool/operation.rb +88 -0
- data/lib/phronomy/tool/schema.rb +214 -0
- data/lib/phronomy/tool/settings.rb +31 -0
- data/lib/phronomy/tool/tool_error.rb +7 -0
- data/lib/phronomy/tool/tool_executor.rb +58 -0
- data/lib/phronomy/tools/agent.rb +22 -90
- data/lib/phronomy/tools/mcp.rb +21 -14
- data/lib/phronomy/tools/vector_search.rb +4 -4
- data/lib/phronomy/tracing/automatic.rb +11 -6
- data/lib/phronomy/tracing/base.rb +36 -7
- data/lib/phronomy/tracing/observation.rb +37 -0
- data/lib/phronomy/tracing/open_telemetry_tracer.rb +4 -29
- data/lib/phronomy/tracing/settings.rb +36 -0
- data/lib/phronomy/vector_store/{async_backend.rb → async/async_client.rb} +35 -29
- data/lib/phronomy/vector_store/{in_memory.rb → backends/in_memory.rb} +12 -10
- data/lib/phronomy/vector_store/{pgvector.rb → backends/pgvector.rb} +44 -16
- data/lib/phronomy/vector_store/{redis_search.rb → backends/redis_search.rb} +74 -37
- data/lib/phronomy/vector_store/base.rb +118 -49
- data/lib/phronomy/vector_store/invalid_result_error.rb +12 -0
- data/lib/phronomy/vector_store.rb +2 -17
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/workflow/completion.rb +11 -0
- data/lib/phronomy/workflow/execution/workflow.rb +37 -9
- data/lib/phronomy/workflow/execution/workflow_action_rules.rb +67 -0
- data/lib/phronomy/workflow/execution/workflow_context.rb +2 -2
- data/lib/phronomy/workflow/execution/workflow_execution_environment.rb +28 -0
- data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +77 -8
- data/lib/phronomy/workflow/execution/workflow_runner.rb +105 -92
- data/lib/phronomy/workflow/execution/workflow_settings.rb +28 -0
- data/lib/phronomy/workflow/execution/workflow_task_execution.rb +153 -0
- data/lib/phronomy/workflow/execution/workflow_task_reception.rb +72 -0
- data/lib/phronomy/workflow/execution/workflow_task_resume_error.rb +14 -0
- data/lib/phronomy/workflow/persistence/state_repository.rb +28 -22
- data/lib/phronomy/workflow/persistence/storage_schema.rb +13 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_engine_environment.rb +61 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb +158 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_task_session.rb +76 -0
- data/lib/phronomy/workflow/{execution → runtime_binding}/workflow_terminal_policy.rb +4 -4
- data/lib/phronomy.rb +63 -16
- data/scripts/api_snapshot.rb +47 -15
- data/scripts/check_readme_ruby.rb +24 -29
- data/scripts/check_readme_runnable.rb +92 -81
- data/sig/_private/phronomy/agent/settings.rbs +19 -0
- data/sig/_private/phronomy/configuration/runtime_settings.rbs +20 -0
- data/sig/_private/phronomy/llm_adapter/async/async_client.rbs +10 -0
- data/sig/_private/phronomy/multi_agent/store.rbs +8 -0
- data/sig/_private/phronomy/persistence/storage_boundary.rbs +7 -0
- data/sig/_private/phronomy/tool/settings.rbs +13 -0
- data/sig/_private/phronomy/tracing/settings.rbs +15 -0
- data/sig/_private/phronomy/workflow/execution/workflow_settings.rbs +13 -0
- data/sig/phronomy/agent/admission.rbs +19 -0
- data/sig/phronomy/agent/agent_not_found_error.rbs +6 -0
- data/sig/phronomy/agent/agent_unloaded_error.rbs +6 -0
- data/sig/phronomy/agent/execution_contract.rbs +132 -0
- data/sig/phronomy/agent/execution_environment.rbs +20 -0
- data/sig/phronomy/agent/store.rbs +73 -0
- data/sig/phronomy/agent/stream_callback_error.rbs +8 -0
- data/sig/phronomy/agent.rbs +29 -135
- data/sig/phronomy/common.rbs +7 -0
- data/sig/phronomy/content_store/contracts.rbs +13 -0
- data/sig/phronomy/context.rbs +152 -0
- data/sig/phronomy/embeddings/async/async_client.rbs +9 -0
- data/sig/phronomy/embeddings/base.rbs +23 -0
- data/sig/phronomy/engine/concurrency/timer_bindings.rbs +22 -0
- data/sig/phronomy/engine/control_bindings.rbs +11 -0
- data/sig/phronomy/engine/control_protocols.rbs +23 -0
- data/sig/phronomy/engine/execution_binding.rbs +6 -0
- data/sig/phronomy/engine/execution_receiver.rbs +15 -0
- data/sig/phronomy/engine/observability.rbs +13 -0
- data/sig/phronomy/execution/async_operation.rbs +7 -0
- data/sig/phronomy/execution/blocking.rbs +5 -0
- data/sig/phronomy/execution/concurrency/cancellation_token.rbs +13 -0
- data/sig/phronomy/execution/concurrency/deadline.rbs +10 -0
- data/sig/phronomy/execution/concurrency/result_composition.rbs +7 -0
- data/sig/phronomy/execution/control_protocols.rbs +18 -0
- data/sig/phronomy/execution/errors.rbs +7 -0
- data/sig/phronomy/execution/execution.rbs +41 -0
- data/sig/phronomy/execution/execution_receiver.rbs +40 -0
- data/sig/phronomy/execution/execution_scope_state.rbs +9 -0
- data/sig/phronomy/execution/invocation_context.rbs +18 -0
- data/sig/phronomy/execution/invocation_controls.rbs +13 -0
- data/sig/phronomy/execution/task_result.rbs +29 -0
- data/sig/phronomy/extensions.rbs +1 -21
- data/sig/phronomy/filter/filter_block_error.rbs +6 -0
- data/sig/phronomy/generation/low_confidence_error.rbs +6 -0
- data/sig/phronomy/handoff.rbs +10 -4
- data/sig/phronomy/integrations.rbs +14 -0
- data/sig/phronomy/llm_adapter/backends/ruby_llm.rbs +6 -0
- data/sig/phronomy/llm_adapter/base.rbs +14 -0
- data/sig/phronomy/llm_adapter/usage.rbs +14 -0
- data/sig/phronomy/llm_adapter/values.rbs +43 -0
- data/sig/phronomy/llm_context_window.rbs +10 -0
- data/sig/phronomy/multi_agent/reserved_child_admission.rbs +8 -0
- data/sig/phronomy/multi_agent/store.rbs +54 -0
- data/sig/phronomy/multi_agent/team_busy_error.rbs +8 -0
- data/sig/phronomy/multi_agent/team_not_found_error.rbs +8 -0
- data/sig/phronomy/multi_agent.rbs +6 -3
- data/sig/phronomy/persistence/errors.rbs +18 -0
- data/sig/phronomy/persistence/migration.rbs +13 -0
- data/sig/phronomy/persistence/transaction.rbs +19 -0
- data/sig/phronomy/persistence.rbs +1 -99
- data/sig/phronomy/persistence_composition.rbs +19 -0
- data/sig/phronomy/runtime_composition/configuration.rbs +36 -0
- data/sig/phronomy/storage/async/async_client.rbs +8 -0
- data/sig/phronomy/storage/backends/in_memory.rbs +9 -0
- data/sig/phronomy/{storage.rbs → storage/contracts.rbs} +4 -18
- data/sig/phronomy/tool.rbs +48 -4
- data/sig/phronomy/tracing/observation.rbs +7 -0
- data/sig/phronomy/vector_store/async/async_client.rbs +13 -0
- data/sig/phronomy/vector_store/base.rbs +23 -0
- data/sig/phronomy/workflow/execution/workflow_execution_environment.rbs +15 -0
- data/sig/phronomy/workflow/runtime_binding/workflow_terminal_policy.rbs +7 -0
- data/sig/phronomy/workflow/store.rbs +8 -0
- data/sig/phronomy/workflow.rbs +5 -1
- data/sig/phronomy.rbs +4 -49
- metadata +365 -117
- data/lib/phronomy/agent/concerns/before_llm_input.rb +0 -66
- data/lib/phronomy/agent/concerns/error_translation.rb +0 -45
- data/lib/phronomy/agent/concerns/filterable.rb +0 -184
- data/lib/phronomy/agent/context/capability/base.rb +0 -548
- data/lib/phronomy/agent/context/capability/tool_executor.rb +0 -62
- data/lib/phronomy/agent/context/instruction/prompt_template.rb +0 -102
- data/lib/phronomy/agent/context_assembly/runtime_chat_builder.rb +0 -36
- data/lib/phronomy/agent/context_assembly/token_budget_resolver.rb +0 -22
- data/lib/phronomy/agent/context_parts/validators/final_budget_validator.rb +0 -37
- data/lib/phronomy/agent/context_policies/default.rb +0 -142
- data/lib/phronomy/agent/execution/phase_machine_builder.rb +0 -101
- data/lib/phronomy/agent/execution/provider_call_outcome.rb +0 -106
- data/lib/phronomy/agent/handoff/handoff_execution_coordinator.rb +0 -15
- data/lib/phronomy/agent/handoff/handoff_outcome_committer.rb +0 -131
- data/lib/phronomy/agent/handoff/handoff_request.rb +0 -45
- data/lib/phronomy/agent/lifecycle_contract/handoff_error.rb +0 -7
- data/lib/phronomy/agent/persistence/handoff_state_repository.rb +0 -58
- data/lib/phronomy/agent/persistence/queries.rb +0 -61
- data/lib/phronomy/agent/selection/candidate.rb +0 -53
- data/lib/phronomy/agent/selection/constraint.rb +0 -49
- data/lib/phronomy/agent/tool_execution/tool_call_intercepted.rb +0 -29
- data/lib/phronomy/agent/tool_execution/tool_invocation_session_builder.rb +0 -276
- data/lib/phronomy/configuration/configuration.rb +0 -68
- data/lib/phronomy/configuration/global_configuration.rb +0 -26
- data/lib/phronomy/engine/concurrency/cancellation_scope.rb +0 -124
- data/lib/phronomy/engine/concurrency/operation_binding.rb +0 -43
- data/lib/phronomy/engine/execution_receiver.rb +0 -65
- data/lib/phronomy/engine/runnable.rb +0 -54
- data/lib/phronomy/llm_adapter/ruby_llm.rb +0 -47
- data/lib/phronomy/llm_contract/token_usage.rb +0 -85
- data/lib/phronomy/multi_agent/persistence/queries.rb +0 -30
- data/lib/phronomy/multi_agent/storage_contract/team_storage_schema.rb +0 -15
- data/lib/phronomy/output_parser/contract/parse_error.rb +0 -7
- data/lib/phronomy/persistence/api/persistence.rb +0 -140
- data/lib/phronomy/persistence_composition/repositories.rb +0 -77
- data/lib/phronomy/recovery/recovery.rb +0 -186
- data/lib/phronomy/tool/contract/tool_error.rb +0 -7
- data/lib/phronomy/vector_store/embeddings/base.rb +0 -44
- data/lib/phronomy/vector_store/embeddings/ruby_llm_embeddings.rb +0 -47
- data/lib/phronomy/workflow/phase_machine_builder.rb +0 -205
- data/lib/phronomy/workflow/storage_contract/workflow_storage_schema.rb +0 -9
- data/sig/phronomy/execution_receiver.rbs +0 -34
- data/sig/phronomy/llm_adapter.rbs +0 -11
- data/sig/phronomy/runtime.rbs +0 -76
- /data/lib/phronomy/agent/{context_assembly/context_importer.rb → context_importer.rb} +0 -0
- /data/lib/phronomy/{engine → execution}/backpressure_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/cancellation_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/concurrency/worker_input_restricted.rb +0 -0
- /data/lib/phronomy/{engine → execution}/event_loop_reentrancy_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/invalid_async_entry_action_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/invalid_async_transition_action_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/invalid_async_workflow_action_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/pool_shutdown_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/recursion_limit_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/runtime_shutdown_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/runtime_shutdown_reentrancy_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/scheduler_reentrancy_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/timeout_error.rb +0 -0
|
@@ -12,37 +12,34 @@ module Phronomy
|
|
|
12
12
|
# Candidates have no side effects. Runtime retains the first registration.
|
|
13
13
|
# Existing instances remain available for lookup and transition completion
|
|
14
14
|
# after admission closes; their own gate rejects new ownership changes.
|
|
15
|
-
def self.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def self.existing_for(runtime)
|
|
22
|
-
runtime.__shutdown_participant(key: self)
|
|
23
|
-
end
|
|
15
|
+
def self.current = ExecutionEnvironment.current.ownership
|
|
16
|
+
def self.existing_current = ExecutionEnvironment.current.existing_ownership
|
|
17
|
+
def self.for(environment) = environment.ownership
|
|
18
|
+
def self.existing_for(environment) = environment.existing_ownership
|
|
24
19
|
|
|
25
20
|
Entry = Data.define(:state, :agent, :token)
|
|
26
21
|
private_constant :Entry
|
|
27
22
|
|
|
28
|
-
def initialize(
|
|
29
|
-
@
|
|
23
|
+
def initialize(environment:)
|
|
24
|
+
@environment = environment
|
|
30
25
|
@mutex = Mutex.new
|
|
31
26
|
@condition = ConditionVariable.new
|
|
32
27
|
@entries = {}
|
|
28
|
+
@scope_owners = {}
|
|
29
|
+
@operations = {}
|
|
33
30
|
@state = :running
|
|
34
31
|
end
|
|
35
32
|
|
|
36
|
-
def create(agent_id, expected_class:)
|
|
33
|
+
def create(agent_id, expected_class:, scope_owner: nil)
|
|
37
34
|
key = normalize_agent_id(agent_id)
|
|
38
|
-
token = reserve_create!(key)
|
|
35
|
+
token = reserve_create!(key, scope_owner: scope_owner)
|
|
39
36
|
|
|
40
37
|
begin
|
|
41
|
-
agent = yield(@
|
|
38
|
+
agent = yield(@environment)
|
|
42
39
|
rescue Phronomy::AgentAlreadyExistsError,
|
|
43
|
-
Phronomy::
|
|
44
|
-
Phronomy::
|
|
45
|
-
Phronomy::
|
|
40
|
+
Phronomy::Persistence::ConflictError,
|
|
41
|
+
Phronomy::Persistence::NotFoundError,
|
|
42
|
+
Phronomy::Persistence::SerializationError,
|
|
46
43
|
ArgumentError,
|
|
47
44
|
Phronomy::ConfigurationError
|
|
48
45
|
release_construction!(key, token)
|
|
@@ -64,7 +61,7 @@ module Phronomy
|
|
|
64
61
|
)
|
|
65
62
|
end
|
|
66
63
|
|
|
67
|
-
def load(agent_id, expected_class:)
|
|
64
|
+
def load(agent_id, expected_class:, scope_owner: nil)
|
|
68
65
|
key = normalize_agent_id(agent_id)
|
|
69
66
|
token = nil
|
|
70
67
|
|
|
@@ -72,9 +69,14 @@ module Phronomy
|
|
|
72
69
|
existing = @mutex.synchronize do
|
|
73
70
|
ensure_running!
|
|
74
71
|
entry = @entries[key]
|
|
72
|
+
if scope_owner && entry && !@scope_owners[key].equal?(scope_owner)
|
|
73
|
+
raise Phronomy::AgentBusyError, "Agent #{key.inspect} belongs to another live owner"
|
|
74
|
+
end
|
|
75
75
|
case entry&.state
|
|
76
76
|
when :live
|
|
77
77
|
entry.agent
|
|
78
|
+
when :unloading
|
|
79
|
+
raise Phronomy::AgentBusyError, "Agent #{key.inspect} is unloading"
|
|
78
80
|
when :constructing, :purging
|
|
79
81
|
@condition.wait(@mutex)
|
|
80
82
|
:retry
|
|
@@ -83,6 +85,7 @@ module Phronomy
|
|
|
83
85
|
when nil
|
|
84
86
|
token = Object.new.freeze
|
|
85
87
|
@entries[key] = Entry.new(state: :constructing, agent: nil, token: token)
|
|
88
|
+
@scope_owners[key] = scope_owner if scope_owner
|
|
86
89
|
nil
|
|
87
90
|
# :nocov:
|
|
88
91
|
else
|
|
@@ -98,7 +101,7 @@ module Phronomy
|
|
|
98
101
|
end
|
|
99
102
|
|
|
100
103
|
begin
|
|
101
|
-
agent = yield(@
|
|
104
|
+
agent = yield(@environment)
|
|
102
105
|
publish_constructed!(
|
|
103
106
|
key,
|
|
104
107
|
token,
|
|
@@ -124,8 +127,8 @@ module Phronomy
|
|
|
124
127
|
when :recovery_required
|
|
125
128
|
raise ownership_recovery_error(key)
|
|
126
129
|
# :nocov:
|
|
127
|
-
when :purging
|
|
128
|
-
raise Phronomy::
|
|
130
|
+
when :purging, :unloading
|
|
131
|
+
raise Phronomy::AgentBusyError, "Agent #{key.inspect} is #{entry.state}"
|
|
129
132
|
# :cover:
|
|
130
133
|
end
|
|
131
134
|
end
|
|
@@ -134,13 +137,77 @@ module Phronomy
|
|
|
134
137
|
validate_expected_class!(agent, expected_class, key)
|
|
135
138
|
end
|
|
136
139
|
|
|
140
|
+
# Admission and lifetime transitions share this mutex. No Runtime or Store
|
|
141
|
+
# operation is called while it is held.
|
|
142
|
+
def acquire_operation(agent, read_only: false)
|
|
143
|
+
token = Object.new.freeze
|
|
144
|
+
@mutex.synchronize do
|
|
145
|
+
ensure_running! unless read_only && @state == :draining
|
|
146
|
+
entry = @entries[agent.agent_id]
|
|
147
|
+
if entry&.agent&.equal?(agent) && entry.state == :unloading
|
|
148
|
+
raise Phronomy::AgentBusyError, "Agent #{agent.agent_id.inspect} is unloading"
|
|
149
|
+
end
|
|
150
|
+
unless entry&.state == :live && entry.agent.equal?(agent)
|
|
151
|
+
raise Phronomy::RuntimeShutdownError, "Agent is no longer the live Runtime owner"
|
|
152
|
+
end
|
|
153
|
+
(@operations[agent] ||= {})[token] = true
|
|
154
|
+
end
|
|
155
|
+
OperationLease.new(self, agent, token)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def release_operation(agent, token)
|
|
159
|
+
@mutex.synchronize do
|
|
160
|
+
operations = @operations[agent]
|
|
161
|
+
operations&.delete(token)
|
|
162
|
+
@operations.delete(agent) if operations && operations.empty?
|
|
163
|
+
@condition.broadcast
|
|
164
|
+
end
|
|
165
|
+
true
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Returns the already accepted transition for duplicate callers.
|
|
169
|
+
def reserve_unload(agent)
|
|
170
|
+
@mutex.synchronize do
|
|
171
|
+
ensure_running!
|
|
172
|
+
entry = @entries[agent.agent_id]
|
|
173
|
+
if entry&.state == :unloading && entry.agent.equal?(agent)
|
|
174
|
+
return [entry.token, false]
|
|
175
|
+
end
|
|
176
|
+
unless entry&.state == :live && entry.agent.equal?(agent)
|
|
177
|
+
raise Phronomy::RuntimeShutdownError, "Agent is no longer the live Runtime owner"
|
|
178
|
+
end
|
|
179
|
+
assert_no_operations!(agent)
|
|
180
|
+
transition = UnloadTransition.new(agent: agent, environment: @environment, ownership: self)
|
|
181
|
+
@entries[agent.agent_id] = Entry.new(state: :unloading, agent: agent, token: transition)
|
|
182
|
+
[transition, true]
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def finish_unload(agent, transition, success:)
|
|
187
|
+
@mutex.synchronize do
|
|
188
|
+
entry = @entries[agent.agent_id]
|
|
189
|
+
unless entry&.state == :unloading && entry.agent.equal?(agent) && entry.token.equal?(transition)
|
|
190
|
+
raise Phronomy::RuntimeShutdownError, "Agent unload reservation is no longer authoritative"
|
|
191
|
+
end
|
|
192
|
+
if success
|
|
193
|
+
agent.send(:__mark_unloaded!)
|
|
194
|
+
@entries.delete(agent.agent_id)
|
|
195
|
+
@scope_owners.delete(agent.agent_id)
|
|
196
|
+
else
|
|
197
|
+
@entries[agent.agent_id] = Entry.new(state: :live, agent: agent, token: nil)
|
|
198
|
+
end
|
|
199
|
+
@condition.broadcast
|
|
200
|
+
end
|
|
201
|
+
true
|
|
202
|
+
end
|
|
203
|
+
|
|
137
204
|
def owned?(agent)
|
|
138
205
|
return false unless agent
|
|
139
206
|
|
|
140
207
|
key = normalize_agent_id(agent.agent_id)
|
|
141
208
|
@mutex.synchronize do
|
|
142
209
|
entry = @entries[key]
|
|
143
|
-
entry&.state
|
|
210
|
+
%i[live unloading].include?(entry&.state) && entry.agent.equal?(agent)
|
|
144
211
|
end
|
|
145
212
|
rescue ArgumentError
|
|
146
213
|
false
|
|
@@ -156,12 +223,16 @@ module Phronomy
|
|
|
156
223
|
ensure_running!
|
|
157
224
|
entry = @entries[key]
|
|
158
225
|
unless entry&.state == :live && entry.agent.equal?(agent)
|
|
226
|
+
if entry&.state == :unloading && entry.agent.equal?(agent)
|
|
227
|
+
raise Phronomy::AgentBusyError, "Agent #{key.inspect} is unloading"
|
|
228
|
+
end
|
|
159
229
|
raise Phronomy::RuntimeShutdownError,
|
|
160
230
|
"Agent #{key.inspect} is not the live owner in this Runtime"
|
|
161
231
|
end
|
|
232
|
+
assert_no_operations!(agent)
|
|
162
233
|
@entries[key] = Entry.new(state: :purging, agent: agent, token: token)
|
|
163
234
|
end
|
|
164
|
-
agent.send(:__mark_purging!, @
|
|
235
|
+
agent.send(:__mark_purging!, @environment)
|
|
165
236
|
token
|
|
166
237
|
end
|
|
167
238
|
|
|
@@ -170,8 +241,9 @@ module Phronomy
|
|
|
170
241
|
@mutex.synchronize do
|
|
171
242
|
entry = @entries[key]
|
|
172
243
|
validate_purge_entry!(entry, agent, token, key)
|
|
173
|
-
agent.send(:__mark_purged!, @
|
|
244
|
+
agent.send(:__mark_purged!, @environment)
|
|
174
245
|
@entries.delete(key)
|
|
246
|
+
@scope_owners.delete(key)
|
|
175
247
|
@condition.broadcast
|
|
176
248
|
end
|
|
177
249
|
true
|
|
@@ -183,7 +255,7 @@ module Phronomy
|
|
|
183
255
|
@mutex.synchronize do
|
|
184
256
|
entry = @entries[key]
|
|
185
257
|
validate_purge_entry!(entry, agent, token, key)
|
|
186
|
-
agent.send(:__restore_live_after_purge_abort!, @
|
|
258
|
+
agent.send(:__restore_live_after_purge_abort!, @environment)
|
|
187
259
|
@entries[key] = Entry.new(state: :live, agent: agent, token: nil)
|
|
188
260
|
@condition.broadcast
|
|
189
261
|
end
|
|
@@ -197,7 +269,7 @@ module Phronomy
|
|
|
197
269
|
key = normalize_agent_id(agent.agent_id)
|
|
198
270
|
@mutex.synchronize do
|
|
199
271
|
validate_purge_entry!(@entries[key], agent, token, key)
|
|
200
|
-
agent.send(:__mark_ownership_recovery_required!, @
|
|
272
|
+
agent.send(:__mark_ownership_recovery_required!, @environment)
|
|
201
273
|
@entries[key] = Entry.new(
|
|
202
274
|
state: :recovery_required,
|
|
203
275
|
agent: agent,
|
|
@@ -220,7 +292,7 @@ module Phronomy
|
|
|
220
292
|
# ownership transition that is still actively changing state must settle.
|
|
221
293
|
def wait_until_idle(deadline)
|
|
222
294
|
@mutex.synchronize do
|
|
223
|
-
while @entries.values.any? { |entry| %i[constructing purging].include?(entry.state) }
|
|
295
|
+
while @entries.values.any? { |entry| %i[constructing purging unloading].include?(entry.state) }
|
|
224
296
|
remaining = deadline - monotonic_now
|
|
225
297
|
# :nocov:
|
|
226
298
|
return false if remaining <= 0
|
|
@@ -236,26 +308,33 @@ module Phronomy
|
|
|
236
308
|
@state = :terminated
|
|
237
309
|
owned = @entries.values.filter_map(&:agent).uniq
|
|
238
310
|
@entries.clear
|
|
311
|
+
@scope_owners.clear
|
|
312
|
+
@operations.clear
|
|
239
313
|
@condition.broadcast
|
|
240
314
|
owned
|
|
241
315
|
end
|
|
242
|
-
agents.each { |agent| agent.send(:__release_runtime_owner!, @
|
|
316
|
+
agents.each { |agent| agent.send(:__release_runtime_owner!, @environment) }
|
|
243
317
|
true
|
|
244
318
|
end
|
|
245
319
|
|
|
246
320
|
private
|
|
247
321
|
|
|
248
|
-
def reserve_create!(key)
|
|
322
|
+
def reserve_create!(key, scope_owner: nil)
|
|
249
323
|
token = Object.new.freeze
|
|
250
324
|
@mutex.synchronize do
|
|
251
325
|
ensure_running!
|
|
252
326
|
if (entry = @entries[key])
|
|
253
327
|
raise ownership_recovery_error(key) if entry.state == :recovery_required
|
|
254
328
|
|
|
329
|
+
if scope_owner
|
|
330
|
+
raise Phronomy::AgentBusyError, "Agent #{key.inspect} already has a Runtime owner"
|
|
331
|
+
end
|
|
332
|
+
|
|
255
333
|
raise Phronomy::AgentAlreadyExistsError,
|
|
256
334
|
"Agent #{key.inspect} already exists in this Runtime (#{entry.state})"
|
|
257
335
|
end
|
|
258
336
|
@entries[key] = Entry.new(state: :constructing, agent: nil, token: token)
|
|
337
|
+
@scope_owners[key] = scope_owner if scope_owner
|
|
259
338
|
end
|
|
260
339
|
token
|
|
261
340
|
end
|
|
@@ -267,7 +346,7 @@ module Phronomy
|
|
|
267
346
|
"constructed Agent identity mismatch: reserved #{key.inspect}, got #{agent.agent_id.inspect}"
|
|
268
347
|
end
|
|
269
348
|
|
|
270
|
-
agent.send(:__bind_runtime_owner!, @
|
|
349
|
+
agent.send(:__bind_runtime_owner!, @environment)
|
|
271
350
|
@mutex.synchronize do
|
|
272
351
|
entry = @entries[key]
|
|
273
352
|
unless entry&.state == :constructing && entry.token.equal?(token)
|
|
@@ -282,7 +361,7 @@ module Phronomy
|
|
|
282
361
|
# :nocov:
|
|
283
362
|
if fail_closed
|
|
284
363
|
fail_construction_closed!(key, token, agent: agent)
|
|
285
|
-
agent&.send(:__mark_ownership_recovery_required!, @
|
|
364
|
+
agent&.send(:__mark_ownership_recovery_required!, @environment)
|
|
286
365
|
else
|
|
287
366
|
release_construction!(key, token)
|
|
288
367
|
end
|
|
@@ -296,6 +375,7 @@ module Phronomy
|
|
|
296
375
|
# :nocov:
|
|
297
376
|
if entry&.state == :constructing && entry.token.equal?(token)
|
|
298
377
|
@entries.delete(key)
|
|
378
|
+
@scope_owners.delete(key)
|
|
299
379
|
@condition.broadcast
|
|
300
380
|
end
|
|
301
381
|
# :cover:
|
|
@@ -358,6 +438,12 @@ module Phronomy
|
|
|
358
438
|
# :cover:
|
|
359
439
|
end
|
|
360
440
|
|
|
441
|
+
def assert_no_operations!(agent)
|
|
442
|
+
return unless @operations[agent]&.any?
|
|
443
|
+
|
|
444
|
+
raise Phronomy::AgentBusyError, "Agent #{agent.agent_id.inspect} still has accepted operations"
|
|
445
|
+
end
|
|
446
|
+
|
|
361
447
|
def monotonic_now
|
|
362
448
|
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
363
449
|
end
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Agent
|
|
5
|
+
# Owns the Agents materialized within one application operation. A scope is
|
|
6
|
+
# Runtime-bound, process-local, and must not be persisted as Workflow state.
|
|
7
|
+
# @api public
|
|
8
|
+
class Scope
|
|
9
|
+
# @api private
|
|
10
|
+
def self.open
|
|
11
|
+
raise ArgumentError, "open_scope requires a block" unless block_given?
|
|
12
|
+
if Phronomy::WaitPolicy.blocking_forbidden?
|
|
13
|
+
raise Phronomy::EventLoopReentrancyError, "Agent.open_scope cannot block EventLoop"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
scope = new(environment: ExecutionEnvironment.current)
|
|
17
|
+
result = nil
|
|
18
|
+
begin
|
|
19
|
+
result = yield(scope)
|
|
20
|
+
result = result.wait_result if result.is_a?(Phronomy::TaskResult)
|
|
21
|
+
rescue Exception => primary # standard:disable Lint/RescueException
|
|
22
|
+
raise
|
|
23
|
+
ensure
|
|
24
|
+
failures = scope.send(:__close)
|
|
25
|
+
if primary
|
|
26
|
+
failures.each do |failure|
|
|
27
|
+
error = failure.fetch(:error)
|
|
28
|
+
Phronomy::RuntimeSettings.current.logger&.warn(
|
|
29
|
+
"[Agent scope] cleanup failed agent_id=#{failure.fetch(:agent_id).inspect}: #{error.class}: #{error.message}"
|
|
30
|
+
)
|
|
31
|
+
rescue
|
|
32
|
+
nil
|
|
33
|
+
end
|
|
34
|
+
elsif failures.any?
|
|
35
|
+
raise ScopeCleanupError.new(failures: failures, result: result)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
result
|
|
39
|
+
end
|
|
40
|
+
private_class_method :open
|
|
41
|
+
|
|
42
|
+
def initialize(environment:)
|
|
43
|
+
@environment = environment
|
|
44
|
+
@ownership = environment.ownership
|
|
45
|
+
@owner_token = Object.new.freeze
|
|
46
|
+
@mutex = Mutex.new
|
|
47
|
+
@condition = ConditionVariable.new
|
|
48
|
+
@state = :open
|
|
49
|
+
@receipts = {}
|
|
50
|
+
@agents = {}
|
|
51
|
+
end
|
|
52
|
+
private_class_method :new
|
|
53
|
+
|
|
54
|
+
# @return [Agent::Base]
|
|
55
|
+
# @api public
|
|
56
|
+
def create(agent_class, **options, &listener)
|
|
57
|
+
check_blocking!
|
|
58
|
+
materialize(:create, agent_class, options, listener, async: false)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# @return [TaskResult<Agent::Base>]
|
|
62
|
+
# @api public
|
|
63
|
+
def create_async(agent_class, **options, &listener)
|
|
64
|
+
materialize(:create, agent_class, options, listener, async: true)
|
|
65
|
+
rescue => error
|
|
66
|
+
Phronomy::TaskResult.failed(error)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Repeated load in this scope returns the same live owner. Supplying a new
|
|
70
|
+
# listener or different Store for an already live instance is rejected.
|
|
71
|
+
# @return [Agent::Base]
|
|
72
|
+
# @api public
|
|
73
|
+
def load(agent_class, agent_id, persistence:, **options, &listener)
|
|
74
|
+
check_blocking!
|
|
75
|
+
materialize(:load, agent_class, options.merge(agent_id: agent_id, persistence: persistence), listener, async: false)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# @return [TaskResult<Agent::Base>]
|
|
79
|
+
# @api public
|
|
80
|
+
def load_async(agent_class, agent_id, persistence:, **options, &listener)
|
|
81
|
+
materialize(:load, agent_class, options.merge(agent_id: agent_id, persistence: persistence), listener, async: true)
|
|
82
|
+
rescue => error
|
|
83
|
+
Phronomy::TaskResult.failed(error)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# @api private
|
|
87
|
+
def __close
|
|
88
|
+
agents = @mutex.synchronize do
|
|
89
|
+
@state = :closing
|
|
90
|
+
@condition.wait(@mutex) until @receipts.empty?
|
|
91
|
+
@agents.values.dup
|
|
92
|
+
end
|
|
93
|
+
failures = []
|
|
94
|
+
agents.each do |agent|
|
|
95
|
+
agent.unload!
|
|
96
|
+
rescue => error
|
|
97
|
+
failures << {agent_id: agent.agent_id, error: error}.freeze
|
|
98
|
+
end
|
|
99
|
+
@mutex.synchronize do
|
|
100
|
+
@agents.clear
|
|
101
|
+
@environment = @ownership = @owner_token = nil
|
|
102
|
+
@state = :closed
|
|
103
|
+
end
|
|
104
|
+
failures.freeze
|
|
105
|
+
end
|
|
106
|
+
private :__close
|
|
107
|
+
|
|
108
|
+
private
|
|
109
|
+
|
|
110
|
+
def check_blocking!
|
|
111
|
+
return unless Phronomy::WaitPolicy.blocking_forbidden?
|
|
112
|
+
|
|
113
|
+
raise Phronomy::EventLoopReentrancyError, "Agent scope materialization cannot block EventLoop; use create_async/load_async"
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def materialize(kind, agent_class, options, listener, async:)
|
|
117
|
+
unless agent_class.is_a?(Class) && agent_class <= Base
|
|
118
|
+
raise ArgumentError, "agent_class must be an Agent::Base subclass"
|
|
119
|
+
end
|
|
120
|
+
if options[:on_event] && listener
|
|
121
|
+
raise ArgumentError, "Provide either on_event: or a block, not both"
|
|
122
|
+
end
|
|
123
|
+
if kind == :load && !options[:persistence]
|
|
124
|
+
raise ArgumentError, "persistence is required"
|
|
125
|
+
end
|
|
126
|
+
key = (options[:agent_id] || (SecureRandom.uuid if kind == :create)).to_s.dup.freeze
|
|
127
|
+
raise ArgumentError, "agent_id must not be empty" if key.empty?
|
|
128
|
+
options = options.merge(agent_id: key)
|
|
129
|
+
receipt = Object.new.freeze
|
|
130
|
+
@mutex.synchronize do
|
|
131
|
+
raise Phronomy::Error, "Agent scope is #{@state}" unless @state == :open
|
|
132
|
+
@receipts[receipt] = key
|
|
133
|
+
end
|
|
134
|
+
operation = -> { acquire(kind, agent_class, key, options, listener) }
|
|
135
|
+
if async
|
|
136
|
+
begin
|
|
137
|
+
task = @environment.submit(on_full: :raise, &operation)
|
|
138
|
+
task.on_physical_complete { finish_receipt(receipt) }
|
|
139
|
+
task
|
|
140
|
+
rescue
|
|
141
|
+
finish_receipt(receipt)
|
|
142
|
+
raise
|
|
143
|
+
end
|
|
144
|
+
else
|
|
145
|
+
begin
|
|
146
|
+
operation.call
|
|
147
|
+
ensure
|
|
148
|
+
finish_receipt(receipt)
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def acquire(kind, agent_class, key, options, listener)
|
|
154
|
+
materialized = false
|
|
155
|
+
agent = @ownership.public_send(kind, key, expected_class: agent_class, scope_owner: @owner_token) do |environment|
|
|
156
|
+
materialized = true
|
|
157
|
+
instance = agent_class.send(:__construct_owned_agent, environment, key,
|
|
158
|
+
**options, load_existing: kind == :load, &listener)
|
|
159
|
+
RecoveryCoordinator.new(instance).recover_on_load! if kind == :load
|
|
160
|
+
instance
|
|
161
|
+
end
|
|
162
|
+
# Register before any result or post-materialization check reaches the
|
|
163
|
+
# caller, even when scope closure began while the worker was running.
|
|
164
|
+
@mutex.synchronize { @agents[agent.object_id] = agent }
|
|
165
|
+
if kind == :load
|
|
166
|
+
unless agent.persistence.equal?(options[:persistence])
|
|
167
|
+
raise Phronomy::ConfigurationError, "Agent #{key.inspect} is already live with a different Persistence instance"
|
|
168
|
+
end
|
|
169
|
+
if !materialized && (options[:on_event] || listener)
|
|
170
|
+
raise Phronomy::ConfigurationError, "Agent #{key.inspect} is already live; load cannot re-bind on_event"
|
|
171
|
+
end
|
|
172
|
+
end
|
|
173
|
+
agent
|
|
174
|
+
rescue Phronomy::Persistence::ConflictError => error
|
|
175
|
+
raise unless kind == :create
|
|
176
|
+
raise Phronomy::AgentAlreadyExistsError, "Agent #{key.inspect} already exists durably: #{error.message}"
|
|
177
|
+
rescue => error
|
|
178
|
+
# A fail-closed construction remains reserved in C04. Do not purge or
|
|
179
|
+
# release that reservation just because no instance reached this scope.
|
|
180
|
+
report_materialization_error(key, error)
|
|
181
|
+
raise
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def finish_receipt(receipt)
|
|
185
|
+
@mutex.synchronize do
|
|
186
|
+
@receipts.delete(receipt)
|
|
187
|
+
@condition.broadcast
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def report_materialization_error(key, error)
|
|
192
|
+
Phronomy::RuntimeSettings.current.logger&.warn("[Agent scope] materialization failed agent_id=#{key.inspect}: #{error.class}: #{error.message}")
|
|
193
|
+
rescue
|
|
194
|
+
nil
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Agent
|
|
5
|
+
# Successful application work with one or more failed Agent releases.
|
|
6
|
+
# Original exceptions and the successful result remain available to the caller.
|
|
7
|
+
# @api public
|
|
8
|
+
class ScopeCleanupError < Phronomy::Error
|
|
9
|
+
attr_reader :failures, :result
|
|
10
|
+
|
|
11
|
+
def initialize(failures:, result:)
|
|
12
|
+
@failures = failures.map { |failure| failure.dup.freeze }.freeze
|
|
13
|
+
@result = result
|
|
14
|
+
super("Agent scope cleanup failed for: #{@failures.map { |f| f.fetch(:agent_id) }.join(", ")}",
|
|
15
|
+
code: "agent.scope_cleanup_failed")
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Agent
|
|
5
|
+
# Agent-owned two-phase release. The worker only checks durable facts; the
|
|
6
|
+
# EventLoop completes the exact process-local reservation after physical I/O.
|
|
7
|
+
# @api private
|
|
8
|
+
class UnloadTransition
|
|
9
|
+
Command = Data.define(:coordinator, :phase)
|
|
10
|
+
private_constant :Command
|
|
11
|
+
|
|
12
|
+
attr_reader :result
|
|
13
|
+
|
|
14
|
+
def initialize(agent:, environment:, ownership:)
|
|
15
|
+
@agent, @environment, @ownership = agent, environment, ownership
|
|
16
|
+
@result = Phronomy::TaskResult.deferred(name: "agent-unload:#{agent.agent_id}")
|
|
17
|
+
@mutex = Mutex.new
|
|
18
|
+
@finished = false
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def start
|
|
22
|
+
post(:check)
|
|
23
|
+
@result
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def deliver_on_event_loop(command)
|
|
27
|
+
if command.phase == :check
|
|
28
|
+
if @environment.existing_registry&.agent_execution_admitted?(@agent.agent_id)
|
|
29
|
+
raise Phronomy::AgentBusyError, "Agent #{@agent.agent_id.inspect} has a nonterminal execution"
|
|
30
|
+
end
|
|
31
|
+
agent_id, persistence = @agent.agent_id, @agent.persistence
|
|
32
|
+
task = @environment.submit(on_full: :raise) do
|
|
33
|
+
persistence.transaction do |tx|
|
|
34
|
+
tx.executions.assert_idle!(agent_id)
|
|
35
|
+
tx.guard_agent!(agent_id)
|
|
36
|
+
unless tx.retentions.list(agent_id).empty?
|
|
37
|
+
raise Phronomy::AgentBusyError, "Agent #{agent_id.inspect} is retained by durable coordination"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
task.on_complete do |_value, error|
|
|
42
|
+
@read_error = error
|
|
43
|
+
task.on_physical_complete { post(:apply) }
|
|
44
|
+
end
|
|
45
|
+
else
|
|
46
|
+
finish(@read_error)
|
|
47
|
+
end
|
|
48
|
+
rescue => error
|
|
49
|
+
finish(error)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
private
|
|
53
|
+
|
|
54
|
+
def post(phase)
|
|
55
|
+
accepted = @environment.registry.post(Command.new(coordinator: self, phase: phase), completion: @result)
|
|
56
|
+
finish(Phronomy::RuntimeShutdownError.new("EventLoop rejected Agent unload #{phase}")) unless accepted
|
|
57
|
+
rescue => error
|
|
58
|
+
finish(error)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def finish(error)
|
|
62
|
+
first = @mutex.synchronize do
|
|
63
|
+
next false if @finished
|
|
64
|
+
@finished = true
|
|
65
|
+
end
|
|
66
|
+
return unless first
|
|
67
|
+
|
|
68
|
+
begin
|
|
69
|
+
@ownership.finish_unload(@agent, self, success: error.nil?)
|
|
70
|
+
rescue => ownership_error
|
|
71
|
+
error ||= ownership_error
|
|
72
|
+
end
|
|
73
|
+
@agent = @environment = @ownership = nil
|
|
74
|
+
error ? @result.fail(error) : @result.complete(true)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -9,39 +9,47 @@ module Phronomy
|
|
|
9
9
|
def initialize(view) = @view = view
|
|
10
10
|
|
|
11
11
|
def create(root)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
Phronomy::Persistence::StorageBoundary.call do
|
|
13
|
+
record = Codec.encode_agent_root(root)
|
|
14
|
+
@view.atomic do |bound|
|
|
15
|
+
entry = bound.records(StorageSchema::ROOTS).insert(key: root.agent_id.to_s,
|
|
16
|
+
revision: Integer(root.agent_revision), attributes: {}, record: record)
|
|
17
|
+
decode(entry, root.agent_id, revision: root.agent_revision)
|
|
18
|
+
end
|
|
17
19
|
end
|
|
18
20
|
end
|
|
19
21
|
|
|
20
22
|
def load(agent_id)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
Phronomy::Persistence::StorageBoundary.call do
|
|
24
|
+
value = @view.atomic do |bound|
|
|
25
|
+
entry = bound.records(StorageSchema::ROOTS).read(agent_id.to_s)
|
|
26
|
+
entry && decode(entry, agent_id)
|
|
27
|
+
end
|
|
28
|
+
value || raise(Phronomy::Storage::NotFoundError, "Agent not found: #{agent_id}")
|
|
24
29
|
end
|
|
25
|
-
value || raise(Phronomy::Storage::NotFoundError, "Agent not found: #{agent_id}")
|
|
26
30
|
end
|
|
27
31
|
|
|
28
32
|
def save(agent_id, expected_revision:, root:)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
Phronomy::Persistence::StorageBoundary.call do
|
|
34
|
+
expected = Integer(expected_revision)
|
|
35
|
+
revision = Integer(root.agent_revision)
|
|
36
|
+
raise Phronomy::Storage::ConflictError, "root save must advance revision exactly once" unless revision == expected + 1
|
|
37
|
+
unless root.agent_id.to_s == agent_id.to_s
|
|
38
|
+
raise Phronomy::Storage::SerializationError, "Agent root identity mismatch"
|
|
39
|
+
end
|
|
40
|
+
record = Codec.encode_agent_root(root)
|
|
41
|
+
@view.atomic do |bound|
|
|
42
|
+
entry = bound.records(StorageSchema::ROOTS).replace(key: agent_id.to_s,
|
|
43
|
+
expected_revision: expected, next_revision: revision, attributes: {}, record: record)
|
|
44
|
+
decode(entry, agent_id, revision: revision)
|
|
45
|
+
end
|
|
40
46
|
end
|
|
41
47
|
end
|
|
42
48
|
|
|
43
49
|
def delete(agent_id)
|
|
44
|
-
|
|
50
|
+
Phronomy::Persistence::StorageBoundary.call do
|
|
51
|
+
@view.records(StorageSchema::ROOTS).delete(key: agent_id.to_s)
|
|
52
|
+
end
|
|
45
53
|
end
|
|
46
54
|
|
|
47
55
|
private
|