phronomy 0.27.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.mutant.yml +9 -9
- data/CHANGELOG.md +52 -0
- data/CONTRIBUTING.md +8 -8
- data/README.md +9 -3
- data/benchmark/bench_agent_invoke.rb +25 -50
- data/benchmark/bench_context_assembler.rb +14 -11
- data/benchmark/bench_regression.rb +1 -1
- data/benchmark/bench_tool_schema.rb +1 -1
- data/docs/architecture/agent-chat-and-state-ownership.md +7 -1
- data/docs/architecture/agent-transition-ownership.md +13 -89
- data/docs/architecture/backend-groups.md +152 -0
- data/docs/architecture/before-llm-input.md +3 -3
- data/docs/architecture/context-management.md +1 -1
- data/docs/architecture/execution-abstraction-review.md +88 -0
- data/docs/architecture/execution-boundaries.md +28 -0
- data/docs/architecture/execution-metadata-and-values.md +6 -0
- data/docs/architecture/multi-agent-handoff.md +6 -5
- data/docs/architecture/persistence.md +27 -9
- data/docs/architecture/r8-unit1.md +104 -0
- data/docs/architecture/r8-unit10.md +72 -0
- data/docs/architecture/r8-unit11.md +71 -0
- data/docs/architecture/r8-unit12.md +67 -0
- data/docs/architecture/r8-unit13.md +61 -0
- data/docs/architecture/r8-unit14.md +72 -0
- data/docs/architecture/r8-unit15.md +65 -0
- data/docs/architecture/r8-unit16.md +54 -0
- data/docs/architecture/r8-unit17.md +73 -0
- data/docs/architecture/r8-unit18.md +92 -0
- data/docs/architecture/r8-unit19.md +61 -0
- data/docs/architecture/r8-unit2.md +55 -0
- data/docs/architecture/r8-unit20.md +51 -0
- data/docs/architecture/r8-unit21.md +79 -0
- data/docs/architecture/r8-unit22.md +74 -0
- data/docs/architecture/r8-unit23.md +81 -0
- data/docs/architecture/r8-unit3.md +130 -0
- data/docs/architecture/r8-unit4.md +79 -0
- data/docs/architecture/r8-unit5.md +115 -0
- data/docs/architecture/r8-unit6.md +71 -0
- data/docs/architecture/r8-unit7.md +70 -0
- data/docs/architecture/r8-unit8.md +78 -0
- data/docs/architecture/r8-unit9.md +73 -0
- data/docs/architecture/refactoring-closure.md +10 -1
- data/docs/architecture/remaining-refactoring-plan.md +16 -0
- data/docs/architecture/storage-execution-boundaries.md +54 -0
- data/docs/architecture/tool-schema-recording-gap.md +6 -0
- data/docs/architecture/tracing.md +1 -1
- data/docs/architecture/workflow-terminal-ownership-design.md +2 -2
- data/docs/architecture.md +24 -11
- data/docs/decisions/015-tool-public-facade-and-rbs-boundary.md +3 -3
- data/docs/decisions/027-llm-adapter-provider-boundary.md +2 -0
- data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +2 -0
- data/docs/decisions/031-durable-multi-agent-coordination.md +2 -0
- data/docs/decisions/032-storage-backend-composition.md +5 -0
- data/docs/decisions/034-handoff-runner-coordination-ownership.md +2 -0
- data/docs/decisions/035-tool-executor-capability-ownership.md +1 -1
- data/docs/decisions/038-responsibility-based-source-layout.md +14 -0
- data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +8 -0
- data/docs/decisions/040-configuration-default-composition.md +12 -1
- data/docs/decisions/045-worker-input-restriction-ownership.md +4 -0
- data/docs/decisions/051-execution-outcome-worker-ownership.md +2 -0
- data/docs/decisions/056-workflow-terminal-policy-ownership.md +1 -1
- data/docs/decisions/058-neutral-storage-primitives.md +5 -0
- data/docs/decisions/059-backend-contracts-and-async-clients.md +74 -0
- data/docs/decisions/060-runtime-settings-and-application-configuration.md +86 -0
- data/docs/decisions/061-persistence-failure-contracts.md +89 -0
- data/docs/decisions/062-content-store-service.md +52 -0
- data/docs/decisions/063-execution-contracts-and-services.md +88 -0
- data/docs/decisions/064-agent-execution-and-multi-agent-responsibility.md +36 -0
- data/docs/decisions/065-vector-and-embeddings-common-operations.md +38 -0
- data/docs/decisions/066-llm-and-tool-operation-contracts.md +20 -0
- data/docs/decisions/067-agent-progress-and-engine-connection.md +29 -0
- data/docs/decisions/068-agent-tool-child-progress-and-tool-operation.md +36 -0
- data/docs/decisions/README.md +18 -1
- data/docs/execution-boundaries-preview.md +45 -0
- data/docs/features.md +8 -3
- data/docs/getting-started.md +9 -6
- data/docs/migrations/0.22.md +2 -2
- data/docs/migrations/durable-semantic-coordination-v2.md +2 -0
- data/docs/migrations/handoff-runner-multi-agent.md +2 -0
- data/docs/migrations/persistence-failure-contracts.md +67 -0
- data/docs/migrations/r8-unit10.md +36 -0
- data/docs/migrations/r8-unit11.md +33 -0
- data/docs/migrations/r8-unit12.md +31 -0
- data/docs/migrations/r8-unit13.md +36 -0
- data/docs/migrations/r8-unit14.md +31 -0
- data/docs/migrations/r8-unit15.md +31 -0
- data/docs/migrations/r8-unit16.md +26 -0
- data/docs/migrations/r8-unit17.md +28 -0
- data/docs/migrations/r8-unit18.md +29 -0
- data/docs/migrations/r8-unit19.md +28 -0
- data/docs/migrations/r8-unit20.md +24 -0
- data/docs/migrations/r8-unit21.md +28 -0
- data/docs/migrations/r8-unit22.md +29 -0
- data/docs/migrations/r8-unit23.md +20 -0
- data/docs/migrations/r8-unit4.md +67 -0
- data/docs/migrations/r8-unit5.md +75 -0
- data/docs/migrations/r8-unit6.md +109 -0
- data/docs/migrations/r8-unit7.md +31 -0
- data/docs/migrations/r8-unit8.md +33 -0
- data/docs/migrations/r8-unit9.md +28 -0
- data/docs/migrations/storage-async-client.md +103 -0
- data/docs/migrations/vector-async-clients.md +110 -0
- data/docs/persistence-backends.md +38 -21
- data/docs/runtime-and-concurrency.md +26 -1
- data/lib/phronomy/agent/admission.rb +78 -0
- data/lib/phronomy/agent/{lifecycle_contract/agent_already_exists_error.rb → agent_already_exists_error.rb} +1 -1
- data/lib/phronomy/agent/{lifecycle_contract/agent_busy_error.rb → agent_busy_error.rb} +1 -1
- data/lib/phronomy/agent/{lifecycle_contract/agent_purged_error.rb → agent_purged_error.rb} +1 -1
- data/lib/phronomy/{recovery → agent/api}/execution_rehydration_required_error.rb +1 -1
- data/lib/phronomy/agent/async_event_api.rb +37 -32
- data/lib/phronomy/agent/base.rb +74 -72
- data/lib/phronomy/agent/before_llm_input.rb +64 -0
- data/lib/phronomy/agent/composition/run_once.rb +1 -1
- data/lib/phronomy/agent/{context_assembly/context_candidate_resolver.rb → context_candidate_resolver.rb} +2 -2
- data/lib/phronomy/agent/{context_assembly/context_assembler.rb → context_preparation.rb} +35 -221
- data/lib/phronomy/agent/control_request.rb +21 -0
- data/lib/phronomy/agent/execution/agent_execution.rb +1 -1
- data/lib/phronomy/agent/execution/agent_invocation.rb +27 -53
- data/lib/phronomy/agent/execution/approval_resume_commit.rb +1 -1
- data/lib/phronomy/agent/execution/dispatch_preparation.rb +17 -127
- data/lib/phronomy/agent/execution/exact_execution.rb +44 -23
- data/lib/phronomy/agent/execution/execution_cancellation.rb +8 -4
- data/lib/phronomy/agent/execution/execution_coordinator.rb +214 -395
- data/lib/phronomy/agent/execution/execution_metadata.rb +1 -0
- data/lib/phronomy/agent/execution/execution_outcome_committer.rb +70 -126
- data/lib/phronomy/agent/execution/execution_registry.rb +6 -2
- data/lib/phronomy/agent/execution/execution_session_runner.rb +22 -27
- data/lib/phronomy/agent/execution/initial_preparation.rb +18 -116
- data/lib/phronomy/agent/execution/{agent_invocation_session_builder.rb → invocation_actions.rb} +54 -194
- data/lib/phronomy/agent/execution/invocation_transitions.rb +39 -1
- data/lib/phronomy/agent/execution/runtime_record_encoder.rb +7 -7
- data/lib/phronomy/agent/execution_change.rb +161 -0
- data/lib/phronomy/agent/execution_environment.rb +36 -0
- data/lib/phronomy/agent/execution_extension_state.rb +21 -0
- data/lib/phronomy/agent/execution_observation.rb +35 -0
- data/lib/phronomy/agent/filterable.rb +182 -0
- data/lib/phronomy/agent/handoff/{handoff_context.rb → transfer_context.rb} +4 -4
- data/lib/phronomy/agent/handoff/{handoff_projection.rb → transfer_projection.rb} +14 -33
- data/lib/phronomy/{llm_contract/transport_error.rb → agent/handoff_error.rb} +1 -1
- data/lib/phronomy/agent/journal/journal_projection.rb +1 -1
- data/lib/phronomy/agent/knowledge_item.rb +13 -0
- data/lib/phronomy/agent/lifecycle/agent_root.rb +1 -1
- data/lib/phronomy/agent/lifecycle/ownership_registry.rb +18 -23
- data/lib/phronomy/agent/persistence/agent_repository.rb +29 -21
- data/lib/phronomy/agent/persistence/cancellation_repository.rb +51 -0
- data/lib/phronomy/agent/persistence/codec.rb +1 -21
- data/lib/phronomy/agent/persistence/execution_repository.rb +57 -41
- data/lib/phronomy/agent/persistence/journal_repository.rb +27 -18
- data/lib/phronomy/agent/persistence/records.rb +33 -0
- data/lib/phronomy/agent/persistence/retention_repository.rb +70 -0
- data/lib/phronomy/agent/persistence/storage_schema.rb +7 -2
- data/lib/phronomy/agent/persistence/watermark.rb +13 -11
- data/lib/phronomy/agent/recovery/invocation_restorer.rb +13 -21
- data/lib/phronomy/agent/recovery/recovery_coordinator/continuation.rb +10 -9
- data/lib/phronomy/agent/recovery/recovery_coordinator/installation.rb +27 -27
- data/lib/phronomy/agent/recovery/recovery_coordinator/resolution.rb +19 -21
- data/lib/phronomy/agent/recovery/recovery_coordinator.rb +23 -16
- data/lib/phronomy/agent/recovery/recovery_rules.rb +119 -0
- data/lib/phronomy/agent/recovery/recovery_support.rb +8 -11
- data/lib/phronomy/agent/reserved_execution.rb +16 -0
- data/lib/phronomy/agent/retention.rb +17 -0
- data/lib/phronomy/agent/runtime_binding/engine_environment.rb +60 -0
- data/lib/phronomy/agent/runtime_binding/engine_session_builder.rb +34 -0
- data/lib/phronomy/agent/runtime_binding/phase_machine_builder.rb +64 -0
- data/lib/phronomy/agent/runtime_binding/tool_session_builder.rb +34 -0
- data/lib/phronomy/agent/{context_assembly/ruby_llm_materializer.rb → runtime_input.rb} +18 -35
- data/lib/phronomy/agent/{context_assembly/saved_context_reader.rb → saved_context_reader.rb} +6 -4
- data/lib/phronomy/agent/settings.rb +47 -0
- data/lib/phronomy/agent/{context_assembly/state_writer.rb → state_writer.rb} +4 -3
- data/lib/phronomy/agent/store.rb +253 -0
- data/lib/phronomy/agent/{lifecycle_contract/stream_callback_error.rb → stream_callback_error.rb} +1 -1
- data/lib/phronomy/agent/tool_execution/tool_binding.rb +4 -64
- data/lib/phronomy/agent/tool_execution/tool_invocation.rb +50 -219
- data/lib/phronomy/agent/tool_execution/tool_invocation_actions.rb +107 -0
- data/lib/phronomy/agent/tool_execution/tool_invocation_transitions.rb +70 -0
- data/lib/phronomy/configuration/runtime_settings.rb +49 -0
- data/lib/phronomy/content_store/storage_schema.rb +1 -0
- data/lib/phronomy/content_store/stored_contents.rb +2 -1
- data/lib/phronomy/context/assembly.rb +202 -0
- data/lib/phronomy/{llm_contract/context_budget_exceeded_error.rb → context/budget_exceeded_error.rb} +3 -1
- data/lib/phronomy/context/candidate.rb +51 -0
- data/lib/phronomy/context/constraint.rb +47 -0
- data/lib/phronomy/{agent/context_contract → context}/context_plan.rb +1 -1
- data/lib/phronomy/{agent/context_contract → context}/context_plan_validator.rb +3 -3
- data/lib/phronomy/{agent/context_contract → context}/context_policy.rb +1 -1
- data/lib/phronomy/{agent/context_contract → context}/context_policy_input.rb +1 -1
- data/lib/phronomy/{agent/context_assembly → context}/context_policy_input_builder.rb +1 -1
- data/lib/phronomy/context/default_policy.rb +140 -0
- data/lib/phronomy/context/final_budget_validator.rb +33 -0
- data/lib/phronomy/{agent/context_contract → context}/llm_input_build_context.rb +1 -1
- data/lib/phronomy/{agent/context_contract → context}/llm_input_manifest.rb +19 -19
- data/lib/phronomy/{agent/context_contract → context}/llm_input_patch.rb +1 -1
- data/lib/phronomy/context/manifest_error.rb +10 -0
- data/lib/phronomy/context/prompt_template.rb +98 -0
- data/lib/phronomy/{vector_store → documents}/loader/base.rb +1 -1
- data/lib/phronomy/{vector_store → documents}/loader/csv_loader.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/loader/markdown_loader.rb +3 -3
- data/lib/phronomy/{vector_store → documents}/loader/plain_text_loader.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/splitter/base.rb +1 -1
- data/lib/phronomy/{vector_store → documents}/splitter/fixed_size_splitter.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/splitter/recursive_splitter.rb +2 -2
- data/lib/phronomy/embeddings/async/async_client.rb +44 -0
- data/lib/phronomy/embeddings/backends/ruby_llm_embeddings.rb +48 -0
- data/lib/phronomy/embeddings/base.rb +43 -0
- data/lib/phronomy/embeddings/invalid_result_error.rb +11 -0
- data/lib/phronomy/embeddings/transport_error.rb +11 -0
- data/lib/phronomy/engine/concurrency/offload_pool.rb +36 -21
- data/lib/phronomy/engine/concurrency/pool_registry.rb +47 -12
- data/lib/phronomy/engine/diagnostics.rb +1 -1
- data/lib/phronomy/engine/event_loop.rb +7 -7
- data/lib/phronomy/engine/execution_binding.rb +23 -0
- data/lib/phronomy/engine/execution_receiver_binding.rb +43 -0
- data/lib/phronomy/engine/fsm_protocol.rb +1 -6
- data/lib/phronomy/engine/fsm_session.rb +3 -3
- data/lib/phronomy/engine/metrics.rb +16 -14
- data/lib/phronomy/engine/runtime/timer_queue.rb +1 -1
- data/lib/phronomy/engine/runtime.rb +25 -12
- data/lib/phronomy/engine/terminal_decision.rb +7 -0
- data/lib/phronomy/execution/async_operation.rb +41 -0
- data/lib/phronomy/{engine → execution}/blocking.rb +2 -2
- data/lib/phronomy/{engine → execution}/concurrency/cancellation_token.rb +1 -1
- data/lib/phronomy/{engine → execution}/concurrency/deadline.rb +0 -17
- data/lib/phronomy/{engine → execution}/concurrency/physical_completion_task.rb +1 -1
- data/lib/phronomy/{engine → execution}/concurrency/result_collector.rb +1 -1
- data/lib/phronomy/{engine → execution}/concurrency/result_composition.rb +33 -12
- data/lib/phronomy/{engine/concurrency/subscriptions.rb → execution/concurrency/result_subscriptions.rb} +14 -18
- data/lib/phronomy/execution/concurrency/subscriptions.rb +25 -0
- data/lib/phronomy/{engine → execution}/execution.rb +61 -27
- data/lib/phronomy/execution/execution_receiver.rb +78 -0
- data/lib/phronomy/execution/execution_scope_state.rb +35 -0
- data/lib/phronomy/{engine → execution}/invocation_context.rb +0 -15
- data/lib/phronomy/execution/invocation_controls.rb +73 -0
- data/lib/phronomy/execution/runnable.rb +24 -0
- data/lib/phronomy/{engine → execution}/task_result.rb +3 -3
- data/lib/phronomy/execution/wait_policy.rb +24 -0
- data/lib/phronomy/filter/{contract/filter_block_error.rb → filter_block_error.rb} +1 -1
- data/lib/phronomy/generation/generator_verifier/agent_operation.rb +65 -0
- data/lib/phronomy/generation/generator_verifier/default_parser.rb +27 -0
- data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +2 -2
- data/lib/phronomy/generation/generator_verifier.rb +7 -7
- data/lib/phronomy/llm_adapter/async/async_client.rb +47 -0
- data/lib/phronomy/{llm_contract → llm_adapter}/authentication_error.rb +3 -1
- data/lib/phronomy/llm_adapter/backends/ruby_llm.rb +183 -0
- data/lib/phronomy/llm_adapter/base.rb +49 -68
- data/lib/phronomy/{llm_contract → llm_adapter}/context_length_error.rb +3 -1
- data/lib/phronomy/llm_adapter/invalid_result_error.rb +10 -0
- data/lib/phronomy/llm_adapter/message.rb +62 -0
- data/lib/phronomy/{llm_contract → llm_adapter}/rate_limit_error.rb +3 -1
- data/lib/phronomy/llm_adapter/request.rb +79 -0
- data/lib/phronomy/llm_adapter/response.rb +36 -0
- data/lib/phronomy/llm_adapter/stream_chunk.rb +18 -0
- data/lib/phronomy/llm_adapter/token_usage.rb +64 -0
- data/lib/phronomy/llm_adapter/transport_error.rb +9 -0
- data/lib/phronomy/llm_adapter.rb +3 -16
- data/lib/phronomy/multi_agent/admission_registry.rb +1 -8
- data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +108 -84
- data/lib/phronomy/multi_agent/execution_environment.rb +29 -0
- data/lib/phronomy/{agent/handoff → multi_agent}/handoff.rb +2 -2
- data/lib/phronomy/{agent/handoff → multi_agent}/handoff_capability_factory.rb +21 -2
- data/lib/phronomy/multi_agent/handoff_participant.rb +79 -0
- data/lib/phronomy/{agent/handoff → multi_agent}/handoff_policy.rb +1 -1
- data/lib/phronomy/multi_agent/handoff_runner.rb +77 -100
- data/lib/phronomy/{agent/handoff → multi_agent}/handoff_state.rb +1 -1
- data/lib/phronomy/multi_agent/orchestrator.rb +61 -75
- data/lib/phronomy/multi_agent/persistence/codec.rb +20 -0
- data/lib/phronomy/multi_agent/persistence/handoff_state_repository.rb +77 -0
- data/lib/phronomy/multi_agent/persistence/records.rb +27 -0
- data/lib/phronomy/multi_agent/persistence/storage_schema.rb +21 -0
- data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +58 -42
- data/lib/phronomy/multi_agent/persistence/team_repository.rb +29 -21
- data/lib/phronomy/multi_agent/reserved_child_admission.rb +88 -0
- data/lib/phronomy/multi_agent/runtime_binding/engine_environment.rb +35 -0
- data/lib/phronomy/multi_agent/shared_state.rb +2 -2
- data/lib/phronomy/multi_agent/store.rb +156 -0
- data/lib/phronomy/multi_agent/team_coordinator.rb +83 -74
- data/lib/phronomy/multi_agent/team_ownership_registry.rb +2 -12
- data/lib/phronomy/output_parser/parse_error.rb +7 -0
- data/lib/phronomy/persistence/content_repository.rb +26 -0
- data/lib/phronomy/persistence/errors.rb +16 -0
- data/lib/phronomy/persistence/migration/initial_format_migration.rb +2 -2
- data/lib/phronomy/persistence/persistence.rb +23 -0
- data/lib/phronomy/persistence/save_outcome.rb +24 -0
- data/lib/phronomy/persistence/snapshot_comparison.rb +81 -0
- data/lib/phronomy/persistence/storage_boundary.rb +32 -0
- data/lib/phronomy/persistence/transaction.rb +86 -0
- data/lib/phronomy/persistence_composition/storage_schema.rb +9 -4
- data/lib/phronomy/persistence_composition/stores.rb +34 -0
- data/lib/phronomy/persistence_composition/unit4_migration.rb +276 -0
- data/lib/phronomy/runtime_composition/agent_defaults.rb +5 -1
- data/lib/phronomy/runtime_composition/configuration.rb +146 -0
- data/lib/phronomy/runtime_composition/evaluation_defaults.rb +7 -0
- data/lib/phronomy/runtime_composition/execution_defaults.rb +5 -0
- data/lib/phronomy/runtime_composition/generation_defaults.rb +6 -0
- data/lib/phronomy/runtime_composition/global_configuration.rb +38 -0
- data/lib/phronomy/runtime_composition/multi_agent_defaults.rb +9 -0
- data/lib/phronomy/runtime_composition/workflow_defaults.rb +4 -0
- data/lib/phronomy/storage/async/async_client.rb +60 -0
- data/lib/phronomy/storage/backend.rb +8 -2
- data/lib/phronomy/testing/eval/metrics.rb +1 -1
- data/lib/phronomy/testing/eval/scorer/llm_judge.rb +19 -3
- data/lib/phronomy/testing/persistence_contract/a_content_store.rb +2 -2
- data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +6 -6
- data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +55 -52
- data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +3 -3
- data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +7 -7
- data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +11 -11
- data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +57 -51
- data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +1 -1
- data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +23 -22
- data/lib/phronomy/tool/authorization.rb +77 -0
- data/lib/phronomy/tool/base.rb +549 -9
- data/lib/phronomy/tool/call_request.rb +40 -0
- data/lib/phronomy/{agent/tool_execution/tool_definition_set.rb → tool/definition_set.rb} +7 -8
- data/lib/phronomy/tool/operation.rb +88 -0
- data/lib/phronomy/tool/schema.rb +214 -0
- data/lib/phronomy/tool/settings.rb +31 -0
- data/lib/phronomy/tool/tool_error.rb +7 -0
- data/lib/phronomy/tool/tool_executor.rb +58 -0
- data/lib/phronomy/tools/agent.rb +22 -90
- data/lib/phronomy/tools/mcp.rb +21 -14
- data/lib/phronomy/tools/vector_search.rb +4 -4
- data/lib/phronomy/tracing/automatic.rb +11 -6
- data/lib/phronomy/tracing/base.rb +1 -1
- data/lib/phronomy/tracing/observation.rb +37 -0
- data/lib/phronomy/tracing/settings.rb +36 -0
- data/lib/phronomy/vector_store/{async_backend.rb → async/async_client.rb} +35 -29
- data/lib/phronomy/vector_store/{in_memory.rb → backends/in_memory.rb} +12 -10
- data/lib/phronomy/vector_store/{pgvector.rb → backends/pgvector.rb} +44 -16
- data/lib/phronomy/vector_store/{redis_search.rb → backends/redis_search.rb} +74 -37
- data/lib/phronomy/vector_store/base.rb +118 -49
- data/lib/phronomy/vector_store/invalid_result_error.rb +12 -0
- data/lib/phronomy/vector_store.rb +2 -17
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/workflow/completion.rb +11 -0
- data/lib/phronomy/workflow/execution/workflow.rb +7 -7
- data/lib/phronomy/workflow/execution/workflow_action_rules.rb +67 -0
- data/lib/phronomy/workflow/execution/workflow_context.rb +2 -2
- data/lib/phronomy/workflow/execution/workflow_execution_environment.rb +27 -0
- data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +3 -5
- data/lib/phronomy/workflow/execution/workflow_runner.rb +58 -79
- data/lib/phronomy/workflow/execution/workflow_settings.rb +28 -0
- data/lib/phronomy/workflow/persistence/state_repository.rb +28 -22
- data/lib/phronomy/workflow/persistence/storage_schema.rb +13 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_engine_environment.rb +38 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb +156 -0
- data/lib/phronomy/workflow/{execution → runtime_binding}/workflow_terminal_policy.rb +4 -4
- data/lib/phronomy.rb +57 -16
- data/scripts/api_snapshot.rb +35 -15
- data/sig/_private/phronomy/agent/settings.rbs +19 -0
- data/sig/_private/phronomy/configuration/runtime_settings.rbs +20 -0
- data/sig/_private/phronomy/llm_adapter/async/async_client.rbs +10 -0
- data/sig/_private/phronomy/multi_agent/store.rbs +8 -0
- data/sig/_private/phronomy/persistence/storage_boundary.rbs +7 -0
- data/sig/_private/phronomy/tool/settings.rbs +13 -0
- data/sig/_private/phronomy/tracing/settings.rbs +15 -0
- data/sig/_private/phronomy/workflow/execution/workflow_settings.rbs +13 -0
- data/sig/phronomy/agent/admission.rbs +19 -0
- data/sig/phronomy/agent/execution_contract.rbs +132 -0
- data/sig/phronomy/agent/execution_environment.rbs +20 -0
- data/sig/phronomy/agent/store.rbs +73 -0
- data/sig/phronomy/agent.rbs +14 -138
- data/sig/phronomy/common.rbs +7 -0
- data/sig/phronomy/content_store/contracts.rbs +13 -0
- data/sig/phronomy/context.rbs +152 -0
- data/sig/phronomy/embeddings/async/async_client.rbs +9 -0
- data/sig/phronomy/embeddings/base.rbs +17 -0
- data/sig/phronomy/engine/concurrency/timer_bindings.rbs +22 -0
- data/sig/phronomy/engine/control_bindings.rbs +11 -0
- data/sig/phronomy/engine/control_protocols.rbs +23 -0
- data/sig/phronomy/engine/execution_binding.rbs +6 -0
- data/sig/phronomy/engine/execution_receiver.rbs +15 -0
- data/sig/phronomy/engine/observability.rbs +13 -0
- data/sig/phronomy/execution/async_operation.rbs +7 -0
- data/sig/phronomy/execution/blocking.rbs +5 -0
- data/sig/phronomy/execution/concurrency/cancellation_token.rbs +13 -0
- data/sig/phronomy/execution/concurrency/deadline.rbs +10 -0
- data/sig/phronomy/execution/concurrency/result_composition.rbs +7 -0
- data/sig/phronomy/execution/control_protocols.rbs +18 -0
- data/sig/phronomy/execution/errors.rbs +7 -0
- data/sig/phronomy/execution/execution.rbs +41 -0
- data/sig/phronomy/execution/execution_receiver.rbs +40 -0
- data/sig/phronomy/execution/execution_scope_state.rbs +9 -0
- data/sig/phronomy/execution/invocation_context.rbs +18 -0
- data/sig/phronomy/execution/invocation_controls.rbs +13 -0
- data/sig/phronomy/execution/task_result.rbs +29 -0
- data/sig/phronomy/extensions.rbs +0 -21
- data/sig/phronomy/handoff.rbs +10 -4
- data/sig/phronomy/llm_adapter/backends/ruby_llm.rbs +6 -0
- data/sig/phronomy/llm_adapter/base.rbs +14 -0
- data/sig/phronomy/llm_adapter/usage.rbs +14 -0
- data/sig/phronomy/llm_adapter/values.rbs +43 -0
- data/sig/phronomy/llm_context_window.rbs +10 -0
- data/sig/phronomy/multi_agent/reserved_child_admission.rbs +8 -0
- data/sig/phronomy/multi_agent/store.rbs +54 -0
- data/sig/phronomy/multi_agent.rbs +6 -3
- data/sig/phronomy/persistence/errors.rbs +18 -0
- data/sig/phronomy/persistence/migration.rbs +13 -0
- data/sig/phronomy/persistence/transaction.rbs +19 -0
- data/sig/phronomy/persistence.rbs +1 -99
- data/sig/phronomy/persistence_composition.rbs +19 -0
- data/sig/phronomy/runtime_composition/configuration.rbs +36 -0
- data/sig/phronomy/storage/async/async_client.rbs +8 -0
- data/sig/phronomy/storage/backends/in_memory.rbs +9 -0
- data/sig/phronomy/{storage.rbs → storage/contracts.rbs} +2 -16
- data/sig/phronomy/tool.rbs +48 -4
- data/sig/phronomy/tracing/observation.rbs +7 -0
- data/sig/phronomy/vector_store/async/async_client.rbs +13 -0
- data/sig/phronomy/vector_store/base.rbs +23 -0
- data/sig/phronomy/workflow/execution/workflow_execution_environment.rbs +14 -0
- data/sig/phronomy/workflow/runtime_binding/workflow_terminal_policy.rbs +7 -0
- data/sig/phronomy/workflow/store.rbs +8 -0
- data/sig/phronomy/workflow.rbs +1 -1
- data/sig/phronomy.rbs +0 -49
- metadata +318 -117
- data/lib/phronomy/agent/concerns/before_llm_input.rb +0 -66
- data/lib/phronomy/agent/concerns/error_translation.rb +0 -45
- data/lib/phronomy/agent/concerns/filterable.rb +0 -184
- data/lib/phronomy/agent/context/capability/base.rb +0 -548
- data/lib/phronomy/agent/context/capability/tool_executor.rb +0 -62
- data/lib/phronomy/agent/context/instruction/prompt_template.rb +0 -102
- data/lib/phronomy/agent/context_assembly/runtime_chat_builder.rb +0 -36
- data/lib/phronomy/agent/context_assembly/token_budget_resolver.rb +0 -22
- data/lib/phronomy/agent/context_parts/validators/final_budget_validator.rb +0 -37
- data/lib/phronomy/agent/context_policies/default.rb +0 -142
- data/lib/phronomy/agent/execution/phase_machine_builder.rb +0 -101
- data/lib/phronomy/agent/execution/provider_call_outcome.rb +0 -106
- data/lib/phronomy/agent/handoff/handoff_execution_coordinator.rb +0 -15
- data/lib/phronomy/agent/handoff/handoff_outcome_committer.rb +0 -131
- data/lib/phronomy/agent/handoff/handoff_request.rb +0 -45
- data/lib/phronomy/agent/lifecycle_contract/handoff_error.rb +0 -7
- data/lib/phronomy/agent/persistence/handoff_state_repository.rb +0 -58
- data/lib/phronomy/agent/persistence/queries.rb +0 -61
- data/lib/phronomy/agent/selection/candidate.rb +0 -53
- data/lib/phronomy/agent/selection/constraint.rb +0 -49
- data/lib/phronomy/agent/tool_execution/tool_call_intercepted.rb +0 -29
- data/lib/phronomy/agent/tool_execution/tool_invocation_session_builder.rb +0 -276
- data/lib/phronomy/configuration/configuration.rb +0 -68
- data/lib/phronomy/configuration/global_configuration.rb +0 -26
- data/lib/phronomy/engine/concurrency/cancellation_scope.rb +0 -124
- data/lib/phronomy/engine/concurrency/operation_binding.rb +0 -43
- data/lib/phronomy/engine/execution_receiver.rb +0 -65
- data/lib/phronomy/engine/runnable.rb +0 -54
- data/lib/phronomy/llm_adapter/ruby_llm.rb +0 -47
- data/lib/phronomy/llm_contract/token_usage.rb +0 -85
- data/lib/phronomy/multi_agent/persistence/queries.rb +0 -30
- data/lib/phronomy/multi_agent/storage_contract/team_storage_schema.rb +0 -15
- data/lib/phronomy/output_parser/contract/parse_error.rb +0 -7
- data/lib/phronomy/persistence/api/persistence.rb +0 -140
- data/lib/phronomy/persistence_composition/repositories.rb +0 -77
- data/lib/phronomy/recovery/recovery.rb +0 -186
- data/lib/phronomy/tool/contract/tool_error.rb +0 -7
- data/lib/phronomy/vector_store/embeddings/base.rb +0 -44
- data/lib/phronomy/vector_store/embeddings/ruby_llm_embeddings.rb +0 -47
- data/lib/phronomy/workflow/phase_machine_builder.rb +0 -205
- data/lib/phronomy/workflow/storage_contract/workflow_storage_schema.rb +0 -9
- data/sig/phronomy/execution_receiver.rbs +0 -34
- data/sig/phronomy/llm_adapter.rbs +0 -11
- data/sig/phronomy/runtime.rbs +0 -76
- /data/lib/phronomy/agent/{context_assembly/context_importer.rb → context_importer.rb} +0 -0
- /data/lib/phronomy/{engine → execution}/backpressure_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/cancellation_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/concurrency/worker_input_restricted.rb +0 -0
- /data/lib/phronomy/{engine → execution}/event_loop_reentrancy_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/execution_cancellation_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/execution_timeout_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/invalid_async_entry_action_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/invalid_async_transition_action_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/invalid_async_workflow_action_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/pool_shutdown_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/recursion_limit_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/runtime_shutdown_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/runtime_shutdown_reentrancy_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/scheduler_reentrancy_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/timeout_error.rb +0 -0
|
@@ -70,7 +70,7 @@ module Phronomy
|
|
|
70
70
|
|
|
71
71
|
def classify(execution)
|
|
72
72
|
if framework_batch?(execution)
|
|
73
|
-
return Phronomy::
|
|
73
|
+
return Phronomy::Agent::RecoveryRules::Classification.new(disposition: Phronomy::Agent::RecoveryRules::RESUMABLE, reason: :framework_tools)
|
|
74
74
|
end
|
|
75
75
|
if execution.status == :preparing &&
|
|
76
76
|
execution.phase.to_sym == :preparing
|
|
@@ -79,16 +79,16 @@ module Phronomy
|
|
|
79
79
|
"execution #{execution.execution_id} is :preparing but replay safety " \
|
|
80
80
|
"was not durably established"
|
|
81
81
|
end
|
|
82
|
-
return Phronomy::
|
|
83
|
-
disposition: Phronomy::
|
|
82
|
+
return Phronomy::Agent::RecoveryRules::Classification.new(
|
|
83
|
+
disposition: Phronomy::Agent::RecoveryRules::RESUMABLE,
|
|
84
84
|
reason: :initial_preparation
|
|
85
85
|
)
|
|
86
86
|
end
|
|
87
87
|
|
|
88
88
|
if execution.status == :suspended &&
|
|
89
89
|
execution.phase.to_sym == :approval
|
|
90
|
-
return Phronomy::
|
|
91
|
-
disposition: Phronomy::
|
|
90
|
+
return Phronomy::Agent::RecoveryRules::Classification.new(
|
|
91
|
+
disposition: Phronomy::Agent::RecoveryRules::RESUMABLE,
|
|
92
92
|
reason: :approval_wait,
|
|
93
93
|
facts: {
|
|
94
94
|
approval_request_id:
|
|
@@ -105,8 +105,8 @@ module Phronomy
|
|
|
105
105
|
when :recovery_provider_completed,
|
|
106
106
|
:recovery_tools_completed,
|
|
107
107
|
:recovery_resolved_failed
|
|
108
|
-
return Phronomy::
|
|
109
|
-
disposition: Phronomy::
|
|
108
|
+
return Phronomy::Agent::RecoveryRules::Classification.new(
|
|
109
|
+
disposition: Phronomy::Agent::RecoveryRules::RESUMABLE,
|
|
110
110
|
reason: execution.phase
|
|
111
111
|
)
|
|
112
112
|
when :resuming
|
|
@@ -116,8 +116,8 @@ module Phronomy
|
|
|
116
116
|
execution.approval_request[:approved]
|
|
117
117
|
)
|
|
118
118
|
unless approved
|
|
119
|
-
return Phronomy::
|
|
120
|
-
disposition: Phronomy::
|
|
119
|
+
return Phronomy::Agent::RecoveryRules::Classification.new(
|
|
120
|
+
disposition: Phronomy::Agent::RecoveryRules::RESUMABLE,
|
|
121
121
|
reason: :approval_rejection_committed
|
|
122
122
|
)
|
|
123
123
|
end
|
|
@@ -127,8 +127,8 @@ module Phronomy
|
|
|
127
127
|
execution
|
|
128
128
|
)
|
|
129
129
|
if descriptor
|
|
130
|
-
return Phronomy::
|
|
131
|
-
disposition: Phronomy::
|
|
130
|
+
return Phronomy::Agent::RecoveryRules::Classification.new(
|
|
131
|
+
disposition: Phronomy::Agent::RecoveryRules::RESOLUTION_REQUIRED,
|
|
132
132
|
reason: descriptor.fetch(:reason),
|
|
133
133
|
subject: descriptor.fetch(:subject),
|
|
134
134
|
allowed_outcomes:
|
|
@@ -143,20 +143,20 @@ module Phronomy
|
|
|
143
143
|
end
|
|
144
144
|
|
|
145
145
|
def install_on_event_loop(command)
|
|
146
|
-
|
|
146
|
+
environment = @environment
|
|
147
147
|
plan = command.plan
|
|
148
148
|
execution = plan.execution
|
|
149
|
-
main = agent.send(:execution_coordinator_for, agent.
|
|
149
|
+
main = agent.send(:execution_coordinator_for, agent.__execution_wiring)
|
|
150
150
|
admitted = false
|
|
151
151
|
bound = false
|
|
152
152
|
installed = false
|
|
153
153
|
|
|
154
|
-
|
|
154
|
+
environment.registry.admit_agent_execution(
|
|
155
155
|
agent.agent_id,
|
|
156
156
|
owner_token: command.owner_token
|
|
157
157
|
)
|
|
158
158
|
admitted = true
|
|
159
|
-
|
|
159
|
+
environment.registry.bind_agent_execution_admission(
|
|
160
160
|
agent.agent_id,
|
|
161
161
|
owner_token: command.owner_token,
|
|
162
162
|
execution_id: execution.execution_id
|
|
@@ -167,11 +167,11 @@ module Phronomy
|
|
|
167
167
|
if execution.status == :suspended || execution.phase.to_sym == :resuming
|
|
168
168
|
invocation = InvocationRestorer.build_invocation_for_suspended(
|
|
169
169
|
agent, execution, plan.projection, main, agent.send(:_phronomy_event_listener),
|
|
170
|
-
assistant_message: plan.material.assistant_message
|
|
170
|
+
assistant_message: plan.material.assistant_message, cancellation_requested: plan.material.cancellation_requested
|
|
171
171
|
)
|
|
172
172
|
end
|
|
173
173
|
|
|
174
|
-
|
|
174
|
+
environment.registry.install_agent_execution(
|
|
175
175
|
execution_id: execution.execution_id,
|
|
176
176
|
agent: agent,
|
|
177
177
|
coordinator: main,
|
|
@@ -184,7 +184,7 @@ module Phronomy
|
|
|
184
184
|
installed = true
|
|
185
185
|
|
|
186
186
|
if execution.status == :suspended
|
|
187
|
-
|
|
187
|
+
environment.registry.mark_agent_execution_admission(
|
|
188
188
|
agent.agent_id,
|
|
189
189
|
execution_id: execution.execution_id,
|
|
190
190
|
state: :suspended
|
|
@@ -214,8 +214,8 @@ module Phronomy
|
|
|
214
214
|
end
|
|
215
215
|
|
|
216
216
|
case plan.classification.disposition
|
|
217
|
-
when Phronomy::
|
|
218
|
-
|
|
217
|
+
when Phronomy::Agent::RecoveryRules::RESOLUTION_REQUIRED
|
|
218
|
+
environment.registry.mark_agent_execution_admission(
|
|
219
219
|
agent.agent_id,
|
|
220
220
|
execution_id: execution.execution_id,
|
|
221
221
|
state: :recovery_required
|
|
@@ -247,7 +247,7 @@ module Phronomy
|
|
|
247
247
|
)
|
|
248
248
|
end
|
|
249
249
|
command.completion.complete(agent)
|
|
250
|
-
when Phronomy::
|
|
250
|
+
when Phronomy::Agent::RecoveryRules::RESUMABLE
|
|
251
251
|
continue_resumable_on_event_loop(
|
|
252
252
|
execution,
|
|
253
253
|
completion: command.completion, material: plan.material
|
|
@@ -260,7 +260,7 @@ module Phronomy
|
|
|
260
260
|
# simplecov:disable
|
|
261
261
|
if installed
|
|
262
262
|
begin
|
|
263
|
-
|
|
263
|
+
environment.registry.release_agent_execution(
|
|
264
264
|
execution.execution_id
|
|
265
265
|
)
|
|
266
266
|
rescue
|
|
@@ -270,7 +270,7 @@ module Phronomy
|
|
|
270
270
|
if admitted
|
|
271
271
|
if bound
|
|
272
272
|
begin
|
|
273
|
-
|
|
273
|
+
environment.registry.release_agent_execution_admission(
|
|
274
274
|
agent.agent_id,
|
|
275
275
|
execution_id: execution&.execution_id
|
|
276
276
|
)
|
|
@@ -279,7 +279,7 @@ module Phronomy
|
|
|
279
279
|
end
|
|
280
280
|
else
|
|
281
281
|
begin
|
|
282
|
-
|
|
282
|
+
environment.registry.release_agent_execution_admission(
|
|
283
283
|
agent.agent_id,
|
|
284
284
|
owner_token: command.owner_token
|
|
285
285
|
)
|
|
@@ -311,8 +311,8 @@ module Phronomy
|
|
|
311
311
|
completion:, material:
|
|
312
312
|
)
|
|
313
313
|
# simplecov:disable
|
|
314
|
-
|
|
315
|
-
main = agent.send(:execution_coordinator_for, agent.
|
|
314
|
+
environment = @environment
|
|
315
|
+
main = agent.send(:execution_coordinator_for, agent.__execution_wiring)
|
|
316
316
|
|
|
317
317
|
if framework_batch?(execution)
|
|
318
318
|
internal = Phronomy::TaskResult.deferred(name: "framework-tool-recovery:#{execution.execution_id}")
|
|
@@ -348,7 +348,7 @@ module Phronomy
|
|
|
348
348
|
name: "agent-recovery-auto:#{execution.execution_id}"
|
|
349
349
|
)
|
|
350
350
|
observe_recovery_execution(internal_task, execution)
|
|
351
|
-
state =
|
|
351
|
+
state = environment.registry.agent_execution_state(execution.execution_id)
|
|
352
352
|
main.deliver_on_event_loop(ExecutionCoordinator::ContinueRecoveredCommand.new(
|
|
353
353
|
coordinator: main,
|
|
354
354
|
execution_id: execution.execution_id,
|
|
@@ -9,8 +9,8 @@ module Phronomy
|
|
|
9
9
|
private
|
|
10
10
|
|
|
11
11
|
def begin_resolve_on_event_loop(request)
|
|
12
|
-
|
|
13
|
-
state =
|
|
12
|
+
environment = @environment
|
|
13
|
+
state = environment.registry.agent_execution_state(
|
|
14
14
|
request.execution_id
|
|
15
15
|
)
|
|
16
16
|
unless state && state.agent.equal?(agent)
|
|
@@ -26,7 +26,7 @@ module Phronomy
|
|
|
26
26
|
unless current.execution_revision ==
|
|
27
27
|
request.expected_execution_revision
|
|
28
28
|
request.completion.fail(
|
|
29
|
-
Phronomy::
|
|
29
|
+
Phronomy::Persistence::StateConflictError.new(
|
|
30
30
|
"Recovery resolution revision conflict: expected " \
|
|
31
31
|
"#{request.expected_execution_revision}, actual " \
|
|
32
32
|
"#{current.execution_revision}"
|
|
@@ -38,7 +38,7 @@ module Phronomy
|
|
|
38
38
|
descriptor =
|
|
39
39
|
coordination_recovery_descriptor(current)
|
|
40
40
|
unless descriptor &&
|
|
41
|
-
Phronomy::
|
|
41
|
+
Phronomy::Agent::RecoveryRules.subject_equal?(
|
|
42
42
|
descriptor.fetch(:subject),
|
|
43
43
|
request.subject
|
|
44
44
|
)
|
|
@@ -69,14 +69,12 @@ module Phronomy
|
|
|
69
69
|
result: request.result,
|
|
70
70
|
failure: request.failure
|
|
71
71
|
)
|
|
72
|
-
|
|
72
|
+
environment.registry.mark_agent_execution_admission(
|
|
73
73
|
agent.agent_id,
|
|
74
74
|
execution_id: current.execution_id,
|
|
75
75
|
state: :recovery_required
|
|
76
76
|
)
|
|
77
|
-
task = @
|
|
78
|
-
on_full: :raise
|
|
79
|
-
) do
|
|
77
|
+
task = @environment.submit(on_full: :raise) do
|
|
80
78
|
prepare_resolution(operation)
|
|
81
79
|
end
|
|
82
80
|
task.on_complete do |result, error|
|
|
@@ -118,7 +116,7 @@ module Phronomy
|
|
|
118
116
|
unless current.metadata[
|
|
119
117
|
ExecutionMetadata::PENDING_LLM_ID_KEY
|
|
120
118
|
].to_s == llm_call_id
|
|
121
|
-
raise Phronomy::
|
|
119
|
+
raise Phronomy::Persistence::StateConflictError,
|
|
122
120
|
"LLM Recovery subject is no longer pending"
|
|
123
121
|
end
|
|
124
122
|
|
|
@@ -165,7 +163,7 @@ module Phronomy
|
|
|
165
163
|
when :succeeded
|
|
166
164
|
# simplecov:disable
|
|
167
165
|
outcome =
|
|
168
|
-
Phronomy::
|
|
166
|
+
Phronomy::LLMAdapter::Response.from_h(
|
|
169
167
|
operation.result
|
|
170
168
|
)
|
|
171
169
|
updated = nil
|
|
@@ -267,7 +265,7 @@ module Phronomy
|
|
|
267
265
|
recovery = RecoverySupport.build_recovery_hash(subjects)
|
|
268
266
|
end
|
|
269
267
|
unless recovery
|
|
270
|
-
raise Phronomy::
|
|
268
|
+
raise Phronomy::Persistence::StateConflictError,
|
|
271
269
|
"Tool Recovery state is missing"
|
|
272
270
|
end
|
|
273
271
|
|
|
@@ -280,7 +278,7 @@ module Phronomy
|
|
|
280
278
|
hash.fetch("state", "unresolved") == "unresolved"
|
|
281
279
|
end
|
|
282
280
|
unless subject_entry
|
|
283
|
-
raise Phronomy::
|
|
281
|
+
raise Phronomy::Persistence::StateConflictError,
|
|
284
282
|
"Tool Recovery subject is no longer unresolved"
|
|
285
283
|
end
|
|
286
284
|
subject_entry =
|
|
@@ -415,10 +413,10 @@ module Phronomy
|
|
|
415
413
|
# simplecov:enable
|
|
416
414
|
|
|
417
415
|
def known_non_f1_error?(error)
|
|
418
|
-
error.is_a?(Phronomy::
|
|
419
|
-
error.is_a?(Phronomy::
|
|
420
|
-
error.is_a?(Phronomy::
|
|
421
|
-
error.is_a?(Phronomy::
|
|
416
|
+
error.is_a?(Phronomy::Persistence::ConflictError) ||
|
|
417
|
+
error.is_a?(Phronomy::Persistence::NotFoundError) ||
|
|
418
|
+
error.is_a?(Phronomy::Persistence::SerializationError) ||
|
|
419
|
+
error.is_a?(Phronomy::Persistence::UnsupportedBackendError) ||
|
|
422
420
|
error.is_a?(ArgumentError) ||
|
|
423
421
|
error.is_a?(Phronomy::ConfigurationError)
|
|
424
422
|
end
|
|
@@ -474,27 +472,27 @@ module Phronomy
|
|
|
474
472
|
if current.to_h == operation.execution.to_h
|
|
475
473
|
raise(error.is_a?(ResolutionOutcomeUnknownError) ? error.original_error : error)
|
|
476
474
|
end
|
|
477
|
-
raise Phronomy::
|
|
475
|
+
raise Phronomy::Persistence::StateConflictError,
|
|
478
476
|
"Recovery resolution durable outcome conflicts with both expected pre-state and intended post-state"
|
|
479
477
|
end
|
|
480
478
|
|
|
481
479
|
def apply_resolve_on_event_loop(ready)
|
|
482
480
|
request = ready.request
|
|
483
|
-
|
|
484
|
-
state =
|
|
481
|
+
environment = @environment
|
|
482
|
+
state = environment.registry.agent_execution_state(request.execution_id)
|
|
485
483
|
unless state && state.agent.equal?(agent)
|
|
486
484
|
raise Phronomy::ExecutionRehydrationRequiredError,
|
|
487
485
|
"recovered execution disappeared before resolution apply"
|
|
488
486
|
end
|
|
489
487
|
unless state.execution.equal?(ready.operation.execution) && state.fsm_session_id.nil?
|
|
490
|
-
raise Phronomy::
|
|
488
|
+
raise Phronomy::Persistence::StateConflictError,
|
|
491
489
|
"Recovery execution changed before resolution apply"
|
|
492
490
|
end
|
|
493
491
|
raise ready.error if ready.error
|
|
494
492
|
|
|
495
493
|
result = ready.result
|
|
496
494
|
if result.execution
|
|
497
|
-
|
|
495
|
+
environment.registry.replace_agent_execution(request.execution_id, execution: result.execution)
|
|
498
496
|
end
|
|
499
497
|
raise result.error if result.error
|
|
500
498
|
|
|
@@ -7,7 +7,7 @@ module Phronomy
|
|
|
7
7
|
# Durable Agent Recovery owner/resolver.
|
|
8
8
|
#
|
|
9
9
|
# This class owns Agent-specific restart hydration and factual resolution.
|
|
10
|
-
#
|
|
10
|
+
# Agent recovery semantics remain in RecoveryRules.
|
|
11
11
|
# @api private
|
|
12
12
|
class RecoveryCoordinator
|
|
13
13
|
class ResolutionOutcomeUnknownError < Phronomy::Error
|
|
@@ -39,7 +39,7 @@ module Phronomy
|
|
|
39
39
|
)
|
|
40
40
|
ResolutionResult = Data.define(:execution)
|
|
41
41
|
ResolutionPreparation = Data.define(:execution, :material, :error)
|
|
42
|
-
RecoveryMaterial = Data.define(:projection, :messages, :assistant_message, :output, :usage)
|
|
42
|
+
RecoveryMaterial = Data.define(:projection, :messages, :assistant_message, :output, :usage, :cancellation_requested)
|
|
43
43
|
RecoveryPlan = Data.define(
|
|
44
44
|
:execution, :root, :manifest, :base_manifest,
|
|
45
45
|
:projection, :classification, :material
|
|
@@ -49,7 +49,7 @@ module Phronomy
|
|
|
49
49
|
|
|
50
50
|
def initialize(agent)
|
|
51
51
|
@agent = agent
|
|
52
|
-
@
|
|
52
|
+
@environment = agent.__execution_environment
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
def recover_on_load!
|
|
@@ -57,26 +57,33 @@ module Phronomy
|
|
|
57
57
|
Array(tx.executions.list_active(agent.agent_id))
|
|
58
58
|
end
|
|
59
59
|
if active.length > 1
|
|
60
|
-
raise Phronomy::
|
|
60
|
+
raise Phronomy::Persistence::StateConflictError,
|
|
61
61
|
"multiple active AgentExecutions exist for #{agent.agent_id}"
|
|
62
62
|
end
|
|
63
63
|
return agent if active.empty?
|
|
64
64
|
|
|
65
65
|
execution = active.first
|
|
66
66
|
unless execution.agent_id.to_s == agent.agent_id.to_s
|
|
67
|
-
raise Phronomy::
|
|
67
|
+
raise Phronomy::Persistence::StateConflictError,
|
|
68
68
|
"active AgentExecution belongs to another Agent: #{execution.agent_id}"
|
|
69
69
|
end
|
|
70
70
|
|
|
71
|
-
coordination = execution.metadata["
|
|
72
|
-
if coordination && agent.
|
|
71
|
+
coordination = execution.metadata["reservation"] || execution.metadata["execution_extension"]
|
|
72
|
+
if coordination && agent.__execution_wiring.empty?
|
|
73
73
|
return agent
|
|
74
74
|
end
|
|
75
75
|
|
|
76
|
+
if (raw = execution.metadata["execution_extension"])
|
|
77
|
+
extension = ExecutionExtensionState.from_h(raw)
|
|
78
|
+
binding = agent.__execution_wiring[:phronomy_execution_participant]&.binding
|
|
79
|
+
unless binding && binding.binding_key == extension.binding_key && binding.binding_version == extension.binding_version
|
|
80
|
+
raise Phronomy::ExecutionRehydrationRequiredError, "Execution #{execution.execution_id} needs its current participant binding"
|
|
81
|
+
end
|
|
82
|
+
end
|
|
76
83
|
plan = prepare_plan(execution)
|
|
77
84
|
classification = plan.classification
|
|
78
85
|
if classification.disposition ==
|
|
79
|
-
Phronomy::
|
|
86
|
+
Phronomy::Agent::RecoveryRules::RESOLUTION_REQUIRED &&
|
|
80
87
|
!agent.send(:_phronomy_event_listener)
|
|
81
88
|
raise Phronomy::ConfigurationError,
|
|
82
89
|
"Agent #{agent.agent_id.inspect} requires Recovery resolution; " \
|
|
@@ -114,21 +121,21 @@ module Phronomy
|
|
|
114
121
|
expected_execution_revision:,
|
|
115
122
|
subject:,
|
|
116
123
|
outcome:,
|
|
117
|
-
result: Phronomy::
|
|
118
|
-
error: Phronomy::
|
|
124
|
+
result: Phronomy::Agent::RecoveryRules::MISSING,
|
|
125
|
+
error: Phronomy::Agent::RecoveryRules::MISSING
|
|
119
126
|
)
|
|
120
127
|
agent.send(:__assert_live_agent!)
|
|
121
|
-
normalized_outcome = Phronomy::
|
|
122
|
-
result_present = !result.equal?(Phronomy::
|
|
123
|
-
error_present = !error.equal?(Phronomy::
|
|
124
|
-
Phronomy::
|
|
128
|
+
normalized_outcome = Phronomy::Agent::RecoveryRules.normalize_outcome(outcome)
|
|
129
|
+
result_present = !result.equal?(Phronomy::Agent::RecoveryRules::MISSING)
|
|
130
|
+
error_present = !error.equal?(Phronomy::Agent::RecoveryRules::MISSING)
|
|
131
|
+
Phronomy::Agent::RecoveryRules.validate_resolution_material!(
|
|
125
132
|
outcome: normalized_outcome,
|
|
126
133
|
result_present: result_present,
|
|
127
134
|
error_present: error_present
|
|
128
135
|
)
|
|
129
136
|
|
|
130
137
|
normalized_subject =
|
|
131
|
-
Phronomy::
|
|
138
|
+
Phronomy::Agent::RecoveryRules.normalize_subject(subject)
|
|
132
139
|
canonical_result = if result_present
|
|
133
140
|
if normalized_subject[:type] == :llm_call &&
|
|
134
141
|
normalized_outcome == :succeeded
|
|
@@ -189,7 +196,7 @@ module Phronomy
|
|
|
189
196
|
def post_control(command)
|
|
190
197
|
admission = command.is_a?(InstallCommand) || command.is_a?(ResolveCommand)
|
|
191
198
|
completion = admission ? command.completion : command.request.completion
|
|
192
|
-
|
|
199
|
+
@environment.registry.post(command,
|
|
193
200
|
admission: admission, completion: completion)
|
|
194
201
|
rescue Phronomy::RuntimeShutdownError
|
|
195
202
|
false
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Agent
|
|
5
|
+
# Agent recovery subjects, factual resolutions and resumption classifications.
|
|
6
|
+
# Persistence evidence comparison belongs to Persistence::SnapshotComparison.
|
|
7
|
+
# @api private
|
|
8
|
+
module RecoveryRules
|
|
9
|
+
RESUMABLE = :resumable
|
|
10
|
+
RECONCILABLE = :reconcilable
|
|
11
|
+
RESOLUTION_REQUIRED = :resolution_required
|
|
12
|
+
DISPOSITIONS = [RESUMABLE, RECONCILABLE, RESOLUTION_REQUIRED].freeze
|
|
13
|
+
|
|
14
|
+
ALLOWED_OUTCOMES = %i[succeeded failed not_performed].freeze
|
|
15
|
+
# Internal shorthand used by the Agent Recovery implementation. This is not
|
|
16
|
+
# a public API constant; both names refer to the same frozen value.
|
|
17
|
+
OUTCOMES = ALLOWED_OUTCOMES
|
|
18
|
+
|
|
19
|
+
MISSING = Object.new.freeze
|
|
20
|
+
|
|
21
|
+
Classification = Data.define(
|
|
22
|
+
:disposition, :reason, :subject, :allowed_outcomes, :facts
|
|
23
|
+
) do
|
|
24
|
+
def initialize(
|
|
25
|
+
disposition:,
|
|
26
|
+
reason:,
|
|
27
|
+
subject: nil,
|
|
28
|
+
allowed_outcomes: [],
|
|
29
|
+
facts: {}
|
|
30
|
+
)
|
|
31
|
+
normalized = disposition.to_sym
|
|
32
|
+
unless Phronomy::Agent::RecoveryRules::DISPOSITIONS.include?(normalized)
|
|
33
|
+
raise ArgumentError, "unknown Recovery disposition: #{disposition.inspect}"
|
|
34
|
+
end
|
|
35
|
+
super(
|
|
36
|
+
disposition: normalized,
|
|
37
|
+
reason: reason.to_sym,
|
|
38
|
+
subject: subject && Phronomy::Agent::RecoveryRules.normalize_subject(subject),
|
|
39
|
+
allowed_outcomes: Array(allowed_outcomes).map(&:to_sym).freeze,
|
|
40
|
+
facts: Phronomy::Values::Immutable.copy(facts || {})
|
|
41
|
+
)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
module_function
|
|
46
|
+
|
|
47
|
+
def normalize_outcome(outcome)
|
|
48
|
+
normalized = outcome.to_sym
|
|
49
|
+
unless OUTCOMES.include?(normalized)
|
|
50
|
+
raise ArgumentError, "unsupported Recovery outcome: #{outcome.inspect}"
|
|
51
|
+
end
|
|
52
|
+
normalized
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def validate_resolution_material!(outcome:, result_present:, error_present:)
|
|
56
|
+
normalized = normalize_outcome(outcome)
|
|
57
|
+
case normalized
|
|
58
|
+
when :succeeded
|
|
59
|
+
raise ArgumentError, "Recovery :succeeded requires result:" unless result_present
|
|
60
|
+
raise ArgumentError, "Recovery :succeeded forbids error:" if error_present
|
|
61
|
+
when :failed
|
|
62
|
+
raise ArgumentError, "Recovery :failed requires error:" unless error_present
|
|
63
|
+
raise ArgumentError, "Recovery :failed forbids result:" if result_present
|
|
64
|
+
when :not_performed
|
|
65
|
+
if result_present || error_present
|
|
66
|
+
raise ArgumentError, "Recovery :not_performed accepts neither result: nor error:"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
true
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def normalize_subject(subject)
|
|
73
|
+
hash = subject.to_h.transform_keys(&:to_sym)
|
|
74
|
+
type = hash.fetch(:type).to_sym
|
|
75
|
+
normalized = {type: type}
|
|
76
|
+
case type
|
|
77
|
+
when :llm_call
|
|
78
|
+
normalized[:llm_call_id] = required_id(hash, :llm_call_id)
|
|
79
|
+
when :tool_invocation
|
|
80
|
+
normalized[:tool_invocation_id] = required_id(hash, :tool_invocation_id)
|
|
81
|
+
when :persistence_operation
|
|
82
|
+
# Persistence operation references are structural only. Do not introduce
|
|
83
|
+
# a generic durable operation/recovery identity here.
|
|
84
|
+
normalized[:entity] = hash.fetch(:entity).to_sym
|
|
85
|
+
else
|
|
86
|
+
raise ArgumentError, "unsupported Recovery subject type: #{type.inspect}"
|
|
87
|
+
end
|
|
88
|
+
normalized.freeze
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def subject_key(subject)
|
|
92
|
+
normalized = normalize_subject(subject)
|
|
93
|
+
case normalized.fetch(:type)
|
|
94
|
+
when :llm_call
|
|
95
|
+
"llm_call:#{normalized.fetch(:llm_call_id)}"
|
|
96
|
+
when :tool_invocation
|
|
97
|
+
"tool_invocation:#{normalized.fetch(:tool_invocation_id)}"
|
|
98
|
+
when :persistence_operation
|
|
99
|
+
"persistence_operation:#{normalized.fetch(:entity)}"
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def subject_equal?(left, right)
|
|
104
|
+
normalize_subject(left) == normalize_subject(right)
|
|
105
|
+
rescue ArgumentError, KeyError
|
|
106
|
+
false
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def required_id(hash, key)
|
|
110
|
+
value = hash.fetch(key)
|
|
111
|
+
text = value.to_s
|
|
112
|
+
raise ArgumentError, "#{key} must not be empty" if text.empty?
|
|
113
|
+
|
|
114
|
+
text.freeze
|
|
115
|
+
end
|
|
116
|
+
private_class_method :required_id
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -15,17 +15,14 @@ module Phronomy
|
|
|
15
15
|
end
|
|
16
16
|
|
|
17
17
|
def normalize_provider_outcome(value)
|
|
18
|
-
return value if value.is_a?(Phronomy::
|
|
18
|
+
return value if value.is_a?(Phronomy::LLMAdapter::Response)
|
|
19
19
|
|
|
20
20
|
if value.is_a?(Hash)
|
|
21
|
-
return Phronomy::
|
|
21
|
+
return Phronomy::LLMAdapter::Response.from_h(value)
|
|
22
22
|
end
|
|
23
23
|
|
|
24
|
-
captured = Phronomy::Agent::ProviderCallOutcome.capture(value)
|
|
25
|
-
return captured if captured
|
|
26
|
-
|
|
27
24
|
raise ArgumentError,
|
|
28
|
-
"LLM Recovery :succeeded requires
|
|
25
|
+
"LLM Recovery :succeeded requires an LLMAdapter::Response or its canonical Hash"
|
|
29
26
|
end
|
|
30
27
|
|
|
31
28
|
def resolution_failure(error)
|
|
@@ -47,7 +44,7 @@ module Phronomy
|
|
|
47
44
|
execution_id: execution.execution_id,
|
|
48
45
|
execution_revision: execution.execution_revision,
|
|
49
46
|
reason: descriptor.fetch(:reason),
|
|
50
|
-
subject: Phronomy::
|
|
47
|
+
subject: Phronomy::Agent::RecoveryRules.normalize_subject(
|
|
51
48
|
descriptor.fetch(:subject)
|
|
52
49
|
),
|
|
53
50
|
allowed_outcomes: Array(
|
|
@@ -69,7 +66,7 @@ module Phronomy
|
|
|
69
66
|
type: :llm_call,
|
|
70
67
|
llm_call_id: llm_call_id
|
|
71
68
|
},
|
|
72
|
-
allowed_outcomes: Phronomy::
|
|
69
|
+
allowed_outcomes: Phronomy::Agent::RecoveryRules::OUTCOMES,
|
|
73
70
|
facts: {
|
|
74
71
|
manifest_ref: execution.metadata["manifest_ref"],
|
|
75
72
|
call_sequence: execution.llm_calls.length + 1
|
|
@@ -103,7 +100,7 @@ module Phronomy
|
|
|
103
100
|
allowed_outcomes: Array(
|
|
104
101
|
recovery["allowed_outcomes"] ||
|
|
105
102
|
recovery[:allowed_outcomes] ||
|
|
106
|
-
Phronomy::
|
|
103
|
+
Phronomy::Agent::RecoveryRules::OUTCOMES
|
|
107
104
|
).map(&:to_sym),
|
|
108
105
|
facts: {
|
|
109
106
|
tool_call_id: hash["tool_call_id"],
|
|
@@ -145,7 +142,7 @@ module Phronomy
|
|
|
145
142
|
type: :tool_invocation,
|
|
146
143
|
tool_invocation_id: first.fetch("tool_invocation_id")
|
|
147
144
|
},
|
|
148
|
-
allowed_outcomes: Phronomy::
|
|
145
|
+
allowed_outcomes: Phronomy::Agent::RecoveryRules::OUTCOMES,
|
|
149
146
|
facts: {
|
|
150
147
|
tool_call_id: first["tool_call_id"],
|
|
151
148
|
tool_name: first["tool_name"],
|
|
@@ -194,7 +191,7 @@ module Phronomy
|
|
|
194
191
|
end.freeze
|
|
195
192
|
end
|
|
196
193
|
|
|
197
|
-
def build_recovery_hash(subjects, reason: :outcome_unknown, allowed_outcomes: Phronomy::
|
|
194
|
+
def build_recovery_hash(subjects, reason: :outcome_unknown, allowed_outcomes: Phronomy::Agent::RecoveryRules::OUTCOMES)
|
|
198
195
|
{
|
|
199
196
|
"version" => ExecutionMetadata::CONTRACT_VERSION,
|
|
200
197
|
"reason" => reason.to_s,
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Agent
|
|
5
|
+
# Exact identity reserved by a coordinating domain. Correlation is immutable;
|
|
6
|
+
# mutable routing fences belong to the coordinator, never to this identity.
|
|
7
|
+
# @api public
|
|
8
|
+
ReservedExecution = Data.define(:agent_id, :execution_id, :correlation) do
|
|
9
|
+
def initialize(agent_id:, execution_id:, correlation: nil)
|
|
10
|
+
raise ArgumentError, "reserved identities must not be empty" if agent_id.to_s.empty? || execution_id.to_s.empty?
|
|
11
|
+
super(agent_id: agent_id.to_s.freeze, execution_id: execution_id.to_s.freeze,
|
|
12
|
+
correlation: Phronomy::Values::Immutable.copy(correlation))
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Agent
|
|
5
|
+
# A durable reference prevents whole-Agent purge. The referencing domain
|
|
6
|
+
# decides when its opaque owner key can be released.
|
|
7
|
+
# @api public
|
|
8
|
+
Retention = Data.define(:agent_id, :execution_id, :owner_key) do
|
|
9
|
+
def initialize(agent_id:, owner_key:, execution_id: nil)
|
|
10
|
+
raise ArgumentError, "retention identities must not be empty" if agent_id.to_s.empty? || owner_key.to_s.empty?
|
|
11
|
+
super(agent_id: agent_id.to_s.freeze, execution_id: execution_id&.to_s&.freeze, owner_key: owner_key.to_s.freeze)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def to_h = {"agent_id" => agent_id, "execution_id" => execution_id, "owner_key" => owner_key}.freeze
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|