phronomy 0.27.0 → 0.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.mutant.yml +9 -9
- data/CHANGELOG.md +52 -0
- data/CONTRIBUTING.md +8 -8
- data/README.md +28 -7
- data/benchmark/bench_agent_invoke.rb +25 -50
- data/benchmark/bench_context_assembler.rb +14 -11
- data/benchmark/bench_regression.rb +1 -1
- data/benchmark/bench_tool_schema.rb +1 -1
- data/docs/application-recipes.md +229 -0
- data/docs/architecture/agent-chat-and-state-ownership.md +7 -1
- data/docs/architecture/agent-transition-ownership.md +13 -89
- data/docs/architecture/backend-groups.md +152 -0
- data/docs/architecture/before-llm-input.md +3 -3
- data/docs/architecture/context-management.md +1 -1
- data/docs/architecture/dx05-agent-scope.md +119 -0
- data/docs/architecture/dx05-workflow-task-state.md +134 -0
- data/docs/architecture/error-api-review.md +245 -0
- data/docs/architecture/execution-abstraction-review.md +88 -0
- data/docs/architecture/execution-boundaries.md +28 -0
- data/docs/architecture/execution-metadata-and-values.md +6 -0
- data/docs/architecture/multi-agent-handoff.md +6 -5
- data/docs/architecture/persistence.md +27 -9
- data/docs/architecture/r8-unit1.md +104 -0
- data/docs/architecture/r8-unit10.md +72 -0
- data/docs/architecture/r8-unit11.md +71 -0
- data/docs/architecture/r8-unit12.md +67 -0
- data/docs/architecture/r8-unit13.md +61 -0
- data/docs/architecture/r8-unit14.md +72 -0
- data/docs/architecture/r8-unit15.md +65 -0
- data/docs/architecture/r8-unit16.md +54 -0
- data/docs/architecture/r8-unit17.md +73 -0
- data/docs/architecture/r8-unit18.md +92 -0
- data/docs/architecture/r8-unit19.md +61 -0
- data/docs/architecture/r8-unit2.md +55 -0
- data/docs/architecture/r8-unit20.md +51 -0
- data/docs/architecture/r8-unit21.md +79 -0
- data/docs/architecture/r8-unit22.md +74 -0
- data/docs/architecture/r8-unit23.md +81 -0
- data/docs/architecture/r8-unit3.md +130 -0
- data/docs/architecture/r8-unit4.md +79 -0
- data/docs/architecture/r8-unit5.md +115 -0
- data/docs/architecture/r8-unit6.md +71 -0
- data/docs/architecture/r8-unit7.md +70 -0
- data/docs/architecture/r8-unit8.md +78 -0
- data/docs/architecture/r8-unit9.md +73 -0
- data/docs/architecture/refactoring-closure.md +10 -1
- data/docs/architecture/remaining-refactoring-plan.md +16 -0
- data/docs/architecture/storage-execution-boundaries.md +54 -0
- data/docs/architecture/tool-schema-recording-gap.md +6 -0
- data/docs/architecture/tracing.md +1 -1
- data/docs/architecture/workflow-terminal-ownership-design.md +2 -2
- data/docs/architecture.md +24 -11
- data/docs/decisions/015-tool-public-facade-and-rbs-boundary.md +3 -3
- data/docs/decisions/027-llm-adapter-provider-boundary.md +2 -0
- data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +2 -0
- data/docs/decisions/031-durable-multi-agent-coordination.md +2 -0
- data/docs/decisions/032-storage-backend-composition.md +5 -0
- data/docs/decisions/034-handoff-runner-coordination-ownership.md +2 -0
- data/docs/decisions/035-tool-executor-capability-ownership.md +1 -1
- data/docs/decisions/038-responsibility-based-source-layout.md +14 -0
- data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +8 -0
- data/docs/decisions/040-configuration-default-composition.md +12 -1
- data/docs/decisions/045-worker-input-restriction-ownership.md +4 -0
- data/docs/decisions/051-execution-outcome-worker-ownership.md +2 -0
- data/docs/decisions/056-workflow-terminal-policy-ownership.md +1 -1
- data/docs/decisions/058-neutral-storage-primitives.md +5 -0
- data/docs/decisions/059-backend-contracts-and-async-clients.md +74 -0
- data/docs/decisions/060-runtime-settings-and-application-configuration.md +86 -0
- data/docs/decisions/061-persistence-failure-contracts.md +89 -0
- data/docs/decisions/062-content-store-service.md +52 -0
- data/docs/decisions/063-execution-contracts-and-services.md +88 -0
- data/docs/decisions/064-agent-execution-and-multi-agent-responsibility.md +36 -0
- data/docs/decisions/065-vector-and-embeddings-common-operations.md +38 -0
- data/docs/decisions/066-llm-and-tool-operation-contracts.md +20 -0
- data/docs/decisions/067-agent-progress-and-engine-connection.md +29 -0
- data/docs/decisions/068-agent-tool-child-progress-and-tool-operation.md +36 -0
- data/docs/decisions/README.md +18 -1
- data/docs/execution-boundaries-preview.md +45 -0
- data/docs/failure-reason-codes.md +45 -0
- data/docs/features.md +12 -4
- data/docs/getting-started.md +52 -48
- data/docs/migrations/0.22.md +2 -2
- data/docs/migrations/durable-semantic-coordination-v2.md +2 -0
- data/docs/migrations/handoff-runner-multi-agent.md +2 -0
- data/docs/migrations/persistence-failure-contracts.md +67 -0
- data/docs/migrations/r8-unit10.md +36 -0
- data/docs/migrations/r8-unit11.md +33 -0
- data/docs/migrations/r8-unit12.md +31 -0
- data/docs/migrations/r8-unit13.md +36 -0
- data/docs/migrations/r8-unit14.md +31 -0
- data/docs/migrations/r8-unit15.md +31 -0
- data/docs/migrations/r8-unit16.md +26 -0
- data/docs/migrations/r8-unit17.md +28 -0
- data/docs/migrations/r8-unit18.md +29 -0
- data/docs/migrations/r8-unit19.md +28 -0
- data/docs/migrations/r8-unit20.md +24 -0
- data/docs/migrations/r8-unit21.md +28 -0
- data/docs/migrations/r8-unit22.md +29 -0
- data/docs/migrations/r8-unit23.md +20 -0
- data/docs/migrations/r8-unit4.md +67 -0
- data/docs/migrations/r8-unit5.md +75 -0
- data/docs/migrations/r8-unit6.md +109 -0
- data/docs/migrations/r8-unit7.md +31 -0
- data/docs/migrations/r8-unit8.md +33 -0
- data/docs/migrations/r8-unit9.md +28 -0
- data/docs/migrations/storage-async-client.md +103 -0
- data/docs/migrations/vector-async-clients.md +110 -0
- data/docs/ordered-event-delivery.md +54 -0
- data/docs/persistence-backends.md +38 -21
- data/docs/runtime-and-concurrency.md +26 -1
- data/docs/workflow-completion.md +92 -0
- data/gemfiles/http_minimum.gemfile +5 -0
- data/lib/phronomy/agent/admission.rb +78 -0
- data/lib/phronomy/agent/{lifecycle_contract/agent_already_exists_error.rb → agent_already_exists_error.rb} +1 -1
- data/lib/phronomy/agent/{lifecycle_contract/agent_busy_error.rb → agent_busy_error.rb} +1 -1
- data/lib/phronomy/agent/agent_not_found_error.rb +16 -0
- data/lib/phronomy/agent/{lifecycle_contract/agent_purged_error.rb → agent_purged_error.rb} +1 -1
- data/lib/phronomy/agent/agent_unloaded_error.rb +14 -0
- data/lib/phronomy/agent/api/agent.rb +13 -0
- data/lib/phronomy/{recovery → agent/api}/execution_rehydration_required_error.rb +1 -1
- data/lib/phronomy/agent/async_event_api.rb +132 -103
- data/lib/phronomy/agent/base.rb +146 -113
- data/lib/phronomy/agent/before_llm_input.rb +70 -0
- data/lib/phronomy/agent/composition/run_once.rb +1 -1
- data/lib/phronomy/agent/{context_assembly/context_candidate_resolver.rb → context_candidate_resolver.rb} +2 -2
- data/lib/phronomy/agent/{context_assembly/context_assembler.rb → context_preparation.rb} +35 -221
- data/lib/phronomy/agent/control_request.rb +21 -0
- data/lib/phronomy/agent/execution/agent_execution.rb +1 -1
- data/lib/phronomy/agent/execution/agent_invocation.rb +27 -53
- data/lib/phronomy/agent/execution/approval_resume_commit.rb +1 -1
- data/lib/phronomy/agent/execution/dispatch_preparation.rb +17 -127
- data/lib/phronomy/agent/execution/exact_execution.rb +55 -32
- data/lib/phronomy/agent/execution/execution_cancellation.rb +8 -4
- data/lib/phronomy/agent/execution/execution_coordinator.rb +229 -407
- data/lib/phronomy/agent/execution/execution_metadata.rb +1 -0
- data/lib/phronomy/agent/execution/execution_outcome_committer.rb +71 -127
- data/lib/phronomy/agent/execution/execution_registry.rb +6 -2
- data/lib/phronomy/agent/execution/execution_session_runner.rb +22 -27
- data/lib/phronomy/agent/execution/initial_preparation.rb +19 -120
- data/lib/phronomy/agent/execution/{agent_invocation_session_builder.rb → invocation_actions.rb} +54 -194
- data/lib/phronomy/agent/execution/invocation_transitions.rb +39 -1
- data/lib/phronomy/agent/execution/runtime_record_encoder.rb +8 -11
- data/lib/phronomy/agent/execution_change.rb +161 -0
- data/lib/phronomy/agent/execution_environment.rb +36 -0
- data/lib/phronomy/agent/execution_extension_state.rb +21 -0
- data/lib/phronomy/agent/execution_observation.rb +35 -0
- data/lib/phronomy/agent/filterable.rb +188 -0
- data/lib/phronomy/agent/handoff/{handoff_context.rb → transfer_context.rb} +4 -4
- data/lib/phronomy/agent/handoff/{handoff_projection.rb → transfer_projection.rb} +14 -33
- data/lib/phronomy/{llm_contract/transport_error.rb → agent/handoff_error.rb} +1 -1
- data/lib/phronomy/agent/journal/journal_projection.rb +1 -1
- data/lib/phronomy/agent/knowledge_item.rb +13 -0
- data/lib/phronomy/agent/lifecycle/agent_root.rb +1 -1
- data/lib/phronomy/agent/lifecycle/lifetime_api.rb +76 -0
- data/lib/phronomy/agent/lifecycle/operation_lease.rb +33 -0
- data/lib/phronomy/agent/lifecycle/operation_result.rb +53 -0
- data/lib/phronomy/agent/lifecycle/ownership_registry.rb +117 -31
- data/lib/phronomy/agent/lifecycle/scope.rb +198 -0
- data/lib/phronomy/agent/lifecycle/scope_cleanup_error.rb +19 -0
- data/lib/phronomy/agent/lifecycle/unload_transition.rb +78 -0
- data/lib/phronomy/agent/persistence/agent_repository.rb +29 -21
- data/lib/phronomy/agent/persistence/cancellation_repository.rb +51 -0
- data/lib/phronomy/agent/persistence/codec.rb +1 -21
- data/lib/phronomy/agent/persistence/execution_repository.rb +57 -41
- data/lib/phronomy/agent/persistence/journal_repository.rb +27 -18
- data/lib/phronomy/agent/persistence/records.rb +33 -0
- data/lib/phronomy/agent/persistence/retention_repository.rb +70 -0
- data/lib/phronomy/agent/persistence/storage_schema.rb +7 -2
- data/lib/phronomy/agent/persistence/watermark.rb +13 -11
- data/lib/phronomy/agent/recovery/invocation_restorer.rb +13 -21
- data/lib/phronomy/agent/recovery/recovery_coordinator/continuation.rb +13 -12
- data/lib/phronomy/agent/recovery/recovery_coordinator/installation.rb +34 -34
- data/lib/phronomy/agent/recovery/recovery_coordinator/resolution.rb +33 -39
- data/lib/phronomy/agent/recovery/recovery_coordinator.rb +28 -25
- data/lib/phronomy/agent/recovery/recovery_rules.rb +119 -0
- data/lib/phronomy/agent/recovery/recovery_support.rb +10 -19
- data/lib/phronomy/agent/reserved_execution.rb +16 -0
- data/lib/phronomy/agent/retention.rb +17 -0
- data/lib/phronomy/agent/runtime_binding/engine_environment.rb +60 -0
- data/lib/phronomy/agent/runtime_binding/engine_session_builder.rb +34 -0
- data/lib/phronomy/agent/runtime_binding/phase_machine_builder.rb +64 -0
- data/lib/phronomy/agent/runtime_binding/tool_session_builder.rb +34 -0
- data/lib/phronomy/agent/{context_assembly/ruby_llm_materializer.rb → runtime_input.rb} +18 -35
- data/lib/phronomy/agent/{context_assembly/saved_context_reader.rb → saved_context_reader.rb} +6 -4
- data/lib/phronomy/agent/settings.rb +47 -0
- data/lib/phronomy/agent/{context_assembly/state_writer.rb → state_writer.rb} +4 -3
- data/lib/phronomy/agent/store.rb +253 -0
- data/lib/phronomy/agent/{lifecycle_contract/stream_callback_error.rb → stream_callback_error.rb} +3 -3
- data/lib/phronomy/agent/tool_execution/tool_binding.rb +4 -64
- data/lib/phronomy/agent/tool_execution/tool_invocation.rb +50 -219
- data/lib/phronomy/agent/tool_execution/tool_invocation_actions.rb +107 -0
- data/lib/phronomy/agent/tool_execution/tool_invocation_transitions.rb +70 -0
- data/lib/phronomy/common/error.rb +65 -1
- data/lib/phronomy/configuration/runtime_settings.rb +49 -0
- data/lib/phronomy/content_store/base.rb +7 -2
- data/lib/phronomy/content_store/storage_schema.rb +1 -0
- data/lib/phronomy/content_store/stored_contents.rb +2 -1
- data/lib/phronomy/context/assembly.rb +195 -0
- data/lib/phronomy/{llm_contract/context_budget_exceeded_error.rb → context/budget_exceeded_error.rb} +3 -1
- data/lib/phronomy/context/candidate.rb +51 -0
- data/lib/phronomy/context/constraint.rb +47 -0
- data/lib/phronomy/{agent/context_contract → context}/context_plan.rb +1 -1
- data/lib/phronomy/{agent/context_contract → context}/context_plan_validator.rb +3 -3
- data/lib/phronomy/{agent/context_contract → context}/context_policy.rb +1 -1
- data/lib/phronomy/{agent/context_contract → context}/context_policy_input.rb +1 -1
- data/lib/phronomy/{agent/context_assembly → context}/context_policy_input_builder.rb +1 -1
- data/lib/phronomy/context/default_policy.rb +140 -0
- data/lib/phronomy/context/final_budget_validator.rb +33 -0
- data/lib/phronomy/{agent/context_contract → context}/llm_input_build_context.rb +1 -1
- data/lib/phronomy/{agent/context_contract → context}/llm_input_manifest.rb +19 -19
- data/lib/phronomy/{agent/context_contract → context}/llm_input_patch.rb +1 -1
- data/lib/phronomy/context/manifest_error.rb +10 -0
- data/lib/phronomy/context/prompt_template.rb +98 -0
- data/lib/phronomy/{vector_store → documents}/loader/base.rb +1 -1
- data/lib/phronomy/{vector_store → documents}/loader/csv_loader.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/loader/markdown_loader.rb +3 -3
- data/lib/phronomy/{vector_store → documents}/loader/plain_text_loader.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/splitter/base.rb +1 -1
- data/lib/phronomy/{vector_store → documents}/splitter/fixed_size_splitter.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/splitter/recursive_splitter.rb +2 -2
- data/lib/phronomy/embeddings/async/async_client.rb +44 -0
- data/lib/phronomy/embeddings/authentication_error.rb +11 -0
- data/lib/phronomy/embeddings/backends/ruby_llm_embeddings.rb +52 -0
- data/lib/phronomy/embeddings/base.rb +43 -0
- data/lib/phronomy/embeddings/invalid_result_error.rb +11 -0
- data/lib/phronomy/embeddings/rate_limit_error.rb +11 -0
- data/lib/phronomy/embeddings/transport_error.rb +11 -0
- data/lib/phronomy/engine/concurrency/offload_pool.rb +36 -21
- data/lib/phronomy/engine/concurrency/pool_registry.rb +47 -12
- data/lib/phronomy/engine/diagnostics.rb +1 -1
- data/lib/phronomy/engine/event_loop.rb +7 -7
- data/lib/phronomy/engine/execution_binding.rb +23 -0
- data/lib/phronomy/engine/execution_receiver_binding.rb +43 -0
- data/lib/phronomy/engine/fsm_protocol.rb +1 -6
- data/lib/phronomy/engine/fsm_session.rb +3 -3
- data/lib/phronomy/engine/metrics.rb +16 -14
- data/lib/phronomy/engine/runtime/timer_queue.rb +1 -1
- data/lib/phronomy/engine/runtime.rb +25 -12
- data/lib/phronomy/engine/terminal_decision.rb +7 -0
- data/lib/phronomy/execution/async_operation.rb +41 -0
- data/lib/phronomy/{engine → execution}/blocking.rb +2 -2
- data/lib/phronomy/{engine → execution}/concurrency/cancellation_token.rb +1 -1
- data/lib/phronomy/{engine → execution}/concurrency/deadline.rb +0 -17
- data/lib/phronomy/{engine → execution}/concurrency/physical_completion_task.rb +1 -1
- data/lib/phronomy/{engine → execution}/concurrency/result_collector.rb +1 -1
- data/lib/phronomy/{engine → execution}/concurrency/result_composition.rb +33 -12
- data/lib/phronomy/{engine/concurrency/subscriptions.rb → execution/concurrency/result_subscriptions.rb} +14 -18
- data/lib/phronomy/execution/concurrency/subscriptions.rb +25 -0
- data/lib/phronomy/{engine → execution}/execution.rb +61 -27
- data/lib/phronomy/{engine → execution}/execution_cancellation_error.rb +2 -2
- data/lib/phronomy/execution/execution_receiver.rb +78 -0
- data/lib/phronomy/execution/execution_scope_state.rb +35 -0
- data/lib/phronomy/{engine → execution}/execution_timeout_error.rb +2 -2
- data/lib/phronomy/{engine → execution}/invocation_context.rb +0 -15
- data/lib/phronomy/execution/invocation_controls.rb +73 -0
- data/lib/phronomy/execution/runnable.rb +24 -0
- data/lib/phronomy/{engine → execution}/task_result.rb +9 -4
- data/lib/phronomy/execution/wait_policy.rb +24 -0
- data/lib/phronomy/filter/{contract/filter_block_error.rb → filter_block_error.rb} +3 -3
- data/lib/phronomy/generation/generator_verifier/agent_operation.rb +65 -0
- data/lib/phronomy/generation/generator_verifier/default_parser.rb +27 -0
- data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +2 -2
- data/lib/phronomy/generation/generator_verifier.rb +7 -7
- data/lib/phronomy/generation/low_confidence_error.rb +2 -2
- data/lib/phronomy/integrations/ordered_event_delivery.rb +208 -0
- data/lib/phronomy/llm_adapter/async/async_client.rb +47 -0
- data/lib/phronomy/{llm_contract → llm_adapter}/authentication_error.rb +3 -1
- data/lib/phronomy/llm_adapter/backends/ruby_llm.rb +183 -0
- data/lib/phronomy/llm_adapter/base.rb +49 -68
- data/lib/phronomy/{llm_contract → llm_adapter}/context_length_error.rb +3 -1
- data/lib/phronomy/llm_adapter/invalid_result_error.rb +10 -0
- data/lib/phronomy/llm_adapter/message.rb +62 -0
- data/lib/phronomy/{llm_contract → llm_adapter}/rate_limit_error.rb +3 -1
- data/lib/phronomy/llm_adapter/request.rb +79 -0
- data/lib/phronomy/llm_adapter/response.rb +36 -0
- data/lib/phronomy/llm_adapter/stream_chunk.rb +18 -0
- data/lib/phronomy/llm_adapter/token_usage.rb +64 -0
- data/lib/phronomy/llm_adapter/transport_error.rb +9 -0
- data/lib/phronomy/llm_adapter.rb +3 -16
- data/lib/phronomy/multi_agent/admission_registry.rb +1 -8
- data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +108 -84
- data/lib/phronomy/multi_agent/execution_environment.rb +29 -0
- data/lib/phronomy/{agent/handoff → multi_agent}/handoff.rb +2 -2
- data/lib/phronomy/{agent/handoff → multi_agent}/handoff_capability_factory.rb +21 -2
- data/lib/phronomy/multi_agent/handoff_participant.rb +79 -0
- data/lib/phronomy/{agent/handoff → multi_agent}/handoff_policy.rb +1 -1
- data/lib/phronomy/multi_agent/handoff_runner.rb +77 -100
- data/lib/phronomy/{agent/handoff → multi_agent}/handoff_state.rb +1 -1
- data/lib/phronomy/multi_agent/orchestrator.rb +61 -75
- data/lib/phronomy/multi_agent/persistence/codec.rb +20 -0
- data/lib/phronomy/multi_agent/persistence/handoff_state_repository.rb +77 -0
- data/lib/phronomy/multi_agent/persistence/records.rb +27 -0
- data/lib/phronomy/multi_agent/persistence/storage_schema.rb +21 -0
- data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +60 -44
- data/lib/phronomy/multi_agent/persistence/team_repository.rb +29 -21
- data/lib/phronomy/multi_agent/reserved_child_admission.rb +88 -0
- data/lib/phronomy/multi_agent/runtime_binding/engine_environment.rb +35 -0
- data/lib/phronomy/multi_agent/shared_state.rb +2 -2
- data/lib/phronomy/multi_agent/store.rb +156 -0
- data/lib/phronomy/multi_agent/team_busy_error.rb +18 -0
- data/lib/phronomy/multi_agent/team_coordinator.rb +99 -77
- data/lib/phronomy/multi_agent/team_not_found_error.rb +18 -0
- data/lib/phronomy/multi_agent/team_ownership_registry.rb +2 -12
- data/lib/phronomy/output_parser/parse_error.rb +7 -0
- data/lib/phronomy/persistence/content_repository.rb +26 -0
- data/lib/phronomy/persistence/errors.rb +16 -0
- data/lib/phronomy/persistence/migration/initial_format_migration.rb +2 -2
- data/lib/phronomy/persistence/persistence.rb +23 -0
- data/lib/phronomy/persistence/save_outcome.rb +24 -0
- data/lib/phronomy/persistence/snapshot_comparison.rb +81 -0
- data/lib/phronomy/persistence/storage_boundary.rb +32 -0
- data/lib/phronomy/persistence/transaction.rb +86 -0
- data/lib/phronomy/persistence_composition/storage_schema.rb +9 -4
- data/lib/phronomy/persistence_composition/stores.rb +34 -0
- data/lib/phronomy/persistence_composition/unit4_migration.rb +276 -0
- data/lib/phronomy/runtime_composition/agent_defaults.rb +5 -1
- data/lib/phronomy/runtime_composition/configuration.rb +146 -0
- data/lib/phronomy/runtime_composition/evaluation_defaults.rb +7 -0
- data/lib/phronomy/runtime_composition/execution_defaults.rb +5 -0
- data/lib/phronomy/runtime_composition/generation_defaults.rb +6 -0
- data/lib/phronomy/runtime_composition/global_configuration.rb +38 -0
- data/lib/phronomy/runtime_composition/multi_agent_defaults.rb +9 -0
- data/lib/phronomy/runtime_composition/workflow_defaults.rb +4 -0
- data/lib/phronomy/storage/async/async_client.rb +60 -0
- data/lib/phronomy/storage/backend.rb +8 -2
- data/lib/phronomy/storage/condition_failed_error.rb +2 -2
- data/lib/phronomy/storage/unique_constraint_error.rb +2 -2
- data/lib/phronomy/testing/eval/metrics.rb +1 -1
- data/lib/phronomy/testing/eval/scorer/llm_judge.rb +19 -3
- data/lib/phronomy/testing/persistence_contract/a_content_store.rb +2 -2
- data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +6 -6
- data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +55 -52
- data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +3 -3
- data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +7 -7
- data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +11 -11
- data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +57 -51
- data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +1 -1
- data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +23 -22
- data/lib/phronomy/tool/authorization.rb +77 -0
- data/lib/phronomy/tool/base.rb +551 -9
- data/lib/phronomy/tool/call_request.rb +40 -0
- data/lib/phronomy/{agent/tool_execution/tool_definition_set.rb → tool/definition_set.rb} +7 -8
- data/lib/phronomy/tool/operation.rb +88 -0
- data/lib/phronomy/tool/schema.rb +214 -0
- data/lib/phronomy/tool/settings.rb +31 -0
- data/lib/phronomy/tool/tool_error.rb +7 -0
- data/lib/phronomy/tool/tool_executor.rb +58 -0
- data/lib/phronomy/tools/agent.rb +22 -90
- data/lib/phronomy/tools/mcp.rb +21 -14
- data/lib/phronomy/tools/vector_search.rb +4 -4
- data/lib/phronomy/tracing/automatic.rb +11 -6
- data/lib/phronomy/tracing/base.rb +36 -7
- data/lib/phronomy/tracing/observation.rb +37 -0
- data/lib/phronomy/tracing/open_telemetry_tracer.rb +4 -29
- data/lib/phronomy/tracing/settings.rb +36 -0
- data/lib/phronomy/vector_store/{async_backend.rb → async/async_client.rb} +35 -29
- data/lib/phronomy/vector_store/{in_memory.rb → backends/in_memory.rb} +12 -10
- data/lib/phronomy/vector_store/{pgvector.rb → backends/pgvector.rb} +44 -16
- data/lib/phronomy/vector_store/{redis_search.rb → backends/redis_search.rb} +74 -37
- data/lib/phronomy/vector_store/base.rb +118 -49
- data/lib/phronomy/vector_store/invalid_result_error.rb +12 -0
- data/lib/phronomy/vector_store.rb +2 -17
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/workflow/completion.rb +11 -0
- data/lib/phronomy/workflow/execution/workflow.rb +37 -9
- data/lib/phronomy/workflow/execution/workflow_action_rules.rb +67 -0
- data/lib/phronomy/workflow/execution/workflow_context.rb +2 -2
- data/lib/phronomy/workflow/execution/workflow_execution_environment.rb +28 -0
- data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +77 -8
- data/lib/phronomy/workflow/execution/workflow_runner.rb +105 -92
- data/lib/phronomy/workflow/execution/workflow_settings.rb +28 -0
- data/lib/phronomy/workflow/execution/workflow_task_execution.rb +153 -0
- data/lib/phronomy/workflow/execution/workflow_task_reception.rb +72 -0
- data/lib/phronomy/workflow/execution/workflow_task_resume_error.rb +14 -0
- data/lib/phronomy/workflow/persistence/state_repository.rb +28 -22
- data/lib/phronomy/workflow/persistence/storage_schema.rb +13 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_engine_environment.rb +61 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb +158 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_task_session.rb +76 -0
- data/lib/phronomy/workflow/{execution → runtime_binding}/workflow_terminal_policy.rb +4 -4
- data/lib/phronomy.rb +63 -16
- data/scripts/api_snapshot.rb +47 -15
- data/scripts/check_readme_ruby.rb +24 -29
- data/scripts/check_readme_runnable.rb +92 -81
- data/sig/_private/phronomy/agent/settings.rbs +19 -0
- data/sig/_private/phronomy/configuration/runtime_settings.rbs +20 -0
- data/sig/_private/phronomy/llm_adapter/async/async_client.rbs +10 -0
- data/sig/_private/phronomy/multi_agent/store.rbs +8 -0
- data/sig/_private/phronomy/persistence/storage_boundary.rbs +7 -0
- data/sig/_private/phronomy/tool/settings.rbs +13 -0
- data/sig/_private/phronomy/tracing/settings.rbs +15 -0
- data/sig/_private/phronomy/workflow/execution/workflow_settings.rbs +13 -0
- data/sig/phronomy/agent/admission.rbs +19 -0
- data/sig/phronomy/agent/agent_not_found_error.rbs +6 -0
- data/sig/phronomy/agent/agent_unloaded_error.rbs +6 -0
- data/sig/phronomy/agent/execution_contract.rbs +132 -0
- data/sig/phronomy/agent/execution_environment.rbs +20 -0
- data/sig/phronomy/agent/store.rbs +73 -0
- data/sig/phronomy/agent/stream_callback_error.rbs +8 -0
- data/sig/phronomy/agent.rbs +29 -135
- data/sig/phronomy/common.rbs +7 -0
- data/sig/phronomy/content_store/contracts.rbs +13 -0
- data/sig/phronomy/context.rbs +152 -0
- data/sig/phronomy/embeddings/async/async_client.rbs +9 -0
- data/sig/phronomy/embeddings/base.rbs +23 -0
- data/sig/phronomy/engine/concurrency/timer_bindings.rbs +22 -0
- data/sig/phronomy/engine/control_bindings.rbs +11 -0
- data/sig/phronomy/engine/control_protocols.rbs +23 -0
- data/sig/phronomy/engine/execution_binding.rbs +6 -0
- data/sig/phronomy/engine/execution_receiver.rbs +15 -0
- data/sig/phronomy/engine/observability.rbs +13 -0
- data/sig/phronomy/execution/async_operation.rbs +7 -0
- data/sig/phronomy/execution/blocking.rbs +5 -0
- data/sig/phronomy/execution/concurrency/cancellation_token.rbs +13 -0
- data/sig/phronomy/execution/concurrency/deadline.rbs +10 -0
- data/sig/phronomy/execution/concurrency/result_composition.rbs +7 -0
- data/sig/phronomy/execution/control_protocols.rbs +18 -0
- data/sig/phronomy/execution/errors.rbs +7 -0
- data/sig/phronomy/execution/execution.rbs +41 -0
- data/sig/phronomy/execution/execution_receiver.rbs +40 -0
- data/sig/phronomy/execution/execution_scope_state.rbs +9 -0
- data/sig/phronomy/execution/invocation_context.rbs +18 -0
- data/sig/phronomy/execution/invocation_controls.rbs +13 -0
- data/sig/phronomy/execution/task_result.rbs +29 -0
- data/sig/phronomy/extensions.rbs +1 -21
- data/sig/phronomy/filter/filter_block_error.rbs +6 -0
- data/sig/phronomy/generation/low_confidence_error.rbs +6 -0
- data/sig/phronomy/handoff.rbs +10 -4
- data/sig/phronomy/integrations.rbs +14 -0
- data/sig/phronomy/llm_adapter/backends/ruby_llm.rbs +6 -0
- data/sig/phronomy/llm_adapter/base.rbs +14 -0
- data/sig/phronomy/llm_adapter/usage.rbs +14 -0
- data/sig/phronomy/llm_adapter/values.rbs +43 -0
- data/sig/phronomy/llm_context_window.rbs +10 -0
- data/sig/phronomy/multi_agent/reserved_child_admission.rbs +8 -0
- data/sig/phronomy/multi_agent/store.rbs +54 -0
- data/sig/phronomy/multi_agent/team_busy_error.rbs +8 -0
- data/sig/phronomy/multi_agent/team_not_found_error.rbs +8 -0
- data/sig/phronomy/multi_agent.rbs +6 -3
- data/sig/phronomy/persistence/errors.rbs +18 -0
- data/sig/phronomy/persistence/migration.rbs +13 -0
- data/sig/phronomy/persistence/transaction.rbs +19 -0
- data/sig/phronomy/persistence.rbs +1 -99
- data/sig/phronomy/persistence_composition.rbs +19 -0
- data/sig/phronomy/runtime_composition/configuration.rbs +36 -0
- data/sig/phronomy/storage/async/async_client.rbs +8 -0
- data/sig/phronomy/storage/backends/in_memory.rbs +9 -0
- data/sig/phronomy/{storage.rbs → storage/contracts.rbs} +4 -18
- data/sig/phronomy/tool.rbs +48 -4
- data/sig/phronomy/tracing/observation.rbs +7 -0
- data/sig/phronomy/vector_store/async/async_client.rbs +13 -0
- data/sig/phronomy/vector_store/base.rbs +23 -0
- data/sig/phronomy/workflow/execution/workflow_execution_environment.rbs +15 -0
- data/sig/phronomy/workflow/runtime_binding/workflow_terminal_policy.rbs +7 -0
- data/sig/phronomy/workflow/store.rbs +8 -0
- data/sig/phronomy/workflow.rbs +5 -1
- data/sig/phronomy.rbs +4 -49
- metadata +365 -117
- data/lib/phronomy/agent/concerns/before_llm_input.rb +0 -66
- data/lib/phronomy/agent/concerns/error_translation.rb +0 -45
- data/lib/phronomy/agent/concerns/filterable.rb +0 -184
- data/lib/phronomy/agent/context/capability/base.rb +0 -548
- data/lib/phronomy/agent/context/capability/tool_executor.rb +0 -62
- data/lib/phronomy/agent/context/instruction/prompt_template.rb +0 -102
- data/lib/phronomy/agent/context_assembly/runtime_chat_builder.rb +0 -36
- data/lib/phronomy/agent/context_assembly/token_budget_resolver.rb +0 -22
- data/lib/phronomy/agent/context_parts/validators/final_budget_validator.rb +0 -37
- data/lib/phronomy/agent/context_policies/default.rb +0 -142
- data/lib/phronomy/agent/execution/phase_machine_builder.rb +0 -101
- data/lib/phronomy/agent/execution/provider_call_outcome.rb +0 -106
- data/lib/phronomy/agent/handoff/handoff_execution_coordinator.rb +0 -15
- data/lib/phronomy/agent/handoff/handoff_outcome_committer.rb +0 -131
- data/lib/phronomy/agent/handoff/handoff_request.rb +0 -45
- data/lib/phronomy/agent/lifecycle_contract/handoff_error.rb +0 -7
- data/lib/phronomy/agent/persistence/handoff_state_repository.rb +0 -58
- data/lib/phronomy/agent/persistence/queries.rb +0 -61
- data/lib/phronomy/agent/selection/candidate.rb +0 -53
- data/lib/phronomy/agent/selection/constraint.rb +0 -49
- data/lib/phronomy/agent/tool_execution/tool_call_intercepted.rb +0 -29
- data/lib/phronomy/agent/tool_execution/tool_invocation_session_builder.rb +0 -276
- data/lib/phronomy/configuration/configuration.rb +0 -68
- data/lib/phronomy/configuration/global_configuration.rb +0 -26
- data/lib/phronomy/engine/concurrency/cancellation_scope.rb +0 -124
- data/lib/phronomy/engine/concurrency/operation_binding.rb +0 -43
- data/lib/phronomy/engine/execution_receiver.rb +0 -65
- data/lib/phronomy/engine/runnable.rb +0 -54
- data/lib/phronomy/llm_adapter/ruby_llm.rb +0 -47
- data/lib/phronomy/llm_contract/token_usage.rb +0 -85
- data/lib/phronomy/multi_agent/persistence/queries.rb +0 -30
- data/lib/phronomy/multi_agent/storage_contract/team_storage_schema.rb +0 -15
- data/lib/phronomy/output_parser/contract/parse_error.rb +0 -7
- data/lib/phronomy/persistence/api/persistence.rb +0 -140
- data/lib/phronomy/persistence_composition/repositories.rb +0 -77
- data/lib/phronomy/recovery/recovery.rb +0 -186
- data/lib/phronomy/tool/contract/tool_error.rb +0 -7
- data/lib/phronomy/vector_store/embeddings/base.rb +0 -44
- data/lib/phronomy/vector_store/embeddings/ruby_llm_embeddings.rb +0 -47
- data/lib/phronomy/workflow/phase_machine_builder.rb +0 -205
- data/lib/phronomy/workflow/storage_contract/workflow_storage_schema.rb +0 -9
- data/sig/phronomy/execution_receiver.rbs +0 -34
- data/sig/phronomy/llm_adapter.rbs +0 -11
- data/sig/phronomy/runtime.rbs +0 -76
- /data/lib/phronomy/agent/{context_assembly/context_importer.rb → context_importer.rb} +0 -0
- /data/lib/phronomy/{engine → execution}/backpressure_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/cancellation_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/concurrency/worker_input_restricted.rb +0 -0
- /data/lib/phronomy/{engine → execution}/event_loop_reentrancy_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/invalid_async_entry_action_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/invalid_async_transition_action_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/invalid_async_workflow_action_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/pool_shutdown_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/recursion_limit_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/runtime_shutdown_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/runtime_shutdown_reentrancy_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/scheduler_reentrancy_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/timeout_error.rb +0 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Agent
|
|
5
|
+
# Process-local execution connection selected by composition. Agent owns
|
|
6
|
+
# admission, progress, approval, durable outcome and recovery decisions;
|
|
7
|
+
# this extension supplies serial delivery, session routing and resources.
|
|
8
|
+
# Environments and session handles are never persistence authority.
|
|
9
|
+
# @api private
|
|
10
|
+
class ExecutionEnvironment
|
|
11
|
+
class << self
|
|
12
|
+
def install_provider(provider)
|
|
13
|
+
raise ArgumentError, "Agent execution provider must be callable" unless provider.respond_to?(:call)
|
|
14
|
+
@provider = provider
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def current
|
|
18
|
+
@provider&.call || raise(Phronomy::ConfigurationError, "Agent execution environment has not been configured")
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def registry = raise(NotImplementedError)
|
|
23
|
+
def existing_registry = raise(NotImplementedError)
|
|
24
|
+
def ownership = raise(NotImplementedError)
|
|
25
|
+
def existing_ownership = raise(NotImplementedError)
|
|
26
|
+
def executing? = raise(NotImplementedError)
|
|
27
|
+
def session_phase(id) = raise(NotImplementedError)
|
|
28
|
+
def submit(**options, &operation) = raise(NotImplementedError)
|
|
29
|
+
def submit_authorization(timeout:, cancellation_token:, &operation) = raise(NotImplementedError)
|
|
30
|
+
def build_llm_client(adapter:) = raise(NotImplementedError)
|
|
31
|
+
def build_agent_session(invocation:, resume_event: nil, resume_phase: nil) = raise(NotImplementedError)
|
|
32
|
+
def build_tool_session(invocation:, parent_sink:, resume_event: nil, resume_phase: nil) = raise(NotImplementedError)
|
|
33
|
+
def register_session(session, completion:) = raise(NotImplementedError)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Agent
|
|
5
|
+
# An opaque state reference owned and interpreted by its bound participant.
|
|
6
|
+
# @api public
|
|
7
|
+
ExecutionExtensionState = Data.define(:binding_key, :binding_version, :state_ref) do
|
|
8
|
+
def initialize(binding_key:, binding_version:, state_ref: nil)
|
|
9
|
+
raise ArgumentError, "binding_key is required" if binding_key.to_s.empty?
|
|
10
|
+
raise ArgumentError, "binding_version must be positive" unless binding_version.is_a?(Integer) && binding_version.positive?
|
|
11
|
+
super(binding_key: binding_key.to_s.freeze, binding_version: binding_version, state_ref: state_ref&.to_s&.freeze)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def to_h
|
|
15
|
+
{"binding_key" => binding_key, "binding_version" => binding_version, "state_ref" => state_ref}.freeze
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def self.from_h(value) = new(**value.transform_keys(&:to_sym))
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Agent
|
|
5
|
+
# Stable domain observation; does not expose repositories or FSM phases.
|
|
6
|
+
# @api public
|
|
7
|
+
ExecutionObservation = Data.define(:agent_id, :execution_id, :status, :result,
|
|
8
|
+
:error, :reservation, :transfer_receipt, :cancellation_requested) do
|
|
9
|
+
def initialize(**values)
|
|
10
|
+
super(**values.transform_values { |value| Phronomy::Values::Immutable.copy(value) })
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.from_result(value)
|
|
14
|
+
new(agent_id: value.fetch(:agent_id), execution_id: value.fetch(:execution_id), status: value.fetch(:status),
|
|
15
|
+
result: value[:result], error: value[:error], reservation: nil, transfer_receipt: nil, cancellation_requested: false)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def absent? = status == :absent
|
|
19
|
+
def terminal? = !%i[absent preparing active suspended].include?(status)
|
|
20
|
+
def continuable? = %i[preparing active].include?(status) && !cancellation_requested
|
|
21
|
+
def active? = %i[preparing active suspended].include?(status)
|
|
22
|
+
|
|
23
|
+
def value!
|
|
24
|
+
raise Phronomy::Persistence::NotFoundError, "Execution #{execution_id} is absent" if absent?
|
|
25
|
+
raise RecoverySupport.error_from_failure(error) if error
|
|
26
|
+
to_result
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def to_result
|
|
30
|
+
Phronomy::Values::Immutable.copy(execution_id: execution_id, agent_id: agent_id,
|
|
31
|
+
status: status, result: result, output: result, error: error)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Agent
|
|
5
|
+
# Adds input, output, and tool-result filter support to an agent.
|
|
6
|
+
#
|
|
7
|
+
# Filters transform (or block) values at three call sites:
|
|
8
|
+
# - *input* — the raw user input string, before the LLM is called
|
|
9
|
+
# - *output* — the final LLM output string, before it is returned
|
|
10
|
+
# - *tool result* — the return value of each tool call
|
|
11
|
+
#
|
|
12
|
+
# Each filter in the chain receives the value returned by the previous one.
|
|
13
|
+
# A {Phronomy::FilterBlockError} raised inside any filter propagates to the
|
|
14
|
+
# caller.
|
|
15
|
+
#
|
|
16
|
+
# @api private
|
|
17
|
+
module Filterable
|
|
18
|
+
def self.included(base)
|
|
19
|
+
base.extend(ClassMethods)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Class-level DSL mixed into the including agent class.
|
|
23
|
+
module ClassMethods
|
|
24
|
+
# Registers a filter applied to every invocation's user input.
|
|
25
|
+
# Accepts either a {Phronomy::Filter::Base} instance or a subclass;
|
|
26
|
+
# when a class is given it is instantiated with +.new+.
|
|
27
|
+
# @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>]
|
|
28
|
+
# @return [void]
|
|
29
|
+
# @api public
|
|
30
|
+
def input_filter(filter)
|
|
31
|
+
@_class_input_filters ||= []
|
|
32
|
+
@_class_input_filters << _resolve_filter(filter)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Registers a filter applied to every invocation's final LLM output.
|
|
36
|
+
# Accepts either a {Phronomy::Filter::Base} instance or a subclass;
|
|
37
|
+
# when a class is given it is instantiated with +.new+.
|
|
38
|
+
# @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>]
|
|
39
|
+
# @return [void]
|
|
40
|
+
# @api public
|
|
41
|
+
def output_filter(filter)
|
|
42
|
+
@_class_output_filters ||= []
|
|
43
|
+
@_class_output_filters << _resolve_filter(filter)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Registers a filter applied to every tool result for all tools.
|
|
47
|
+
# Accepts either a {Phronomy::Filter::Base} instance or a subclass;
|
|
48
|
+
# when a class is given it is instantiated with +.new+.
|
|
49
|
+
# @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>]
|
|
50
|
+
# @return [void]
|
|
51
|
+
# @api public
|
|
52
|
+
def tool_result_filter(filter)
|
|
53
|
+
@_class_tool_result_filters ||= []
|
|
54
|
+
@_class_tool_result_filters << _resolve_filter(filter)
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# @return [Array<Phronomy::Filter::Base>]
|
|
58
|
+
# @api private
|
|
59
|
+
def _class_input_filters
|
|
60
|
+
@_class_input_filters || []
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# @return [Array<Phronomy::Filter::Base>]
|
|
64
|
+
# @api private
|
|
65
|
+
def _class_output_filters
|
|
66
|
+
@_class_output_filters || []
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# @return [Array<Phronomy::Filter::Base>]
|
|
70
|
+
# @api private
|
|
71
|
+
def _class_tool_result_filters
|
|
72
|
+
@_class_tool_result_filters || []
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
private
|
|
76
|
+
|
|
77
|
+
# Coerce +filter+ to an instance: if a Class is passed, call +.new+;
|
|
78
|
+
# otherwise return the object as-is.
|
|
79
|
+
# @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>]
|
|
80
|
+
# @return [Phronomy::Filter::Base]
|
|
81
|
+
# @api private
|
|
82
|
+
def _resolve_filter(filter)
|
|
83
|
+
filter.is_a?(Class) ? filter.new : filter
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Registers an input filter on this instance.
|
|
88
|
+
# Accepts either a {Phronomy::Filter::Base} instance or a subclass;
|
|
89
|
+
# when a class is given it is instantiated with +.new+.
|
|
90
|
+
# Runs in addition to any class-level input filters.
|
|
91
|
+
# @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>]
|
|
92
|
+
# @return [self]
|
|
93
|
+
# @api public
|
|
94
|
+
def add_input_filter(filter)
|
|
95
|
+
__with_operation do
|
|
96
|
+
@_instance_input_filters ||= []
|
|
97
|
+
@_instance_input_filters << _resolve_filter(filter)
|
|
98
|
+
self
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Registers an output filter on this instance.
|
|
103
|
+
# Accepts either a {Phronomy::Filter::Base} instance or a subclass;
|
|
104
|
+
# when a class is given it is instantiated with +.new+.
|
|
105
|
+
# Runs in addition to any class-level output filters.
|
|
106
|
+
# @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>]
|
|
107
|
+
# @return [self]
|
|
108
|
+
# @api public
|
|
109
|
+
def add_output_filter(filter)
|
|
110
|
+
__with_operation do
|
|
111
|
+
@_instance_output_filters ||= []
|
|
112
|
+
@_instance_output_filters << _resolve_filter(filter)
|
|
113
|
+
self
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Registers a tool result filter on this instance.
|
|
118
|
+
#
|
|
119
|
+
# When called with two arguments, the filter is scoped to the given tool
|
|
120
|
+
# class only. When called with one argument, it applies to all tools.
|
|
121
|
+
# Accepts either a {Phronomy::Filter::Base} instance or a subclass;
|
|
122
|
+
# when a class is given it is instantiated with +.new+.
|
|
123
|
+
#
|
|
124
|
+
# @overload add_tool_result_filter(filter)
|
|
125
|
+
# @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>] applied to every tool
|
|
126
|
+
# @overload add_tool_result_filter(tool_class, filter)
|
|
127
|
+
# @param tool_class [Class] scope the filter to this tool
|
|
128
|
+
# @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>]
|
|
129
|
+
# @return [self]
|
|
130
|
+
# @api public
|
|
131
|
+
def add_tool_result_filter(tool_class_or_filter, filter = nil)
|
|
132
|
+
__with_operation do
|
|
133
|
+
if filter.nil?
|
|
134
|
+
# Single-argument form: apply to all tools.
|
|
135
|
+
@_instance_tool_result_filters ||= []
|
|
136
|
+
@_instance_tool_result_filters << _resolve_filter(tool_class_or_filter)
|
|
137
|
+
else
|
|
138
|
+
# Two-argument form: scoped to one tool class.
|
|
139
|
+
@_scoped_tool_result_filters ||= {}
|
|
140
|
+
(@_scoped_tool_result_filters[tool_class_or_filter] ||= []) << _resolve_filter(filter)
|
|
141
|
+
end
|
|
142
|
+
self
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
private
|
|
147
|
+
|
|
148
|
+
# Run input filters (class-level then instance-level).
|
|
149
|
+
# @param input [String, Hash] the raw user input
|
|
150
|
+
# @return [String, Hash] the (possibly transformed) input
|
|
151
|
+
# @api private
|
|
152
|
+
def run_input_filters!(input)
|
|
153
|
+
class_filters = self.class._class_input_filters
|
|
154
|
+
inst_filters = @_instance_input_filters || []
|
|
155
|
+
(class_filters + inst_filters).inject(input) { |val, f| f.call(val) }
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Run output filters (class-level then instance-level).
|
|
159
|
+
# @param output [String] the LLM output
|
|
160
|
+
# @return [String] the (possibly transformed) output
|
|
161
|
+
# @api private
|
|
162
|
+
def run_output_filters!(output)
|
|
163
|
+
class_filters = self.class._class_output_filters
|
|
164
|
+
inst_filters = @_instance_output_filters || []
|
|
165
|
+
(class_filters + inst_filters).inject(output) { |val, f| f.call(val) }
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Collect all tool-result filters (global + scoped) for a given tool class.
|
|
169
|
+
# @param tool_class [Class]
|
|
170
|
+
# @return [Array<Phronomy::Filter::Base>]
|
|
171
|
+
# @api private
|
|
172
|
+
def _tool_result_filters_for(tool_class)
|
|
173
|
+
global = self.class._class_tool_result_filters + (@_instance_tool_result_filters || [])
|
|
174
|
+
scoped = (@_scoped_tool_result_filters || {})[tool_class] || []
|
|
175
|
+
global + scoped
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Coerce +filter+ to an instance: if a Class is passed, call +.new+;
|
|
179
|
+
# otherwise return the object as-is.
|
|
180
|
+
# @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>]
|
|
181
|
+
# @return [Phronomy::Filter::Base]
|
|
182
|
+
# @api private
|
|
183
|
+
def _resolve_filter(filter)
|
|
184
|
+
filter.is_a?(Class) ? filter.new : filter
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module Phronomy
|
|
4
4
|
module Agent
|
|
5
|
-
class
|
|
5
|
+
class TransferContext
|
|
6
6
|
Provenance = Data.define(
|
|
7
7
|
:origin_agent_id,
|
|
8
8
|
:origin_record_id,
|
|
@@ -59,7 +59,7 @@ module Phronomy
|
|
|
59
59
|
def initialize(**values)
|
|
60
60
|
provenance = values.fetch(:provenance)
|
|
61
61
|
unless provenance.is_a?(Provenance)
|
|
62
|
-
raise ArgumentError, "
|
|
62
|
+
raise ArgumentError, "TransferContext::Item provenance must be Provenance"
|
|
63
63
|
end
|
|
64
64
|
format = (values[:content_format] || :text).to_sym
|
|
65
65
|
unless %i[text json].include?(format)
|
|
@@ -96,7 +96,7 @@ module Phronomy
|
|
|
96
96
|
|
|
97
97
|
def self.from_h(value)
|
|
98
98
|
source = value.transform_keys(&:to_s)
|
|
99
|
-
raise ArgumentError, "
|
|
99
|
+
raise ArgumentError, "TransferContext schema mismatch" unless source.keys.sort == %w[items responsibility]
|
|
100
100
|
new(responsibility: source.fetch("responsibility"), items: source.fetch("items").map do |raw|
|
|
101
101
|
values = raw.transform_keys(&:to_sym)
|
|
102
102
|
values[:provenance] = Provenance.new(**values.fetch(:provenance).transform_keys(&:to_sym))
|
|
@@ -111,7 +111,7 @@ module Phronomy
|
|
|
111
111
|
@items = Array(items).freeze
|
|
112
112
|
raise ArgumentError, "Handoff Context responsibility must not be empty" if @responsibility.strip.empty?
|
|
113
113
|
unless @items.all? { |item| item.is_a?(Item) }
|
|
114
|
-
raise ArgumentError, "Handoff Context items must be
|
|
114
|
+
raise ArgumentError, "Handoff Context items must be TransferContext::Item values"
|
|
115
115
|
end
|
|
116
116
|
freeze
|
|
117
117
|
end
|
|
@@ -2,32 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
module Phronomy
|
|
4
4
|
module Agent
|
|
5
|
-
class
|
|
5
|
+
class TransferProjection
|
|
6
6
|
CONTROL_CATEGORIES = %i[instruction handoff_responsibility].freeze
|
|
7
7
|
JSON_CATEGORIES = %i[assistant_message tool_message].freeze
|
|
8
8
|
|
|
9
9
|
Identity = Data.define(:agent_id)
|
|
10
|
-
|
|
11
|
-
Snapshot = Data.define(:handoff, :responsibility, :selection_intent)
|
|
12
|
-
private_constant :Identity, :Edge, :Snapshot
|
|
13
|
-
|
|
14
|
-
# A terminal command contains values only, captured on EventLoop.
|
|
15
|
-
def build_terminal(view:, manifest:, persistence:, source_agent_id:)
|
|
16
|
-
request = Snapshot.new(
|
|
17
|
-
handoff: Edge.new(policy: HandoffPolicy.from_h(view.policy), target_agent: Identity.new(agent_id: view.target_agent_id)),
|
|
18
|
-
responsibility: view.responsibility,
|
|
19
|
-
selection_intent: view.selection_intent.transform_keys(&:to_sym)
|
|
20
|
-
)
|
|
21
|
-
build(request: request, manifest: manifest, persistence: persistence,
|
|
22
|
-
source_agent: Identity.new(agent_id: source_agent_id))
|
|
23
|
-
end
|
|
10
|
+
private_constant :Identity
|
|
24
11
|
|
|
25
12
|
def build(request:, manifest:, persistence:, source_agent:)
|
|
26
|
-
unless request.is_a?(
|
|
27
|
-
raise ArgumentError, "request must be
|
|
13
|
+
unless request.is_a?(ControlRequest)
|
|
14
|
+
raise ArgumentError, "request must be an Agent::ControlRequest"
|
|
28
15
|
end
|
|
29
|
-
unless manifest.is_a?(Phronomy::
|
|
30
|
-
raise ArgumentError, "manifest must be an LLMInputManifest"
|
|
16
|
+
unless manifest.is_a?(Phronomy::Context::LLMInputManifest)
|
|
17
|
+
raise ArgumentError, "manifest must be an Phronomy::Context::LLMInputManifest"
|
|
31
18
|
end
|
|
32
19
|
|
|
33
20
|
groups = project_visible_groups(manifest)
|
|
@@ -42,12 +29,12 @@ module Phronomy
|
|
|
42
29
|
policy_category: group.fetch(:policy_category),
|
|
43
30
|
persistence: persistence,
|
|
44
31
|
source_agent: source_agent,
|
|
45
|
-
target_agent: request.
|
|
32
|
+
target_agent: Identity.new(agent_id: request.target_agent_id)
|
|
46
33
|
)
|
|
47
34
|
end
|
|
48
35
|
end
|
|
49
36
|
|
|
50
|
-
|
|
37
|
+
TransferContext.new(
|
|
51
38
|
responsibility: request.responsibility,
|
|
52
39
|
items: items
|
|
53
40
|
)
|
|
@@ -116,7 +103,7 @@ module Phronomy
|
|
|
116
103
|
end
|
|
117
104
|
|
|
118
105
|
# Compatibility for finalized pre-ACS-04 manifests. New manifests use
|
|
119
|
-
# context_policy_conversation_group_id and do not recreate
|
|
106
|
+
# context_policy_conversation_group_id and do not recreate Phronomy::Context::Unit.
|
|
120
107
|
unit_id = segment.metadata["selection_unit_id"] ||
|
|
121
108
|
segment.metadata[:selection_unit_id]
|
|
122
109
|
return "unit:#{unit_id}" if unit_id
|
|
@@ -125,13 +112,7 @@ module Phronomy
|
|
|
125
112
|
end
|
|
126
113
|
|
|
127
114
|
def include_group?(request, category)
|
|
128
|
-
|
|
129
|
-
return true if policy.required?(category)
|
|
130
|
-
return false if policy.forbidden?(category)
|
|
131
|
-
|
|
132
|
-
request.selection_intent.fetch(category) do
|
|
133
|
-
policy.default_include?(category)
|
|
134
|
-
end
|
|
115
|
+
request.selection.fetch(category, false)
|
|
135
116
|
end
|
|
136
117
|
|
|
137
118
|
def materialize_item(segment, policy_category:, persistence:, source_agent:, target_agent:)
|
|
@@ -147,7 +128,7 @@ module Phronomy
|
|
|
147
128
|
target_agent: target_agent
|
|
148
129
|
)
|
|
149
130
|
|
|
150
|
-
|
|
131
|
+
TransferContext::Item.new(
|
|
151
132
|
candidate_category: category,
|
|
152
133
|
policy_category: policy_category,
|
|
153
134
|
role: segment.role,
|
|
@@ -169,7 +150,7 @@ module Phronomy
|
|
|
169
150
|
metadata[:context_policy_content_format]
|
|
170
151
|
if explicit
|
|
171
152
|
format = explicit.to_sym
|
|
172
|
-
unless Phronomy::
|
|
153
|
+
unless Phronomy::Context::ContextPolicyInput::CONTENT_FORMATS.include?(format)
|
|
173
154
|
raise Phronomy::HandoffError,
|
|
174
155
|
"unsupported Context content format in Manifest: #{explicit.inspect}"
|
|
175
156
|
end
|
|
@@ -182,7 +163,7 @@ module Phronomy
|
|
|
182
163
|
def provenance_for(metadata, segment:, source_agent:, target_agent:)
|
|
183
164
|
inherited = metadata["handoff_provenance"]
|
|
184
165
|
if inherited
|
|
185
|
-
base =
|
|
166
|
+
base = TransferContext::Provenance.new(
|
|
186
167
|
origin_agent_id: inherited["origin_agent_id"] || inherited[:origin_agent_id],
|
|
187
168
|
origin_record_id: inherited["origin_record_id"] || inherited[:origin_record_id],
|
|
188
169
|
origin_execution_id: inherited["origin_execution_id"] || inherited[:origin_execution_id],
|
|
@@ -193,7 +174,7 @@ module Phronomy
|
|
|
193
174
|
return base.forwarded_to(target_agent.agent_id)
|
|
194
175
|
end
|
|
195
176
|
|
|
196
|
-
|
|
177
|
+
TransferContext::Provenance.new(
|
|
197
178
|
origin_agent_id: metadata["source_agent_id"] || source_agent.agent_id,
|
|
198
179
|
origin_record_id: metadata["journal_record_id"],
|
|
199
180
|
origin_execution_id: metadata["source_execution_id"],
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Agent
|
|
5
|
+
# Materialized knowledge accepted by create(knowledge:) without internal DTOs.
|
|
6
|
+
# @api public
|
|
7
|
+
KnowledgeItem = Data.define(:content, :metadata) do
|
|
8
|
+
def initialize(content:, metadata: {})
|
|
9
|
+
super(content: String(content).dup.freeze, metadata: Phronomy::Values::Immutable.copy(metadata))
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Agent
|
|
5
|
+
# Public admission boundaries. The explicit operation capability belongs to
|
|
6
|
+
# C04, including queued work and domain completion after logical cancellation.
|
|
7
|
+
# @api private
|
|
8
|
+
module LifetimeApi
|
|
9
|
+
# Releases only the Runtime incarnation; durable state and Store stay intact.
|
|
10
|
+
# @return [TaskResult<Boolean>]
|
|
11
|
+
# @api public
|
|
12
|
+
def unload_async
|
|
13
|
+
return Phronomy::TaskResult.completed(false) if @_phronomy_runtime_owner_state == :unloaded
|
|
14
|
+
|
|
15
|
+
__assert_agent_accessible!
|
|
16
|
+
transition, start = __execution_environment.ownership.reserve_unload(self)
|
|
17
|
+
start ? transition.start : transition.result
|
|
18
|
+
rescue => error
|
|
19
|
+
Phronomy::TaskResult.failed(error)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# A timeout stops this wait, never the accepted release.
|
|
23
|
+
# @return [Boolean]
|
|
24
|
+
# @api public
|
|
25
|
+
def unload!(timeout: nil)
|
|
26
|
+
_check_event_loop_reentrancy(:unload!, :unload_async)
|
|
27
|
+
unload_async.wait_result(timeout: timeout)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def __with_operation(async: false, read_only: false)
|
|
33
|
+
lease = __acquire_operation!(read_only: read_only)
|
|
34
|
+
followed = false
|
|
35
|
+
value = yield
|
|
36
|
+
if async && lease
|
|
37
|
+
lease.follow(value)
|
|
38
|
+
followed = true
|
|
39
|
+
end
|
|
40
|
+
value
|
|
41
|
+
rescue Phronomy::AgentUnloadedError, Phronomy::AgentBusyError, Phronomy::RuntimeShutdownError => error
|
|
42
|
+
raise unless async
|
|
43
|
+
Phronomy::TaskResult.failed(error)
|
|
44
|
+
ensure
|
|
45
|
+
lease&.release unless followed
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Accepted C04 commands carry their admission/operation capability. Their
|
|
49
|
+
# continuations read the snapshot without entering the public admission gate.
|
|
50
|
+
def __root_for_operation
|
|
51
|
+
__assert_agent_accessible!
|
|
52
|
+
@root
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def __acquire_operation!(read_only: false)
|
|
56
|
+
__assert_agent_accessible!
|
|
57
|
+
return nil if @_phronomy_runtime_owner_state == :constructing
|
|
58
|
+
|
|
59
|
+
__execution_environment.ownership.acquire_operation(self, read_only: read_only)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def __mark_unloaded!
|
|
63
|
+
@_phronomy_runtime_owner_state = :unloaded
|
|
64
|
+
@root = nil
|
|
65
|
+
@_phronomy_journal_records = [].freeze
|
|
66
|
+
@_phronomy_event_listener = nil
|
|
67
|
+
@_phronomy_execution_wiring = {}.freeze
|
|
68
|
+
@execution_coordinator = nil
|
|
69
|
+
@tool_approval_policy = @before_llm_input = nil
|
|
70
|
+
@_instance_input_filters = @_instance_output_filters = nil
|
|
71
|
+
@_instance_tool_result_filters = @_scoped_tool_result_filters = nil
|
|
72
|
+
@initial_retention = nil
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Agent
|
|
5
|
+
# C04 capability for accepted work. Never inferred from the current thread.
|
|
6
|
+
# @api private
|
|
7
|
+
class OperationLease
|
|
8
|
+
def initialize(registry, agent, token)
|
|
9
|
+
@registry, @agent, @token = registry, agent, token
|
|
10
|
+
@mutex = Mutex.new
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def release
|
|
14
|
+
ownership = @mutex.synchronize do
|
|
15
|
+
next unless @registry
|
|
16
|
+
current = [@registry, @agent, @token]
|
|
17
|
+
@registry = @agent = @token = nil
|
|
18
|
+
current
|
|
19
|
+
end
|
|
20
|
+
ownership&.first&.release_operation(ownership[1], ownership[2])
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def follow(task)
|
|
24
|
+
if task.respond_to?(:on_physical_complete)
|
|
25
|
+
task.on_physical_complete { release }
|
|
26
|
+
else
|
|
27
|
+
task.on_complete { |_value, _error| release }
|
|
28
|
+
end
|
|
29
|
+
task
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Agent
|
|
5
|
+
# Logical cancellation of a public handle does not release Agent ownership.
|
|
6
|
+
# Only the domain runner acknowledges the end of its accepted operation.
|
|
7
|
+
# @api private
|
|
8
|
+
class OperationResult < Phronomy::Concurrency::PhysicalCompletionTask
|
|
9
|
+
def initialize(agent:, name:)
|
|
10
|
+
super(name: name)
|
|
11
|
+
@operation_lease = agent.send(:__acquire_operation!)
|
|
12
|
+
@lifetime_mutex = Mutex.new
|
|
13
|
+
@work = 0
|
|
14
|
+
@finished = false
|
|
15
|
+
@released = false
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def finish_operation!
|
|
19
|
+
@lifetime_mutex.synchronize { @finished = true }
|
|
20
|
+
release_if_quiet
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def track(task)
|
|
24
|
+
@lifetime_mutex.synchronize { @work += 1 }
|
|
25
|
+
task.on_physical_complete do
|
|
26
|
+
@lifetime_mutex.synchronize { @work -= 1 }
|
|
27
|
+
release_if_quiet
|
|
28
|
+
end
|
|
29
|
+
task
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def settle(value = nil, error: nil)
|
|
33
|
+
finish_operation!
|
|
34
|
+
error ? fail(error) : complete(value)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
private
|
|
38
|
+
|
|
39
|
+
def release_if_quiet
|
|
40
|
+
released = @lifetime_mutex.synchronize do
|
|
41
|
+
next unless @finished && @work.zero? && !@released
|
|
42
|
+
@released = true
|
|
43
|
+
current = @operation_lease
|
|
44
|
+
@operation_lease = nil
|
|
45
|
+
[current]
|
|
46
|
+
end
|
|
47
|
+
return unless released
|
|
48
|
+
released.first&.release
|
|
49
|
+
mark_physical_complete!
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|