phronomy 0.26.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.mutant.yml +9 -9
- data/CHANGELOG.md +92 -0
- data/CONTRIBUTING.md +8 -8
- data/README.md +13 -3
- data/VERIFY.sh +27 -27
- data/benchmark/bench_agent_invoke.rb +26 -47
- data/benchmark/bench_context_assembler.rb +17 -15
- data/benchmark/bench_regression.rb +1 -1
- data/benchmark/bench_tool_schema.rb +1 -1
- data/docs/architecture/agent-chat-and-state-ownership.md +153 -0
- data/docs/architecture/agent-configuration-and-tool-binding.md +139 -0
- data/docs/architecture/agent-context.md +4 -2
- data/docs/architecture/agent-transition-ownership.md +13 -0
- data/docs/architecture/backend-groups.md +152 -0
- data/docs/architecture/before-llm-input.md +9 -3
- data/docs/architecture/context-management.md +31 -1
- data/docs/architecture/context-preparation-steps.md +90 -0
- data/docs/architecture/entry-action-and-team-wording.md +72 -0
- data/docs/architecture/execution-abstraction-review.md +88 -0
- data/docs/architecture/execution-boundaries.md +28 -0
- data/docs/architecture/execution-metadata-and-values.md +86 -0
- data/docs/architecture/generator-verifier-ownership.md +111 -0
- data/docs/architecture/multi-agent-handoff.md +11 -4
- data/docs/architecture/persistence-refactoring-plan.md +50 -0
- data/docs/architecture/persistence.md +45 -4
- data/docs/architecture/r8-unit1.md +104 -0
- data/docs/architecture/r8-unit10.md +72 -0
- data/docs/architecture/r8-unit11.md +71 -0
- data/docs/architecture/r8-unit12.md +67 -0
- data/docs/architecture/r8-unit13.md +61 -0
- data/docs/architecture/r8-unit14.md +72 -0
- data/docs/architecture/r8-unit15.md +65 -0
- data/docs/architecture/r8-unit16.md +54 -0
- data/docs/architecture/r8-unit17.md +73 -0
- data/docs/architecture/r8-unit18.md +92 -0
- data/docs/architecture/r8-unit19.md +61 -0
- data/docs/architecture/r8-unit2.md +55 -0
- data/docs/architecture/r8-unit20.md +51 -0
- data/docs/architecture/r8-unit21.md +79 -0
- data/docs/architecture/r8-unit22.md +74 -0
- data/docs/architecture/r8-unit23.md +81 -0
- data/docs/architecture/r8-unit3.md +130 -0
- data/docs/architecture/r8-unit4.md +79 -0
- data/docs/architecture/r8-unit5.md +115 -0
- data/docs/architecture/r8-unit6.md +71 -0
- data/docs/architecture/r8-unit7.md +70 -0
- data/docs/architecture/r8-unit8.md +78 -0
- data/docs/architecture/r8-unit9.md +73 -0
- data/docs/architecture/refactoring-closure.md +343 -0
- data/docs/architecture/remaining-refactoring-plan.md +390 -0
- data/docs/architecture/rubyllm-2-token-ownership.md +82 -0
- data/docs/architecture/storage-execution-boundaries.md +54 -0
- data/docs/architecture/tool-schema-recording-gap.md +56 -0
- data/docs/architecture/tracing.md +2 -2
- data/docs/architecture/workflow-terminal-ownership-design.md +104 -0
- data/docs/architecture.md +222 -0
- data/docs/decisions/014-unified-persistence-durable-state.md +11 -0
- data/docs/decisions/015-tool-public-facade-and-rbs-boundary.md +3 -3
- data/docs/decisions/024-event-loop-single-writer-agent-runtime.md +5 -1
- data/docs/decisions/025-process-local-agent-ownership-and-runtime-admission.md +13 -3
- data/docs/decisions/026-workflow-runtime-admission-and-durable-terminal-barrier.md +5 -1
- data/docs/decisions/027-llm-adapter-provider-boundary.md +2 -0
- data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +7 -0
- data/docs/decisions/031-durable-multi-agent-coordination.md +7 -0
- data/docs/decisions/032-storage-backend-composition.md +85 -0
- data/docs/decisions/033-domain-persistence-ownership.md +79 -0
- data/docs/decisions/034-handoff-runner-coordination-ownership.md +78 -0
- data/docs/decisions/035-tool-executor-capability-ownership.md +68 -0
- data/docs/decisions/036-context-contract-ownership.md +86 -0
- data/docs/decisions/037-common-definition-ownership.md +62 -0
- data/docs/decisions/038-responsibility-based-source-layout.md +145 -0
- data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +82 -0
- data/docs/decisions/040-configuration-default-composition.md +105 -0
- data/docs/decisions/041-feature-owned-identity-registries.md +100 -0
- data/docs/decisions/042-feature-owned-execution-state.md +106 -0
- data/docs/decisions/043-storage-execution-constraint-notifications.md +75 -0
- data/docs/decisions/044-agent-default-and-one-shot-composition.md +106 -0
- data/docs/decisions/045-worker-input-restriction-ownership.md +104 -0
- data/docs/decisions/046-agent-responsibility-layout-and-shared-records.md +110 -0
- data/docs/decisions/047-recovered-execution-continuation-contract.md +116 -0
- data/docs/decisions/048-dispatch-preparation-worker-ownership.md +120 -0
- data/docs/decisions/049-initial-preparation-worker-ownership.md +111 -0
- data/docs/decisions/050-approval-resume-snapshot-and-commit-ownership.md +98 -0
- data/docs/decisions/051-execution-outcome-worker-ownership.md +153 -0
- data/docs/decisions/052-tool-invocation-restoration-ownership.md +92 -0
- data/docs/decisions/053-shared-state-coordination-ownership.md +71 -0
- data/docs/decisions/054-workflow-terminal-save-single-owner.md +76 -0
- data/docs/decisions/055-terminal-observer-failure-settlement.md +60 -0
- data/docs/decisions/056-workflow-terminal-policy-ownership.md +87 -0
- data/docs/decisions/057-storage-transaction-boundaries.md +73 -0
- data/docs/decisions/058-neutral-storage-primitives.md +83 -0
- data/docs/decisions/059-backend-contracts-and-async-clients.md +74 -0
- data/docs/decisions/060-runtime-settings-and-application-configuration.md +86 -0
- data/docs/decisions/061-persistence-failure-contracts.md +89 -0
- data/docs/decisions/062-content-store-service.md +52 -0
- data/docs/decisions/063-execution-contracts-and-services.md +88 -0
- data/docs/decisions/064-agent-execution-and-multi-agent-responsibility.md +36 -0
- data/docs/decisions/065-vector-and-embeddings-common-operations.md +38 -0
- data/docs/decisions/066-llm-and-tool-operation-contracts.md +20 -0
- data/docs/decisions/067-agent-progress-and-engine-connection.md +29 -0
- data/docs/decisions/068-agent-tool-child-progress-and-tool-operation.md +36 -0
- data/docs/decisions/README.md +52 -6
- data/docs/design/durable-semantic-coordination/IMPLEMENTATION_DESIGN_V2.md +6 -0
- data/docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md +6 -0
- data/docs/execution-boundaries-preview.md +45 -0
- data/docs/features.md +16 -5
- data/docs/getting-started.md +11 -8
- data/docs/migrations/0.15.md +5 -0
- data/docs/migrations/0.22.md +2 -2
- data/docs/migrations/durable-semantic-coordination-v2.md +8 -0
- data/docs/migrations/handoff-runner-multi-agent.md +37 -0
- data/docs/migrations/neutral-storage-spi.md +43 -0
- data/docs/migrations/parallel-tool-chat-removal.md +49 -0
- data/docs/migrations/persistence-failure-contracts.md +67 -0
- data/docs/migrations/r8-unit10.md +36 -0
- data/docs/migrations/r8-unit11.md +33 -0
- data/docs/migrations/r8-unit12.md +31 -0
- data/docs/migrations/r8-unit13.md +36 -0
- data/docs/migrations/r8-unit14.md +31 -0
- data/docs/migrations/r8-unit15.md +31 -0
- data/docs/migrations/r8-unit16.md +26 -0
- data/docs/migrations/r8-unit17.md +28 -0
- data/docs/migrations/r8-unit18.md +29 -0
- data/docs/migrations/r8-unit19.md +28 -0
- data/docs/migrations/r8-unit20.md +24 -0
- data/docs/migrations/r8-unit21.md +28 -0
- data/docs/migrations/r8-unit22.md +29 -0
- data/docs/migrations/r8-unit23.md +20 -0
- data/docs/migrations/r8-unit4.md +67 -0
- data/docs/migrations/r8-unit5.md +75 -0
- data/docs/migrations/r8-unit6.md +109 -0
- data/docs/migrations/r8-unit7.md +31 -0
- data/docs/migrations/r8-unit8.md +33 -0
- data/docs/migrations/r8-unit9.md +28 -0
- data/docs/migrations/shared-state-multi-agent.md +44 -0
- data/docs/migrations/storage-async-client.md +103 -0
- data/docs/migrations/storage-backend-composition.md +117 -0
- data/docs/migrations/storage-transaction-boundaries.md +74 -0
- data/docs/migrations/vector-async-clients.md +110 -0
- data/docs/persistence-backends.md +190 -215
- data/docs/runtime-and-concurrency.md +142 -32
- data/lib/phronomy/agent/admission.rb +78 -0
- data/lib/phronomy/{agent_already_exists_error.rb → agent/agent_already_exists_error.rb} +2 -0
- data/lib/phronomy/{agent_busy_error.rb → agent/agent_busy_error.rb} +2 -0
- data/lib/phronomy/{agent_purged_error.rb → agent/agent_purged_error.rb} +2 -0
- data/lib/phronomy/agent/api/agent.rb +17 -0
- data/lib/phronomy/{execution_rehydration_required_error.rb → agent/api/execution_rehydration_required_error.rb} +2 -0
- data/lib/phronomy/agent/async_event_api.rb +38 -33
- data/lib/phronomy/agent/base.rb +106 -294
- data/lib/phronomy/agent/before_llm_input.rb +64 -0
- data/lib/phronomy/{agent.rb → agent/composition/run_once.rb} +4 -13
- data/lib/phronomy/agent/context_candidate_resolver.rb +2 -2
- data/lib/phronomy/agent/context_importer.rb +2 -2
- data/lib/phronomy/agent/{context_assembler.rb → context_preparation.rb} +157 -288
- data/lib/phronomy/agent/control_request.rb +21 -0
- data/lib/phronomy/agent/{agent_execution.rb → execution/agent_execution.rb} +8 -4
- data/lib/phronomy/agent/{agent_invocation.rb → execution/agent_invocation.rb} +32 -63
- data/lib/phronomy/agent/execution/approval_resume_commit.rb +108 -0
- data/lib/phronomy/agent/execution/dispatch_preparation.rb +195 -0
- data/lib/phronomy/agent/{exact_execution.rb → execution/exact_execution.rb} +43 -23
- data/lib/phronomy/agent/{execution_cancellation.rb → execution/execution_cancellation.rb} +8 -5
- data/lib/phronomy/agent/execution/execution_coordinator.rb +1744 -0
- data/lib/phronomy/agent/execution/execution_failure.rb +30 -0
- data/lib/phronomy/agent/execution/execution_metadata.rb +54 -0
- data/lib/phronomy/agent/execution/execution_outcome_committer.rb +288 -0
- data/lib/phronomy/agent/execution/execution_registry.rb +463 -0
- data/lib/phronomy/agent/execution/execution_session_runner.rb +113 -0
- data/lib/phronomy/agent/execution/initial_preparation.rb +323 -0
- data/lib/phronomy/agent/execution/invocation_actions.rb +368 -0
- data/lib/phronomy/agent/execution/invocation_transitions.rb +124 -0
- data/lib/phronomy/agent/execution/runtime_record_encoder.rb +210 -0
- data/lib/phronomy/agent/execution_change.rb +161 -0
- data/lib/phronomy/agent/execution_environment.rb +36 -0
- data/lib/phronomy/agent/execution_extension_state.rb +21 -0
- data/lib/phronomy/agent/execution_observation.rb +35 -0
- data/lib/phronomy/agent/filterable.rb +182 -0
- data/lib/phronomy/agent/{handoff_context.rb → handoff/transfer_context.rb} +6 -6
- data/lib/phronomy/agent/{handoff_projection.rb → handoff/transfer_projection.rb} +14 -33
- data/lib/phronomy/agent/handoff_error.rb +7 -0
- data/lib/phronomy/agent/{journal_projection.rb → journal/journal_projection.rb} +4 -0
- data/lib/phronomy/agent/{journal_record.rb → journal/journal_record.rb} +3 -3
- data/lib/phronomy/agent/{llm_call_record.rb → journal/llm_call_record.rb} +2 -2
- data/lib/phronomy/agent/knowledge_item.rb +13 -0
- data/lib/phronomy/agent/{agent_root.rb → lifecycle/agent_root.rb} +6 -2
- data/lib/phronomy/agent/lifecycle/default_persistence.rb +29 -0
- data/lib/phronomy/{engine/runtime/agent_ownership_registry.rb → agent/lifecycle/ownership_registry.rb} +24 -15
- data/lib/phronomy/agent/persistence/agent_repository.rb +69 -0
- data/lib/phronomy/agent/persistence/cancellation_repository.rb +51 -0
- data/lib/phronomy/agent/persistence/codec.rb +338 -0
- data/lib/phronomy/agent/persistence/execution_repository.rb +124 -0
- data/lib/phronomy/agent/persistence/journal_repository.rb +63 -0
- data/lib/phronomy/agent/persistence/records.rb +33 -0
- data/lib/phronomy/agent/persistence/retention_repository.rb +70 -0
- data/lib/phronomy/agent/persistence/storage_schema.rb +29 -0
- data/lib/phronomy/agent/persistence/watermark.rb +29 -0
- data/lib/phronomy/agent/recovery/invocation_restorer.rb +124 -0
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/continuation.rb +28 -51
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/installation.rb +50 -55
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/resolution.rb +38 -43
- data/lib/phronomy/agent/{recovery_coordinator.rb → recovery/recovery_coordinator.rb} +26 -23
- data/lib/phronomy/agent/recovery/recovery_rules.rb +119 -0
- data/lib/phronomy/agent/recovery/recovery_support.rb +224 -0
- data/lib/phronomy/agent/reserved_execution.rb +16 -0
- data/lib/phronomy/agent/retention.rb +17 -0
- data/lib/phronomy/agent/runtime_binding/engine_environment.rb +60 -0
- data/lib/phronomy/agent/runtime_binding/engine_session_builder.rb +34 -0
- data/lib/phronomy/agent/runtime_binding/phase_machine_builder.rb +64 -0
- data/lib/phronomy/agent/runtime_binding/tool_session_builder.rb +34 -0
- data/lib/phronomy/agent/{ruby_llm_materializer.rb → runtime_input.rb} +19 -39
- data/lib/phronomy/agent/saved_context_reader.rb +55 -0
- data/lib/phronomy/agent/settings.rb +47 -0
- data/lib/phronomy/agent/state_writer.rb +166 -0
- data/lib/phronomy/agent/store.rb +253 -0
- data/lib/phronomy/{stream_callback_error.rb → agent/stream_callback_error.rb} +2 -0
- data/lib/phronomy/agent/{approval_evaluation_request.rb → tool_execution/approval_evaluation_request.rb} +1 -12
- data/lib/phronomy/agent/{tool_approval_request.rb → tool_execution/tool_approval_request.rb} +1 -10
- data/lib/phronomy/agent/tool_execution/tool_binding.rb +30 -0
- data/lib/phronomy/agent/{tool_invocation.rb → tool_execution/tool_invocation.rb} +114 -248
- data/lib/phronomy/agent/tool_execution/tool_invocation_actions.rb +107 -0
- data/lib/phronomy/agent/tool_execution/tool_invocation_transitions.rb +70 -0
- data/lib/phronomy/common/configuration_error.rb +7 -0
- data/lib/phronomy/common/error.rb +5 -0
- data/lib/phronomy/{agent → common/values}/immutable.rb +9 -1
- data/lib/phronomy/common/values/serializable.rb +32 -0
- data/lib/phronomy/configuration/runtime_settings.rb +49 -0
- data/lib/phronomy/content_store/storage_schema.rb +12 -0
- data/lib/phronomy/content_store/stored_contents.rb +44 -0
- data/lib/phronomy/context/assembly.rb +202 -0
- data/lib/phronomy/{context_budget_exceeded_error.rb → context/budget_exceeded_error.rb} +5 -1
- data/lib/phronomy/context/candidate.rb +51 -0
- data/lib/phronomy/context/constraint.rb +47 -0
- data/lib/phronomy/{agent → context}/context_plan.rb +2 -2
- data/lib/phronomy/{agent → context}/context_plan_validator.rb +3 -3
- data/lib/phronomy/{agent → context}/context_policy.rb +1 -1
- data/lib/phronomy/{agent → context}/context_policy_input.rb +7 -7
- data/lib/phronomy/{agent → context}/context_policy_input_builder.rb +1 -1
- data/lib/phronomy/context/default_policy.rb +140 -0
- data/lib/phronomy/context/final_budget_validator.rb +33 -0
- data/lib/phronomy/{agent → context}/llm_input_build_context.rb +2 -2
- data/lib/phronomy/{agent → context}/llm_input_manifest.rb +21 -21
- data/lib/phronomy/{agent → context}/llm_input_patch.rb +3 -3
- data/lib/phronomy/context/manifest_error.rb +10 -0
- data/lib/phronomy/context/prompt_template.rb +98 -0
- data/lib/phronomy/{vector_store → documents}/loader/base.rb +1 -1
- data/lib/phronomy/{vector_store → documents}/loader/csv_loader.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/loader/markdown_loader.rb +3 -3
- data/lib/phronomy/{vector_store → documents}/loader/plain_text_loader.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/splitter/base.rb +1 -1
- data/lib/phronomy/{vector_store → documents}/splitter/fixed_size_splitter.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/splitter/recursive_splitter.rb +2 -2
- data/lib/phronomy/embeddings/async/async_client.rb +44 -0
- data/lib/phronomy/embeddings/backends/ruby_llm_embeddings.rb +48 -0
- data/lib/phronomy/embeddings/base.rb +43 -0
- data/lib/phronomy/embeddings/invalid_result_error.rb +11 -0
- data/lib/phronomy/embeddings/transport_error.rb +11 -0
- data/lib/phronomy/engine/concurrency/offload_pool.rb +39 -20
- data/lib/phronomy/engine/concurrency/pool_registry.rb +47 -12
- data/lib/phronomy/{diagnostics.rb → engine/diagnostics.rb} +1 -1
- data/lib/phronomy/engine/event_loop.rb +164 -630
- data/lib/phronomy/engine/execution_binding.rb +23 -0
- data/lib/phronomy/engine/execution_receiver_binding.rb +43 -0
- data/lib/phronomy/engine/fsm_protocol.rb +9 -0
- data/lib/phronomy/engine/fsm_session.rb +37 -30
- data/lib/phronomy/engine/metrics.rb +28 -0
- data/lib/phronomy/engine/runtime/timer_queue.rb +1 -1
- data/lib/phronomy/engine/runtime.rb +104 -95
- data/lib/phronomy/engine/terminal_decision.rb +7 -0
- data/lib/phronomy/execution/async_operation.rb +41 -0
- data/lib/phronomy/execution/backpressure_error.rb +7 -0
- data/lib/phronomy/{blocking.rb → execution/blocking.rb} +2 -2
- data/lib/phronomy/execution/cancellation_error.rb +7 -0
- data/lib/phronomy/{engine → execution}/concurrency/cancellation_token.rb +5 -1
- data/lib/phronomy/{engine → execution}/concurrency/deadline.rb +0 -17
- data/lib/phronomy/{engine → execution}/concurrency/physical_completion_task.rb +1 -1
- data/lib/phronomy/{engine → execution}/concurrency/result_collector.rb +1 -1
- data/lib/phronomy/{engine → execution}/concurrency/result_composition.rb +33 -12
- data/lib/phronomy/{engine/concurrency/subscriptions.rb → execution/concurrency/result_subscriptions.rb} +14 -18
- data/lib/phronomy/execution/concurrency/subscriptions.rb +25 -0
- data/lib/phronomy/execution/concurrency/worker_input_restricted.rb +14 -0
- data/lib/phronomy/execution/event_loop_reentrancy_error.rb +8 -0
- data/lib/phronomy/{execution.rb → execution/execution.rb} +57 -30
- data/lib/phronomy/{execution_cancellation_error.rb → execution/execution_cancellation_error.rb} +2 -0
- data/lib/phronomy/execution/execution_receiver.rb +78 -0
- data/lib/phronomy/execution/execution_scope_state.rb +35 -0
- data/lib/phronomy/{execution_timeout_error.rb → execution/execution_timeout_error.rb} +2 -0
- data/lib/phronomy/{invalid_async_entry_action_error.rb → execution/invalid_async_entry_action_error.rb} +2 -0
- data/lib/phronomy/{invalid_async_transition_action_error.rb → execution/invalid_async_transition_action_error.rb} +2 -0
- data/lib/phronomy/{invalid_async_workflow_action_error.rb → execution/invalid_async_workflow_action_error.rb} +2 -0
- data/lib/phronomy/{invocation_context.rb → execution/invocation_context.rb} +0 -15
- data/lib/phronomy/execution/invocation_controls.rb +73 -0
- data/lib/phronomy/execution/pool_shutdown_error.rb +7 -0
- data/lib/phronomy/execution/recursion_limit_error.rb +7 -0
- data/lib/phronomy/execution/runnable.rb +24 -0
- data/lib/phronomy/execution/runtime_shutdown_error.rb +7 -0
- data/lib/phronomy/execution/runtime_shutdown_reentrancy_error.rb +7 -0
- data/lib/phronomy/execution/scheduler_reentrancy_error.rb +9 -0
- data/lib/phronomy/{engine → execution}/task_result.rb +7 -3
- data/lib/phronomy/execution/timeout_error.rb +7 -0
- data/lib/phronomy/execution/wait_policy.rb +24 -0
- data/lib/phronomy/filter/filter_block_error.rb +14 -0
- data/lib/phronomy/generation/generator_verifier/agent_operation.rb +65 -0
- data/lib/phronomy/generation/generator_verifier/agent_result_receiver.rb +89 -0
- data/lib/phronomy/generation/generator_verifier/default_parser.rb +27 -0
- data/lib/phronomy/generation/generator_verifier/pipeline_state.rb +57 -0
- data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +112 -0
- data/lib/phronomy/generation/generator_verifier.rb +118 -0
- data/lib/phronomy/generation/low_confidence_error.rb +14 -0
- data/lib/phronomy/llm_adapter/async/async_client.rb +47 -0
- data/lib/phronomy/llm_adapter/authentication_error.rb +9 -0
- data/lib/phronomy/llm_adapter/backends/ruby_llm.rb +183 -0
- data/lib/phronomy/llm_adapter/base.rb +49 -68
- data/lib/phronomy/llm_adapter/context_length_error.rb +9 -0
- data/lib/phronomy/llm_adapter/invalid_result_error.rb +10 -0
- data/lib/phronomy/llm_adapter/message.rb +62 -0
- data/lib/phronomy/llm_adapter/rate_limit_error.rb +9 -0
- data/lib/phronomy/llm_adapter/request.rb +79 -0
- data/lib/phronomy/llm_adapter/response.rb +36 -0
- data/lib/phronomy/llm_adapter/stream_chunk.rb +18 -0
- data/lib/phronomy/llm_adapter/token_usage.rb +64 -0
- data/lib/phronomy/llm_adapter/transport_error.rb +9 -0
- data/lib/phronomy/llm_adapter.rb +3 -16
- data/lib/phronomy/llm_context_window/token_budget.rb +6 -7
- data/lib/phronomy/multi_agent/admission_registry.rb +15 -2
- data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +108 -84
- data/lib/phronomy/multi_agent/execution_environment.rb +29 -0
- data/lib/phronomy/{agent → multi_agent}/handoff.rb +2 -2
- data/lib/phronomy/{agent → multi_agent}/handoff_capability_factory.rb +21 -2
- data/lib/phronomy/multi_agent/handoff_participant.rb +79 -0
- data/lib/phronomy/{agent → multi_agent}/handoff_policy.rb +1 -1
- data/lib/phronomy/multi_agent/handoff_runner.rb +156 -0
- data/lib/phronomy/{agent → multi_agent}/handoff_state.rb +2 -2
- data/lib/phronomy/multi_agent/orchestrator.rb +61 -75
- data/lib/phronomy/multi_agent/persistence/codec.rb +75 -0
- data/lib/phronomy/multi_agent/persistence/handoff_state_repository.rb +77 -0
- data/lib/phronomy/multi_agent/persistence/records.rb +27 -0
- data/lib/phronomy/multi_agent/persistence/storage_schema.rb +21 -0
- data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +124 -0
- data/lib/phronomy/multi_agent/persistence/team_repository.rb +69 -0
- data/lib/phronomy/multi_agent/reserved_child_admission.rb +88 -0
- data/lib/phronomy/multi_agent/runtime_binding/engine_environment.rb +35 -0
- data/lib/phronomy/{agent → multi_agent}/shared_state.rb +56 -39
- data/lib/phronomy/multi_agent/store.rb +156 -0
- data/lib/phronomy/multi_agent/team_coordinator.rb +87 -75
- data/lib/phronomy/multi_agent/team_execution.rb +1 -1
- data/lib/phronomy/{engine/runtime → multi_agent}/team_ownership_registry.rb +5 -5
- data/lib/phronomy/multi_agent/team_root.rb +1 -1
- data/lib/phronomy/output_parser/parse_error.rb +7 -0
- data/lib/phronomy/persistence/content_repository.rb +26 -0
- data/lib/phronomy/persistence/errors.rb +16 -0
- data/lib/phronomy/persistence/migration/initial_format_migration.rb +21 -21
- data/lib/phronomy/persistence/persistence.rb +23 -0
- data/lib/phronomy/persistence/save_outcome.rb +24 -0
- data/lib/phronomy/persistence/snapshot_comparison.rb +81 -0
- data/lib/phronomy/persistence/storage_boundary.rb +32 -0
- data/lib/phronomy/persistence/transaction.rb +86 -0
- data/lib/phronomy/persistence_composition/storage_schema.rb +29 -0
- data/lib/phronomy/persistence_composition/stores.rb +34 -0
- data/lib/phronomy/persistence_composition/unit4_migration.rb +276 -0
- data/lib/phronomy/runtime_composition/agent_defaults.rb +11 -0
- data/lib/phronomy/runtime_composition/configuration.rb +146 -0
- data/lib/phronomy/runtime_composition/configuration_defaults.rb +9 -0
- data/lib/phronomy/runtime_composition/evaluation_defaults.rb +7 -0
- data/lib/phronomy/runtime_composition/execution_defaults.rb +5 -0
- data/lib/phronomy/runtime_composition/generation_defaults.rb +6 -0
- data/lib/phronomy/runtime_composition/global_configuration.rb +38 -0
- data/lib/phronomy/runtime_composition/global_runtime.rb +19 -0
- data/lib/phronomy/runtime_composition/multi_agent_defaults.rb +9 -0
- data/lib/phronomy/runtime_composition/workflow_defaults.rb +4 -0
- data/lib/phronomy/storage/async/async_client.rb +60 -0
- data/lib/phronomy/storage/backend.rb +107 -0
- data/lib/phronomy/storage/backends/in_memory.rb +157 -0
- data/lib/phronomy/storage/blob_conflict_error.rb +10 -0
- data/lib/phronomy/storage/blobs.rb +31 -0
- data/lib/phronomy/storage/condition.rb +25 -0
- data/lib/phronomy/storage/condition_failed_error.rb +16 -0
- data/lib/phronomy/storage/conflict_error.rb +9 -0
- data/lib/phronomy/{persistence → storage}/durable_record.rb +12 -12
- data/lib/phronomy/storage/entry.rb +33 -0
- data/lib/phronomy/storage/guard_ref.rb +13 -0
- data/lib/phronomy/storage/not_found_error.rb +9 -0
- data/lib/phronomy/storage/record_codec.rb +177 -0
- data/lib/phronomy/storage/records.rb +61 -0
- data/lib/phronomy/storage/resource.rb +126 -0
- data/lib/phronomy/storage/scope.rb +25 -0
- data/lib/phronomy/storage/serialization_error.rb +9 -0
- data/lib/phronomy/storage/streams.rb +44 -0
- data/lib/phronomy/storage/transaction_error.rb +10 -0
- data/lib/phronomy/storage/unique_constraint_error.rb +17 -0
- data/lib/phronomy/storage/unsupported_backend_error.rb +9 -0
- data/lib/phronomy/storage/validation.rb +53 -0
- data/lib/phronomy/storage/view.rb +131 -0
- data/lib/phronomy/testing/eval/metrics.rb +1 -1
- data/lib/phronomy/testing/eval/scorer/llm_judge.rb +19 -3
- data/lib/phronomy/testing/persistence_contract/a_content_store.rb +1 -1
- data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +2 -2
- data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +54 -48
- data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +1 -1
- data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +1 -1
- data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +5 -5
- data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +52 -46
- data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +263 -0
- data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +124 -0
- data/lib/phronomy/testing/persistence_contract.rb +4 -0
- data/lib/phronomy/tool/authorization.rb +77 -0
- data/lib/phronomy/tool/base.rb +549 -9
- data/lib/phronomy/tool/call_request.rb +40 -0
- data/lib/phronomy/{agent/tool_definition_set.rb → tool/definition_set.rb} +16 -12
- data/lib/phronomy/tool/operation.rb +88 -0
- data/lib/phronomy/tool/schema.rb +214 -0
- data/lib/phronomy/tool/settings.rb +31 -0
- data/lib/phronomy/tool/tool_error.rb +7 -0
- data/lib/phronomy/{agent → tool}/tool_executor.rb +10 -7
- data/lib/phronomy/tools/agent.rb +22 -90
- data/lib/phronomy/tools/mcp.rb +21 -14
- data/lib/phronomy/tools/vector_search.rb +4 -4
- data/lib/phronomy/tracing/automatic.rb +11 -6
- data/lib/phronomy/tracing/base.rb +1 -1
- data/lib/phronomy/tracing/observation.rb +37 -0
- data/lib/phronomy/tracing/settings.rb +36 -0
- data/lib/phronomy/vector_store/{async_backend.rb → async/async_client.rb} +35 -29
- data/lib/phronomy/vector_store/{in_memory.rb → backends/in_memory.rb} +12 -10
- data/lib/phronomy/vector_store/{pgvector.rb → backends/pgvector.rb} +44 -16
- data/lib/phronomy/vector_store/{redis_search.rb → backends/redis_search.rb} +74 -37
- data/lib/phronomy/vector_store/base.rb +118 -49
- data/lib/phronomy/vector_store/invalid_result_error.rb +12 -0
- data/lib/phronomy/vector_store.rb +2 -17
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/workflow/completion.rb +11 -0
- data/lib/phronomy/{workflow.rb → workflow/execution/workflow.rb} +10 -8
- data/lib/phronomy/workflow/execution/workflow_action_rules.rb +67 -0
- data/lib/phronomy/{workflow_context.rb → workflow/execution/workflow_context.rb} +5 -1
- data/lib/phronomy/workflow/execution/workflow_context_ownership_error.rb +7 -0
- data/lib/phronomy/workflow/execution/workflow_execution_environment.rb +27 -0
- data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +186 -0
- data/lib/phronomy/{workflow_runner.rb → workflow/execution/workflow_runner.rb} +145 -115
- data/lib/phronomy/workflow/execution/workflow_settings.rb +28 -0
- data/lib/phronomy/workflow/persistence/codec.rb +153 -0
- data/lib/phronomy/workflow/persistence/state_repository.rb +63 -0
- data/lib/phronomy/workflow/persistence/storage_schema.rb +13 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_engine_environment.rb +38 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb +156 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_terminal_policy.rb +40 -0
- data/lib/phronomy.rb +97 -98
- data/scripts/api_snapshot.rb +36 -16
- data/scripts/storage_spi_snapshot.rb +36 -0
- data/sig/_private/phronomy/agent/settings.rbs +19 -0
- data/sig/_private/phronomy/configuration/runtime_settings.rbs +20 -0
- data/sig/_private/phronomy/llm_adapter/async/async_client.rbs +10 -0
- data/sig/_private/phronomy/multi_agent/store.rbs +8 -0
- data/sig/_private/phronomy/persistence/storage_boundary.rbs +7 -0
- data/sig/_private/phronomy/tool/settings.rbs +13 -0
- data/sig/_private/phronomy/tracing/settings.rbs +15 -0
- data/sig/_private/phronomy/workflow/execution/workflow_settings.rbs +13 -0
- data/sig/phronomy/agent/admission.rbs +19 -0
- data/sig/phronomy/agent/execution_contract.rbs +132 -0
- data/sig/phronomy/agent/execution_environment.rbs +20 -0
- data/sig/phronomy/agent/store.rbs +73 -0
- data/sig/phronomy/agent.rbs +14 -139
- data/sig/phronomy/common.rbs +7 -0
- data/sig/phronomy/content_store/contracts.rbs +13 -0
- data/sig/phronomy/context.rbs +152 -0
- data/sig/phronomy/embeddings/async/async_client.rbs +9 -0
- data/sig/phronomy/embeddings/base.rbs +17 -0
- data/sig/phronomy/engine/concurrency/timer_bindings.rbs +22 -0
- data/sig/phronomy/engine/control_bindings.rbs +11 -0
- data/sig/phronomy/engine/control_protocols.rbs +23 -0
- data/sig/phronomy/engine/execution_binding.rbs +6 -0
- data/sig/phronomy/engine/execution_receiver.rbs +15 -0
- data/sig/phronomy/engine/observability.rbs +13 -0
- data/sig/phronomy/execution/async_operation.rbs +7 -0
- data/sig/phronomy/execution/blocking.rbs +5 -0
- data/sig/phronomy/execution/concurrency/cancellation_token.rbs +13 -0
- data/sig/phronomy/execution/concurrency/deadline.rbs +10 -0
- data/sig/phronomy/execution/concurrency/result_composition.rbs +7 -0
- data/sig/phronomy/execution/control_protocols.rbs +18 -0
- data/sig/phronomy/execution/errors.rbs +7 -0
- data/sig/phronomy/execution/execution.rbs +41 -0
- data/sig/phronomy/execution/execution_receiver.rbs +40 -0
- data/sig/phronomy/execution/execution_scope_state.rbs +9 -0
- data/sig/phronomy/execution/invocation_context.rbs +18 -0
- data/sig/phronomy/execution/invocation_controls.rbs +13 -0
- data/sig/phronomy/execution/task_result.rbs +29 -0
- data/sig/phronomy/extensions.rbs +0 -21
- data/sig/phronomy/handoff.rbs +12 -4
- data/sig/phronomy/llm_adapter/backends/ruby_llm.rbs +6 -0
- data/sig/phronomy/llm_adapter/base.rbs +14 -0
- data/sig/phronomy/llm_adapter/usage.rbs +14 -0
- data/sig/phronomy/llm_adapter/values.rbs +43 -0
- data/sig/phronomy/llm_context_window.rbs +10 -0
- data/sig/phronomy/multi_agent/reserved_child_admission.rbs +8 -0
- data/sig/phronomy/multi_agent/store.rbs +54 -0
- data/sig/phronomy/multi_agent.rbs +24 -3
- data/sig/phronomy/persistence/errors.rbs +18 -0
- data/sig/phronomy/persistence/migration.rbs +13 -0
- data/sig/phronomy/persistence/transaction.rbs +19 -0
- data/sig/phronomy/persistence.rbs +3 -185
- data/sig/phronomy/persistence_composition.rbs +19 -0
- data/sig/phronomy/runtime_composition/configuration.rbs +36 -0
- data/sig/phronomy/storage/async/async_client.rbs +8 -0
- data/sig/phronomy/storage/backends/in_memory.rbs +9 -0
- data/sig/phronomy/storage/contracts.rbs +160 -0
- data/sig/phronomy/tool.rbs +58 -5
- data/sig/phronomy/tracing/observation.rbs +7 -0
- data/sig/phronomy/vector_store/async/async_client.rbs +13 -0
- data/sig/phronomy/vector_store/base.rbs +23 -0
- data/sig/phronomy/workflow/execution/workflow_execution_environment.rbs +14 -0
- data/sig/phronomy/workflow/runtime_binding/workflow_terminal_policy.rbs +7 -0
- data/sig/phronomy/workflow/store.rbs +8 -0
- data/sig/phronomy/workflow.rbs +1 -1
- data/sig/phronomy.rbs +0 -49
- metadata +462 -130
- data/lib/phronomy/agent/agent_invocation_session_builder.rb +0 -584
- data/lib/phronomy/agent/concerns/before_llm_input.rb +0 -66
- data/lib/phronomy/agent/concerns/error_translation.rb +0 -45
- data/lib/phronomy/agent/concerns/filterable.rb +0 -184
- data/lib/phronomy/agent/context/capability/base.rb +0 -507
- data/lib/phronomy/agent/context/instruction/prompt_template.rb +0 -102
- data/lib/phronomy/agent/context_parts/validators/final_budget_validator.rb +0 -37
- data/lib/phronomy/agent/context_policies/default.rb +0 -142
- data/lib/phronomy/agent/execution_coordinator.rb +0 -3151
- data/lib/phronomy/agent/handoff_execution_coordinator.rb +0 -143
- data/lib/phronomy/agent/handoff_request.rb +0 -45
- data/lib/phronomy/agent/handoff_runner.rb +0 -178
- data/lib/phronomy/agent/phase_machine_builder.rb +0 -152
- data/lib/phronomy/agent/provider_call_outcome.rb +0 -106
- data/lib/phronomy/agent/recovery_support.rb +0 -504
- data/lib/phronomy/agent/selection/candidate.rb +0 -53
- data/lib/phronomy/agent/selection/constraint.rb +0 -49
- data/lib/phronomy/agent/token_budget_resolver.rb +0 -70
- data/lib/phronomy/agent/tool_call_intercepted.rb +0 -29
- data/lib/phronomy/agent/tool_invocation_session_builder.rb +0 -276
- data/lib/phronomy/configuration.rb +0 -59
- data/lib/phronomy/engine/concurrency/cancellation_scope.rb +0 -124
- data/lib/phronomy/engine/concurrency/operation_binding.rb +0 -43
- data/lib/phronomy/generator_verifier.rb +0 -369
- data/lib/phronomy/invalid_context_budget_configuration_error.rb +0 -8
- data/lib/phronomy/llm_adapter/ruby_llm.rb +0 -47
- data/lib/phronomy/metrics.rb +0 -26
- data/lib/phronomy/multi_agent/parallel_tool_chat.rb +0 -116
- data/lib/phronomy/persistence/durable_codec.rb +0 -706
- data/lib/phronomy/persistence/in_memory.rb +0 -690
- data/lib/phronomy/persistence/repository_facades.rb +0 -535
- data/lib/phronomy/persistence.rb +0 -276
- data/lib/phronomy/recovery.rb +0 -186
- data/lib/phronomy/ruby_llm_patches.rb +0 -24
- data/lib/phronomy/runnable.rb +0 -54
- data/lib/phronomy/token_usage.rb +0 -85
- data/lib/phronomy/vector_store/embeddings/base.rb +0 -44
- data/lib/phronomy/vector_store/embeddings/ruby_llm_embeddings.rb +0 -47
- data/lib/phronomy/workflow/phase_machine_builder.rb +0 -205
- data/lib/phronomy/workflow_recovery.rb +0 -123
- data/sig/phronomy/llm_adapter.rbs +0 -11
- data/sig/phronomy/runtime.rbs +0 -76
- /data/lib/phronomy/agent/{llm_operation_result.rb → execution/llm_operation_result.rb} +0 -0
- /data/lib/phronomy/{canonical_json.rb → common/canonical_json.rb} +0 -0
- /data/lib/phronomy/{event.rb → engine/event.rb} +0 -0
|
@@ -23,37 +23,38 @@ are defined by
|
|
|
23
23
|
|
|
24
24
|
## Runtime model
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
│ │ └─ agent_id -> one nonterminal logical Execution admission
|
|
38
|
-
│ └─ Agent execution directory
|
|
39
|
-
│ └─ execution_id -> immutable live-state record
|
|
40
|
-
├─ OffloadPool (bounded operating-system Threads)
|
|
41
|
-
│ ├─ private Operation records
|
|
42
|
-
│ ├─ blocking input/output (I/O)
|
|
43
|
-
│ ├─ central-processing-unit (CPU)-bound synchronous work
|
|
44
|
-
│ └─ operation-specific durable Agent/Workflow work
|
|
45
|
-
├─ named OffloadPools
|
|
46
|
-
└─ EventLoop-driven timers
|
|
47
|
-
|
|
48
|
-
EventLoop / FSMSession ─┐
|
|
49
|
-
├─> TaskResult = completion handle
|
|
50
|
-
OffloadPool ────────────┘
|
|
51
|
-
```
|
|
26
|
+
| Runtime-retained component | Implementation owner and responsibility |
|
|
27
|
+
|---|---|
|
|
28
|
+
| Shutdown participants | Feature-owned registries: Agent identity, Team identity, and MultiAgent synchronous-call admission |
|
|
29
|
+
| EventLoop | Engine dispatcher thread, FSMSessions and registered feature receivers |
|
|
30
|
+
| Execution receivers | Agent/Workflow own execution admission, state and idle policies; mutations run on EventLoop |
|
|
31
|
+
| OffloadPool and named pools | Engine bounded workers and private physical-operation records |
|
|
32
|
+
| Timers | Engine EventLoop-driven scheduling |
|
|
33
|
+
|
|
34
|
+
Runtime retains participants through a generic lifecycle protocol; it does not
|
|
35
|
+
construct Agent or Team identity registries. Each feature registers its own
|
|
36
|
+
instance. See [ADR-041](decisions/041-feature-owned-identity-registries.md).
|
|
52
37
|
|
|
53
38
|
The framework does not allocate one operating-system Thread per logical
|
|
54
39
|
Agent/Workflow/Tool lifecycle. Logical waits remain explicit states plus later
|
|
55
40
|
EventLoop events.
|
|
56
41
|
|
|
42
|
+
## Feature execution receivers
|
|
43
|
+
|
|
44
|
+
[ADR-042](decisions/042-feature-owned-execution-state.md) separates implementation
|
|
45
|
+
ownership from the single-writer execution context. Agent and Workflow create
|
|
46
|
+
`Agent::ExecutionRegistry` and `WorkflowExecutionRegistry` respectively; Engine
|
|
47
|
+
retains registered receivers without selecting concrete feature types.
|
|
48
|
+
|
|
49
|
+
Receiver registration and new request admission close before idle checks.
|
|
50
|
+
Already queued requests, pending durable operations and supervised physical
|
|
51
|
+
work are counted until their feature continuation settles. Worker callbacks
|
|
52
|
+
only enqueue results. Workflow instance routing and generic FSM enqueue share
|
|
53
|
+
one lock. After a clean join, receiver cleanup invalidates retained state;
|
|
54
|
+
a failing receiver makes Runtime cleanup incomplete. Dispatcher failure instead
|
|
55
|
+
notifies receivers on the failing loop thread with the original error.
|
|
56
|
+
|
|
57
|
+
|
|
57
58
|
## Live state and durable state
|
|
58
59
|
|
|
59
60
|
A live Agent or Workflow owns its current logical state. `Persistence` is the
|
|
@@ -61,7 +62,7 @@ last committed durable representation and recovery source; it is not reloaded at
|
|
|
61
62
|
every semantic boundary.
|
|
62
63
|
|
|
63
64
|
For active Agents, **EventLoop is the single writer of Phronomy-managed live
|
|
64
|
-
execution state**.
|
|
65
|
+
execution state**. `Agent::ExecutionRegistry` owns the process-local directory keyed by
|
|
65
66
|
canonical `execution_id`. Each directory value is immutable and is replaced on
|
|
66
67
|
EventLoop when the current AgentExecution, RuntimeProjection, AgentInvocation, or
|
|
67
68
|
owning FSMSession changes. The former mutex-protected
|
|
@@ -75,7 +76,7 @@ handling; workers do not receive AgentInvocation as a mutable state authority.
|
|
|
75
76
|
Mutable Agent/Execution/Journal state is not automatically reloaded before every
|
|
76
77
|
LLM or Tool step. Durable writes use optimistic revision/position guardrails; an
|
|
77
78
|
external writer that advances the durable base causes
|
|
78
|
-
`
|
|
79
|
+
`Storage::ConflictError` rather than automatic reload or merge.
|
|
79
80
|
|
|
80
81
|
For Workflows, the current `WorkflowContext` and FSMSession own the active
|
|
81
82
|
logical state. A durable Workflow hydrates once at invocation/resume and saves at
|
|
@@ -88,8 +89,9 @@ content reference is value materialization rather than mutable state refresh.
|
|
|
88
89
|
|
|
89
90
|
`agent_id` identifies one logical Agent, not a reusable lookup key for independent
|
|
90
91
|
mutable objects. One Runtime therefore publishes at most one mutable live Agent
|
|
91
|
-
instance for a given `agent_id`. The
|
|
92
|
-
cache, and reserves the identity before
|
|
92
|
+
instance for a given `agent_id`. The Agent-owned registry, strongly retained by
|
|
93
|
+
Runtime, is an authority, not a cache, and reserves the identity before
|
|
94
|
+
create/load materialization.
|
|
93
95
|
|
|
94
96
|
The application-facing identity operations are distinct:
|
|
95
97
|
|
|
@@ -100,7 +102,7 @@ new / create
|
|
|
100
102
|
load(agent_id, persistence:)
|
|
101
103
|
live -> exact same Ruby object, with no Persistence reload
|
|
102
104
|
durable-only -> hydrate and publish once
|
|
103
|
-
missing ->
|
|
105
|
+
missing -> Storage::NotFoundError
|
|
104
106
|
|
|
105
107
|
get(agent_id)
|
|
106
108
|
live Runtime lookup only; missing -> nil
|
|
@@ -114,7 +116,8 @@ invalidates the old object, deletes durable state, releases the process-local
|
|
|
114
116
|
identity, and allows a later new Agent to reuse the textual ID.
|
|
115
117
|
|
|
116
118
|
Live Agent ownership and top-level Execution admission are separate lifetimes.
|
|
117
|
-
For one live Agent,
|
|
119
|
+
For one live Agent, `Agent::ExecutionRegistry` admits at most one nonterminal
|
|
120
|
+
top-level Execution on EventLoop.
|
|
118
121
|
Admission is acquired **before** the initial Offload/Persistence operation:
|
|
119
122
|
|
|
120
123
|
```text
|
|
@@ -195,6 +198,15 @@ Phronomy-managed live domain objects are rejected from that value data. A comple
|
|
|
195
198
|
value-type/serialization contract for arbitrary Application-owned opaque objects is
|
|
196
199
|
deferred; such objects remain Application-owned and must be worker-safe.
|
|
197
200
|
|
|
201
|
+
The existing restricted types declare the internal methodless
|
|
202
|
+
`Concurrency::WorkerInputRestricted` marker at their own definitions.
|
|
203
|
+
ToolInvocation checks that execution-boundary contract instead of enumerating
|
|
204
|
+
Agent/Workflow/Runtime classes. Frozen marked values and subclasses remain
|
|
205
|
+
restricted; Hash keys and values, nested Arrays and behavior handles are all
|
|
206
|
+
checked. The marker does not inspect opaque application fields or closure
|
|
207
|
+
captures and is not a general restriction on every OffloadPool command.
|
|
208
|
+
See [ADR-045](decisions/045-worker-input-restriction-ownership.md).
|
|
209
|
+
|
|
198
210
|
Worker authorization/execution outcomes return as values carrying
|
|
199
211
|
`tool_invocation_id`; the Tool FSMSession consumes a mismatched semantic result
|
|
200
212
|
without advancing its current state.
|
|
@@ -279,6 +291,32 @@ remain durable conflict defense rather than distributed ownership.
|
|
|
279
291
|
|
|
280
292
|
## Tool execution modes
|
|
281
293
|
|
|
294
|
+
The default dispatch helper is the private
|
|
295
|
+
`Phronomy::Tool::ToolExecutor`, colocated with Capability
|
|
296
|
+
Base. Agent ToolInvocation supplies Runtime and admission policy for the
|
|
297
|
+
standard path, and owns authorization and logical result handling. Custom
|
|
298
|
+
`call_async` implementations keep the public Tool protocol. This ownership is
|
|
299
|
+
defined by [ADR-035](decisions/035-tool-executor-capability-ownership.md); the
|
|
300
|
+
public `Phronomy::Tool::Base` facade and its Class identity remain unchanged.
|
|
301
|
+
|
|
302
|
+
Agent uses ordinary `RubyLLM::Chat` for both complete and streaming Provider
|
|
303
|
+
calls. The `before_tool_call` callback takes the complete assistant Tool-call
|
|
304
|
+
batch before RubyLLM executes the first Tool body. `AgentInvocation` then owns
|
|
305
|
+
authorization, dispatch, approval suspension and result collection. Offloaded
|
|
306
|
+
Tools can overlap within Runtime capacity; short cooperative Tools run according
|
|
307
|
+
to their existing execution contract.
|
|
308
|
+
|
|
309
|
+
On successful batch completion, Agent records one Tool result per call ID in
|
|
310
|
+
request order and includes the whole exchange in the next Provider request.
|
|
311
|
+
Worker completion order does not split the conversation or trigger a partial
|
|
312
|
+
Provider continuation. Failure, cancellation and approval keep their existing
|
|
313
|
+
Agent lifecycle semantics.
|
|
314
|
+
|
|
315
|
+
There is no separate Chat execution path or `parallel_tool_execution` switch.
|
|
316
|
+
The removed internal `MultiAgent::ParallelToolChat` class is not replaced by an
|
|
317
|
+
alias. See the [Chat migration guide](migrations/parallel-tool-chat-removal.md)
|
|
318
|
+
for application configuration and existing stored model-config records.
|
|
319
|
+
|
|
282
320
|
Phronomy exposes two execution modes for capabilities:
|
|
283
321
|
|
|
284
322
|
- `:cooperative` — short EventLoop-safe work, or specialized asynchronous work
|
|
@@ -539,14 +577,86 @@ TaskResult reports only caller-facing settlement.
|
|
|
539
577
|
## EventLoop metrics
|
|
540
578
|
|
|
541
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.
|
|
542
593
|
Use these to distinguish worker saturation from EventLoop backlog/latency.
|
|
543
594
|
|
|
544
595
|
## Shutdown
|
|
545
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
|
+
|
|
546
609
|
`Runtime#shutdown` is terminal for that Runtime. It drains/terminates the
|
|
547
610
|
Runtime-owned EventLoop, then closes pools and timers according to the Runtime
|
|
548
611
|
shutdown contract.
|
|
549
612
|
|
|
613
|
+
Internal subsystems can register one shutdown participant per Runtime-local key
|
|
614
|
+
through `Runtime#__register_shutdown_participant`. This is an internal lifecycle
|
|
615
|
+
contract, not a public extension API or an execution registry. Runtime does not
|
|
616
|
+
construct participants or interpret their admission rules.
|
|
617
|
+
|
|
618
|
+
Each participant implements two required operations:
|
|
619
|
+
|
|
620
|
+
- `begin_draining` closes admission under the participant's admission lock. It
|
|
621
|
+
must be idempotent, short and nonblocking, and must not call back into Runtime.
|
|
622
|
+
Runtime invokes it under its lifecycle lock when shutdown starts or EventLoop
|
|
623
|
+
fails, so no participant can be registered after closure begins.
|
|
624
|
+
- `wait_until_idle(deadline)` returns whether admitted calls have finished before
|
|
625
|
+
the shared absolute monotonic deadline. Runtime closes every participant before
|
|
626
|
+
waiting, and waits without holding its lifecycle lock. A timeout or failed hook
|
|
627
|
+
makes cleanup incomplete; other participants, pools and timers still receive
|
|
628
|
+
their shutdown calls. Incomplete cleanup prevents default Runtime replacement.
|
|
629
|
+
|
|
630
|
+
Participants may also implement `after_runtime_shutdown`. Runtime calls it
|
|
631
|
+
outside its lifecycle lock after every wait succeeds, EventLoop has stopped,
|
|
632
|
+
and pools/timers have shut down successfully. The hook must be idempotent,
|
|
633
|
+
short, and perform no I/O; its return value is ignored. A hook exception marks
|
|
634
|
+
cleanup incomplete, retains the first failure, and does not skip later hooks.
|
|
635
|
+
Completed releases are not rolled back; repeated shutdown returns its cached
|
|
636
|
+
result without retrying hooks. A prior closure/wait failure, timeout, or worker
|
|
637
|
+
shutdown failure skips finalization entirely.
|
|
638
|
+
|
|
639
|
+
`Runtime#__shutdown_participant(key:)` only retrieves an existing participant,
|
|
640
|
+
including during/after shutdown. Registration still requires a running Runtime,
|
|
641
|
+
even for an existing key. Agent/Team `get` uses lookup without registration;
|
|
642
|
+
feature gates reject new ownership changes through retained registries after
|
|
643
|
+
closure. Already admitted transitions may finish. Identity registries wait only
|
|
644
|
+
for active construction/purge, then detach Agent references or clear Team
|
|
645
|
+
owners at finalization; live idle objects do not themselves prolong shutdown.
|
|
646
|
+
|
|
647
|
+
`MultiAgent::AdmissionRegistry.for(runtime)` constructs and registers the shared
|
|
648
|
+
coordination admission registry. MultiAgent::HandoffRunner and TeamCoordinator retain that
|
|
649
|
+
registry, admit each synchronous call directly and release it in `ensure` after
|
|
650
|
+
successful admission. The registry closes admission atomically with respect to
|
|
651
|
+
`admit!`; a call admitted before closure is included in the wait, and a call after
|
|
652
|
+
closure raises `RuntimeShutdownError`, including after EventLoop failure. The
|
|
653
|
+
existing `HandoffError` for duplicate admission remains unchanged.
|
|
654
|
+
|
|
655
|
+
This tracks the current synchronous Handoff/Team call, not the lifetime or
|
|
656
|
+
completion of a durable AgentExecution or TeamExecution. Multi-Agent policy stays
|
|
657
|
+
in `multi_agent`; Runtime depends only on the shutdown operations. Application
|
|
658
|
+
authors do not register these participants themselves.
|
|
659
|
+
|
|
550
660
|
Workflow durable admission participates in EventLoop idleness: a Workflow whose
|
|
551
661
|
FSMSession has ended but whose durable save is still in flight remains owned until
|
|
552
662
|
that save completes and owner-aware admission is released.
|
|
@@ -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
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Agent
|
|
5
|
+
StreamEvent = Data.define(:type, :payload)
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
require_relative "../async_event_api"
|
|
10
|
+
|
|
11
|
+
unless Phronomy::Agent::Base < Phronomy::Agent::AsyncEventApi
|
|
12
|
+
Phronomy::Agent::Base.prepend(Phronomy::Agent::AsyncEventApi)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Durable Agent Recovery and Agent-incarnation event binding.
|
|
16
|
+
require_relative "../recovery/recovery_support"
|
|
17
|
+
require_relative "../recovery/recovery_coordinator"
|
|
@@ -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::
|
|
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,
|
|
@@ -190,7 +202,7 @@ module Phronomy
|
|
|
190
202
|
end
|
|
191
203
|
|
|
192
204
|
bytes = Phronomy::CanonicalJSON.dump(value)
|
|
193
|
-
snapshot = Phronomy::
|
|
205
|
+
snapshot = Phronomy::Values::Immutable.copy(
|
|
194
206
|
Phronomy::CanonicalJSON.load(bytes)
|
|
195
207
|
)
|
|
196
208
|
config.merge(durable_context: snapshot)
|
|
@@ -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
|