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
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# VectorStore and Embeddings: asynchronous client migration
|
|
2
|
+
|
|
3
|
+
This unreleased P3 change moves the Beta asynchronous API to explicit clients.
|
|
4
|
+
It changes the receiver used by callers. Backend authors continue to implement
|
|
5
|
+
only synchronous methods; method arguments and results remain unchanged.
|
|
6
|
+
|
|
7
|
+
| Previous receiver | New receiver |
|
|
8
|
+
|---|---|
|
|
9
|
+
| `store.add_async(...)` | `async_store.add_async(...)` |
|
|
10
|
+
| `store.search_async(...)` | `async_store.search_async(...)` |
|
|
11
|
+
| `store.remove_async(...)` | `async_store.remove_async(...)` |
|
|
12
|
+
| `store.clear_async(...)` | `async_store.clear_async(...)` |
|
|
13
|
+
| `embeddings.embed_async(text, token, timeout: seconds)` | `async_embeddings.embed_async(text, token, timeout: seconds)` |
|
|
14
|
+
|
|
15
|
+
Construct the clients with `VectorStore::AsyncClient.new(backend: store)` and
|
|
16
|
+
`VectorStore::Embeddings::AsyncClient.new(adapter: embeddings)`. The embedding
|
|
17
|
+
cancellation token stays positional. Synchronous `add`, `search`, `remove`,
|
|
18
|
+
`clear`, `size` and `embed` stay on the existing backend objects.
|
|
19
|
+
|
|
20
|
+
`VectorStore::AsyncBackend` and inherited async methods are removed. There is
|
|
21
|
+
no include/prepend, backend `.async` factory, or automatic compatibility shim.
|
|
22
|
+
Update the receiver wherever an application previously called an async method.
|
|
23
|
+
The clients are public Beta API; their `async/` directories do not introduce
|
|
24
|
+
`Phronomy::Async` or feature-specific `Async` namespaces.
|
|
25
|
+
|
|
26
|
+
## Complete offline example
|
|
27
|
+
|
|
28
|
+
This example uses an intentionally simple local embedding adapter to demonstrate
|
|
29
|
+
the API. It needs no provider key, network request, or database server. The
|
|
30
|
+
implementation supplies only `embed`; Phronomy supplies asynchronous execution.
|
|
31
|
+
Run it against a checkout containing P3, not the previously released 0.27.0 gem.
|
|
32
|
+
|
|
33
|
+
```ruby runnable
|
|
34
|
+
require "phronomy"
|
|
35
|
+
require "json"
|
|
36
|
+
|
|
37
|
+
class KeywordEmbeddings < Phronomy::Embeddings::Base
|
|
38
|
+
protected
|
|
39
|
+
|
|
40
|
+
def perform_embed(text, cancellation_token = nil)
|
|
41
|
+
cancellation_token&.raise_if_cancelled!
|
|
42
|
+
words = text.downcase
|
|
43
|
+
[words.include?("ruby") ? 1.0 : 0.0,
|
|
44
|
+
words.include?("python") ? 1.0 : 0.0]
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
begin
|
|
49
|
+
store = Phronomy::VectorStore::InMemory.new(dimension: 2)
|
|
50
|
+
embeddings = KeywordEmbeddings.new
|
|
51
|
+
async_store = Phronomy::VectorStore::AsyncClient.new(backend: store)
|
|
52
|
+
async_embeddings = Phronomy::Embeddings::AsyncClient.new(adapter: embeddings)
|
|
53
|
+
|
|
54
|
+
{"ruby" => "Ruby guide", "python" => "Python guide"}.each do |id, title|
|
|
55
|
+
async_embeddings.embed_async(title).flat_map do |vector|
|
|
56
|
+
async_store.add_async(id: id, embedding: vector, metadata: {title: title})
|
|
57
|
+
end.wait_result
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
matches = async_embeddings.embed_async("Ruby").flat_map do |vector|
|
|
61
|
+
async_store.search_async(query_embedding: vector, k: 1)
|
|
62
|
+
end.wait_result
|
|
63
|
+
puts JSON.generate(matches)
|
|
64
|
+
ensure
|
|
65
|
+
Phronomy.reset_runtime!
|
|
66
|
+
end
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The result contains the `ruby` document. The application waits only outside
|
|
70
|
+
worker callbacks; `flat_map` returns the next TaskResult without blocking a
|
|
71
|
+
worker on another operation. `Tools::VectorSearch` continues to call synchronous
|
|
72
|
+
`embed` and `search` inside its existing execution boundary, so this migration
|
|
73
|
+
does not add nested offload work to that Tool.
|
|
74
|
+
|
|
75
|
+
## Execution and cancellation
|
|
76
|
+
|
|
77
|
+
Both clients accept optional `pool:` injection. Usually omit it. Construction
|
|
78
|
+
starts no Runtime, and each operation resolves the current default pool. An
|
|
79
|
+
explicit pool remains caller-owned across Runtime resets; it must provide the
|
|
80
|
+
existing OffloadPool submit contract, including its timer service for timeouts.
|
|
81
|
+
|
|
82
|
+
Queue admission uses `on_full: :raise`. A full or stopped pool raises immediately;
|
|
83
|
+
an accepted operation returns the original TaskResult. Timeout includes queue
|
|
84
|
+
wait. Cancellation or timeout before worker pickup skips the operation; after
|
|
85
|
+
execution starts it settles the result without forcibly stopping I/O or undoing
|
|
86
|
+
backend effects. These are the existing OffloadPool semantics.
|
|
87
|
+
|
|
88
|
+
Synchronous RBS signatures accept the structural `_CancellationSignal`
|
|
89
|
+
interface, which requires only `raise_if_cancelled!`. Existing
|
|
90
|
+
`Concurrency::CancellationToken` objects still satisfy it. Async RBS signatures
|
|
91
|
+
live in `sig/phronomy/vector_store/async/async_client.rbs` and
|
|
92
|
+
`sig/phronomy/embeddings/async/async_client.rbs` and retain the full CancellationToken
|
|
93
|
+
and TaskResult contract needed by the execution pool. Do not substitute a
|
|
94
|
+
minimal synchronous signal for an async operation's full token.
|
|
95
|
+
|
|
96
|
+
## Source ownership
|
|
97
|
+
|
|
98
|
+
The public class names below are unchanged. Their implementation files move:
|
|
99
|
+
|
|
100
|
+
| Public class | New path under `lib/phronomy/` |
|
|
101
|
+
|---|---|
|
|
102
|
+
| `VectorStore::InMemory` | `vector_store/backends/in_memory.rb` |
|
|
103
|
+
| `VectorStore::Pgvector` | `vector_store/backends/pgvector.rb` |
|
|
104
|
+
| `VectorStore::RedisSearch` | `vector_store/backends/redis_search.rb` |
|
|
105
|
+
| `VectorStore::Embeddings::RubyLLMEmbeddings` | `embeddings/backends/ruby_llm_embeddings.rb` |
|
|
106
|
+
|
|
107
|
+
Use `require "phronomy"` and the public constants. Direct partial-load paths are
|
|
108
|
+
not the public loading contract; no require shim is kept in the contract
|
|
109
|
+
directories. Backend Contracts have no execution or implementation dependency.
|
|
110
|
+
P4 Storage/ContentStore work remains pending.
|
|
@@ -2,8 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
[ADR-058](decisions/058-neutral-storage-primitives.md) defines the current
|
|
4
4
|
extension contract. This is an intentional breaking replacement of the old
|
|
5
|
-
fixed-repository Backend SPI. Application
|
|
6
|
-
`
|
|
5
|
+
fixed-repository Backend SPI. Application construction now uses
|
|
6
|
+
`PersistenceComposition.build(backend:)` or `.in_memory`; pass its `agent`, `team`,
|
|
7
|
+
or `workflow` component to that domain. `Persistence.new(backend:)` provides only
|
|
8
|
+
the neutral coordinator. The old eight-repository facade and its queries were
|
|
9
|
+
removed; see [r8 unit 3](architecture/r8-unit3.md).
|
|
7
10
|
|
|
8
11
|
## Ownership and composition
|
|
9
12
|
|
|
@@ -13,9 +16,10 @@ record types, active-execution policy, content digest algorithm or watermark API
|
|
|
13
16
|
|
|
14
17
|
Agent, Team, Workflow and ContentStore declare their own resource schemas.
|
|
15
18
|
`PersistenceComposition::StorageSchema` gathers those declarations and
|
|
16
|
-
`PersistenceComposition
|
|
17
|
-
|
|
18
|
-
|
|
19
|
+
`PersistenceComposition` binds domain record adapters to one coordinator.
|
|
20
|
+
Team and Workflow schemas live in their existing `persistence/` implementation
|
|
21
|
+
directories. An isolated Agent uses `PersistenceComposition.agent` and does not
|
|
22
|
+
construct Team or Workflow stores.
|
|
19
23
|
`ContentStore::StoredContents` owns SHA-256 identity and digest verification.
|
|
20
24
|
The Agent-owned `Watermark` composes guarded revision and stream-head conditions.
|
|
21
25
|
|
|
@@ -41,11 +45,10 @@ view.check!(guards: guards, conditions: conditions)
|
|
|
41
45
|
|
|
42
46
|
The backend declares `spi_version: 2` and true values for `atomic_resources`,
|
|
43
47
|
`record_cas`, `stream_cas`, `conditional_unique`, `guarded_checks` and
|
|
44
|
-
`nested_savepoints`. Persistence validates
|
|
45
|
-
resource declarations before
|
|
46
|
-
|
|
47
|
-
adapter
|
|
48
|
-
and `optimistic_revision`, derived by composition from these primitives.
|
|
48
|
+
`nested_savepoints`. Persistence validates backend capabilities; composition
|
|
49
|
+
validates the required domain resource declarations before binding adapters.
|
|
50
|
+
Unsupported backends raise `Persistence::UnsupportedBackendError`. There is no
|
|
51
|
+
eight-slot compatibility adapter or duplicated facade capability API.
|
|
49
52
|
|
|
50
53
|
A root handle routes to the current transaction on the same backend/thread.
|
|
51
54
|
A bound view and all its handles expire on commit or rollback and reject use from
|
|
@@ -139,12 +142,24 @@ stale revisions and ordinary constraint conflicts remain `ConflictError`.
|
|
|
139
142
|
meanings; `TransactionError` identifies invalid scope use. The removed
|
|
140
143
|
`ActiveExecutionConflictError` and `Storage::Repositories` have no aliases.
|
|
141
144
|
|
|
142
|
-
The following sections describe the
|
|
145
|
+
The following sections describe the **domain-owned record protocols**, used by
|
|
146
|
+
domain implementations and backend conformance tests. They are not a shared
|
|
147
|
+
application facade. Unqualified
|
|
148
|
+
ConflictError, NotFoundError, SerializationError and UnsupportedBackendError in
|
|
149
|
+
these domain sections refer to `Phronomy::Persistence` errors. Repository exits
|
|
150
|
+
translate the four raw Storage categories, preserving `cause`, message and trace.
|
|
151
|
+
Backend implementation code continues to raise `Phronomy::Storage` errors as
|
|
152
|
+
specified above. Unknown/transport errors and Storage::TransactionError are not
|
|
153
|
+
translated. See [ADR-061](decisions/061-persistence-failure-contracts.md) and the
|
|
154
|
+
[application migration](migrations/persistence-failure-contracts.md).
|
|
143
155
|
|
|
144
156
|
## Contents repository
|
|
145
157
|
|
|
146
|
-
`ContentStore::StoredContents < ContentStore::Base` supplies
|
|
147
|
-
|
|
158
|
+
`ContentStore::StoredContents < ContentStore::Base` supplies text/JSON helpers
|
|
159
|
+
and canonical content-ID calculation over neutral Blobs. The private
|
|
160
|
+
Persistence::ContentRepository adapts its primitive methods to the upper failure
|
|
161
|
+
contract when accessed through `persistence.contents`. Direct StoredContents
|
|
162
|
+
callers retain the raw Storage error surface.
|
|
148
163
|
|
|
149
164
|
Required primitive surface:
|
|
150
165
|
|
|
@@ -159,7 +174,7 @@ Required semantics:
|
|
|
159
174
|
- content is immutable and content-addressed;
|
|
160
175
|
- writing identical bytes is idempotent and returns the same content ID;
|
|
161
176
|
- `fetch` returns a binary `String` isolated from caller mutation;
|
|
162
|
-
- a missing content ID raises `
|
|
177
|
+
- a missing content ID through `persistence.contents` raises `Persistence::NotFoundError`;
|
|
163
178
|
- one content ID must never resolve to different bytes; a digest-integrity
|
|
164
179
|
violation raises `ContentStore::IntegrityError`.
|
|
165
180
|
|
|
@@ -361,8 +376,9 @@ Array of supported values
|
|
|
361
376
|
Hash with String/Symbol keys and supported values
|
|
362
377
|
```
|
|
363
378
|
|
|
364
|
-
If a value cannot be represented,
|
|
365
|
-
|
|
379
|
+
If a value cannot be represented, the backend/codec raises
|
|
380
|
+
`Storage::SerializationError`; the domain repository exposes
|
|
381
|
+
`Persistence::SerializationError`. Do not silently convert it into a lossy form. JSON backends may return String
|
|
366
382
|
keys after decoding; `WorkflowRunner` deliberately accepts String and Symbol keys
|
|
367
383
|
and normalizes them when comparing durable snapshots.
|
|
368
384
|
|
|
@@ -391,16 +407,17 @@ differs, raise `ConflictError`. On success return `true`.
|
|
|
391
407
|
The operation must not return a replacement AgentRoot or Journal. A mismatch is a
|
|
392
408
|
conflict, not a request to reload/merge mutable state.
|
|
393
409
|
|
|
394
|
-
When used inside `
|
|
410
|
+
When used inside `Agent::Store#transaction`, a SQL backend should perform the
|
|
395
411
|
watermark check in the same database transaction as the subsequent durable
|
|
396
412
|
write.
|
|
397
413
|
|
|
398
414
|
## Transaction contract
|
|
399
415
|
|
|
400
|
-
A transaction
|
|
416
|
+
A domain transaction combines its own record operations. Multiple domains join
|
|
417
|
+
one coordinator scope explicitly; each receives only its own record protocol:
|
|
401
418
|
|
|
402
419
|
```ruby
|
|
403
|
-
|
|
420
|
+
stores.agent.transaction do |tx|
|
|
404
421
|
tx.assert_agent_watermark!(...)
|
|
405
422
|
content_ref = tx.contents.put_text("...")
|
|
406
423
|
tx.journals.append(...)
|
|
@@ -493,7 +510,7 @@ require "phronomy"
|
|
|
493
510
|
require "phronomy/testing/persistence_contract"
|
|
494
511
|
|
|
495
512
|
RSpec.describe MyPersistenceBackend do
|
|
496
|
-
let(:
|
|
513
|
+
let(:stores) { Phronomy::PersistenceComposition.build(backend: described_class.new(...)) }
|
|
497
514
|
|
|
498
515
|
it_behaves_like "a persistence content store"
|
|
499
516
|
it_behaves_like "an Agent repository"
|
|
@@ -507,7 +524,7 @@ RSpec.describe MyPersistenceBackend do
|
|
|
507
524
|
end
|
|
508
525
|
```
|
|
509
526
|
|
|
510
|
-
`
|
|
527
|
+
`PersistenceComposition.in_memory` is run through the same shipped contract source in
|
|
511
528
|
Phronomy CI. The files under `spec/support/shared_examples/` are compatibility
|
|
512
529
|
require wrappers only; the authoritative shared-example implementations live
|
|
513
530
|
under `lib/phronomy/testing/persistence_contract/` so the core suite and external
|
|
@@ -292,7 +292,7 @@ remain durable conflict defense rather than distributed ownership.
|
|
|
292
292
|
## Tool execution modes
|
|
293
293
|
|
|
294
294
|
The default dispatch helper is the private
|
|
295
|
-
`Phronomy::
|
|
295
|
+
`Phronomy::Tool::ToolExecutor`, colocated with Capability
|
|
296
296
|
Base. Agent ToolInvocation supplies Runtime and admission policy for the
|
|
297
297
|
standard path, and owns authorization and logical result handling. Custom
|
|
298
298
|
`call_async` implementations keep the public Tool protocol. This ownership is
|
|
@@ -577,10 +577,35 @@ TaskResult reports only caller-facing settlement.
|
|
|
577
577
|
## EventLoop metrics
|
|
578
578
|
|
|
579
579
|
`Phronomy::Metrics.snapshot` also reports EventLoop queue depth and lag values.
|
|
580
|
+
|
|
581
|
+
Metrics and Diagnostics only observe resources already owned by the default
|
|
582
|
+
Runtime. Calling `Metrics.snapshot`, `Diagnostics.snapshot` or `Diagnostics.dump`
|
|
583
|
+
does not initialize a Runtime, pool, EventLoop or timer. Missing resources report
|
|
584
|
+
zero values, including `offload_pool_size: 0` when no default pool exists. Once a
|
|
585
|
+
pool exists, this field remains its configured capacity, not a live-worker count.
|
|
586
|
+
Named pools are not aggregated into the default-pool metrics.
|
|
587
|
+
|
|
588
|
+
Retained resources remain observable during and after shutdown, including
|
|
589
|
+
incomplete cleanup. Observation does not reopen admission or change a shutdown
|
|
590
|
+
result. After the default Runtime is reset, missing resources again report zero;
|
|
591
|
+
the next snapshot uses any newly initialized default Runtime. Counters are sampled
|
|
592
|
+
independently and do not constitute an atomic cross-resource snapshot.
|
|
580
593
|
Use these to distinguish worker saturation from EventLoop backlog/latency.
|
|
581
594
|
|
|
582
595
|
## Shutdown
|
|
583
596
|
|
|
597
|
+
Pool creation and registration close atomically before the shutdown resource set
|
|
598
|
+
is captured. Accepted continuations can still acquire resources during Runtime's
|
|
599
|
+
draining phase. Once resource shutdown begins, all pools close admission before
|
|
600
|
+
any pool waits for its workers.
|
|
601
|
+
|
|
602
|
+
The stop-phase monotonic deadline is shared by EventLoop and every pool join.
|
|
603
|
+
A join timeout does not stop a synchronous worker. Runtime reports incomplete
|
|
604
|
+
cleanup while any worker remains, even if that worker's TaskResult has already
|
|
605
|
+
been cancelled or timed out. It does not forcefully interrupt worker threads.
|
|
606
|
+
See [R8 unit19](architecture/r8-unit19.md) and its
|
|
607
|
+
[migration notes](migrations/r8-unit19.md) for the shutdown budget and result rules.
|
|
608
|
+
|
|
584
609
|
`Runtime#shutdown` is terminal for that Runtime. It drains/terminates the
|
|
585
610
|
Runtime-owned EventLoop, then closes pools and timers according to the Runtime
|
|
586
611
|
shutdown contract.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "securerandom"
|
|
4
|
+
|
|
5
|
+
module Phronomy
|
|
6
|
+
module Agent
|
|
7
|
+
# The short durable phase of an Agent's already acquired runtime admission.
|
|
8
|
+
# A coordinator may join it to its reservation check; it cannot start an FSM,
|
|
9
|
+
# publish a provisional result or make the Agent interpret a parent's schema.
|
|
10
|
+
# @api public
|
|
11
|
+
class Admission
|
|
12
|
+
attr_reader :agent_id, :execution_id, :correlation
|
|
13
|
+
|
|
14
|
+
# Constructed by Agent execution, never restored from a durable record.
|
|
15
|
+
# @api private
|
|
16
|
+
def initialize(persistence:, root:, input:, config:, preparation_metadata:)
|
|
17
|
+
@persistence, @root, @input, @config = persistence, root, input, config
|
|
18
|
+
@preparation_metadata = preparation_metadata
|
|
19
|
+
@agent_id = root.agent_id
|
|
20
|
+
@execution_id = (config[:phronomy_reserved_execution_id] || SecureRandom.uuid).to_s.freeze
|
|
21
|
+
@correlation = Phronomy::Values::Immutable.copy(config[:phronomy_reservation])
|
|
22
|
+
@thread = Thread.current
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Own a standalone admission transaction.
|
|
26
|
+
# @api public
|
|
27
|
+
def accept
|
|
28
|
+
@persistence.coordinator.atomic { |scope| accept_in(scope) }
|
|
29
|
+
nil
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Participate without committing or publishing live state.
|
|
33
|
+
# @api public
|
|
34
|
+
def accept_in(scope)
|
|
35
|
+
check_thread!
|
|
36
|
+
raise Phronomy::ConfigurationError, "Agent admission is single-use" if @scope
|
|
37
|
+
@scope = scope
|
|
38
|
+
@persistence.participate(scope) do |records|
|
|
39
|
+
raise Phronomy::Error, "agent is closed: #{agent_id}" if @root.lifecycle_status == :closed
|
|
40
|
+
input_ref = records.contents.put_text(@input)
|
|
41
|
+
context_ref = records.contents.put_json(@config.fetch(:durable_context)) if @config.key?(:durable_context)
|
|
42
|
+
input_record = JournalRecord.new(agent_id: agent_id, kind: :input_received,
|
|
43
|
+
channel: :external, role: :user, content_ref: input_ref,
|
|
44
|
+
context_generation: @root.transcript_generation, context_candidate: false)
|
|
45
|
+
execution = AgentExecution.start(agent_root: @root, input_record: input_record,
|
|
46
|
+
execution_id: execution_id, metadata: {
|
|
47
|
+
"reservation" => correlation, "current_input_ref" => input_ref,
|
|
48
|
+
"durable_context_ref" => context_ref,
|
|
49
|
+
"execution_extension" => @config[:phronomy_execution_participant]&.binding&.to_h
|
|
50
|
+
}.merge(@preparation_metadata).compact)
|
|
51
|
+
input_record = JournalRecord.from_h(input_record.to_h.merge("execution_id" => execution_id))
|
|
52
|
+
execution = execution.with(execution_revision: 0, working_records: [input_record])
|
|
53
|
+
records.executions.create_active(execution)
|
|
54
|
+
next_root = @root.with(agent_revision: @root.agent_revision + 1, lifecycle_status: :active)
|
|
55
|
+
records.agents.save(agent_id, expected_revision: @root.agent_revision, root: next_root)
|
|
56
|
+
@result = [execution, next_root].freeze
|
|
57
|
+
end
|
|
58
|
+
nil
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Available to Agent execution only after the outermost successful response.
|
|
62
|
+
# @api private
|
|
63
|
+
def result
|
|
64
|
+
check_thread!
|
|
65
|
+
unless @result && @scope&.committed?
|
|
66
|
+
raise Phronomy::Persistence::TransactionError, "Agent admission has not committed"
|
|
67
|
+
end
|
|
68
|
+
@result
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
private
|
|
72
|
+
|
|
73
|
+
def check_thread!
|
|
74
|
+
raise Phronomy::Persistence::TransactionError, "Agent admission belongs to another execution context" unless @thread.equal?(Thread.current)
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
@@ -12,6 +12,35 @@ module Phronomy
|
|
|
12
12
|
# - approval notifications share the Agent listener as :approval_required;
|
|
13
13
|
# - Recovery resolution uses resolve/resolve_async.
|
|
14
14
|
module AsyncEventApi
|
|
15
|
+
def start_reserved_async(reservation:, input:, config: {})
|
|
16
|
+
unless reservation.is_a?(ReservedExecution) && reservation.agent_id == agent_id
|
|
17
|
+
raise ArgumentError, "reservation must belong to this Agent"
|
|
18
|
+
end
|
|
19
|
+
config = __invocation_config(_prepare_invocation_config(config, nil))
|
|
20
|
+
ExactExecution.start(agent: self, execution_id: reservation.execution_id, input: input,
|
|
21
|
+
config: config.merge(phronomy_reservation: reservation.correlation))
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def resume_async(execution_id, config: {})
|
|
25
|
+
config = __invocation_config(_prepare_invocation_config(config, nil))
|
|
26
|
+
ExactExecution.start(agent: self, execution_id: execution_id, input: nil, config: config, resume_only: true)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def resume(execution_id, config: {})
|
|
30
|
+
_check_event_loop_reentrancy(:resume, :resume_async)
|
|
31
|
+
resume_async(execution_id, config: config).wait_result
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def cancel_async(execution_id)
|
|
35
|
+
__assert_live_agent!
|
|
36
|
+
environment = __execution_environment
|
|
37
|
+
environment.submit(on_full: :raise) do
|
|
38
|
+
requested = persistence.request_cancellation(agent_id: agent_id, execution_id: execution_id)
|
|
39
|
+
ExecutionCancellation.signal(execution_id, agent_id, environment: environment) if requested
|
|
40
|
+
persistence.observe_execution(agent_id: agent_id, execution_id: execution_id)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
15
44
|
def invoke(
|
|
16
45
|
input,
|
|
17
46
|
config: {},
|
|
@@ -96,7 +125,7 @@ module Phronomy
|
|
|
96
125
|
|
|
97
126
|
def approve_async(execution_id, approval_request_id:, approved: true, config: {})
|
|
98
127
|
_reject_removed_generic_identity_keys!(config)
|
|
99
|
-
owner = Phronomy::Agent::ExecutionRegistry.
|
|
128
|
+
owner = Phronomy::Agent::ExecutionRegistry.existing_current&.agent_execution_owner(execution_id)
|
|
100
129
|
coordinator = if owner&.agent&.equal?(self)
|
|
101
130
|
owner.coordinator
|
|
102
131
|
else
|
|
@@ -115,8 +144,8 @@ module Phronomy
|
|
|
115
144
|
expected_execution_revision:,
|
|
116
145
|
subject:,
|
|
117
146
|
outcome:,
|
|
118
|
-
result: Phronomy::
|
|
119
|
-
error: Phronomy::
|
|
147
|
+
result: Phronomy::Agent::RecoveryRules::MISSING,
|
|
148
|
+
error: Phronomy::Agent::RecoveryRules::MISSING
|
|
120
149
|
)
|
|
121
150
|
_check_event_loop_reentrancy(:resolve, :resolve_async)
|
|
122
151
|
resolve_async(
|
|
@@ -134,8 +163,8 @@ module Phronomy
|
|
|
134
163
|
expected_execution_revision:,
|
|
135
164
|
subject:,
|
|
136
165
|
outcome:,
|
|
137
|
-
result: Phronomy::
|
|
138
|
-
error: Phronomy::
|
|
166
|
+
result: Phronomy::Agent::RecoveryRules::MISSING,
|
|
167
|
+
error: Phronomy::Agent::RecoveryRules::MISSING
|
|
139
168
|
)
|
|
140
169
|
Phronomy::Agent::RecoveryCoordinator.new(self).resolve(
|
|
141
170
|
execution_id,
|
|
@@ -149,26 +178,9 @@ module Phronomy
|
|
|
149
178
|
|
|
150
179
|
private
|
|
151
180
|
|
|
152
|
-
# Framework-private execution-local event routing. This is intentionally
|
|
153
|
-
# not a public compatibility path for invoke_async(..., on_event:).
|
|
154
|
-
def __invoke_async_with_event_sink(
|
|
155
|
-
input,
|
|
156
|
-
on_event:, config: {},
|
|
157
|
-
invocation_context: nil
|
|
158
|
-
)
|
|
159
|
-
raise ArgumentError, "on_event is required" unless on_event
|
|
160
|
-
|
|
161
|
-
config = _prepare_invocation_config(config, invocation_context)
|
|
162
|
-
_start_agent_operation(
|
|
163
|
-
input,
|
|
164
|
-
config: config,
|
|
165
|
-
mode: :invoke,
|
|
166
|
-
listener: on_event
|
|
167
|
-
)
|
|
168
|
-
end
|
|
169
|
-
|
|
170
181
|
def _start_agent_operation(input, config:, mode:, listener:)
|
|
171
|
-
config = _snapshot_durable_context(config)
|
|
182
|
+
config = __invocation_config(_snapshot_durable_context(config))
|
|
183
|
+
@_phronomy_execution_wiring = config.dup.freeze
|
|
172
184
|
approval = _approval_configuration_snapshot(nil)
|
|
173
185
|
execution_coordinator_for(config).start(
|
|
174
186
|
input,
|
|
@@ -220,14 +232,7 @@ module Phronomy
|
|
|
220
232
|
@execution_coordinator ||= Agent::ExecutionCoordinator.new(self)
|
|
221
233
|
end
|
|
222
234
|
|
|
223
|
-
def execution_coordinator_for(
|
|
224
|
-
multi_agent = config.key?(:phronomy_handoff_bindings) ||
|
|
225
|
-
config.key?(:phronomy_handoff_context)
|
|
226
|
-
return execution_coordinator unless multi_agent
|
|
227
|
-
|
|
228
|
-
@multi_agent_execution_coordinator ||=
|
|
229
|
-
Phronomy::Agent::HandoffExecutionCoordinator.new(self)
|
|
230
|
-
end
|
|
235
|
+
def execution_coordinator_for(_config) = execution_coordinator
|
|
231
236
|
end
|
|
232
237
|
end
|
|
233
238
|
end
|