phronomy 0.27.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.mutant.yml +9 -9
- data/CHANGELOG.md +52 -0
- data/CONTRIBUTING.md +8 -8
- data/README.md +9 -3
- data/benchmark/bench_agent_invoke.rb +25 -50
- data/benchmark/bench_context_assembler.rb +14 -11
- data/benchmark/bench_regression.rb +1 -1
- data/benchmark/bench_tool_schema.rb +1 -1
- data/docs/architecture/agent-chat-and-state-ownership.md +7 -1
- data/docs/architecture/agent-transition-ownership.md +13 -89
- data/docs/architecture/backend-groups.md +152 -0
- data/docs/architecture/before-llm-input.md +3 -3
- data/docs/architecture/context-management.md +1 -1
- data/docs/architecture/execution-abstraction-review.md +88 -0
- data/docs/architecture/execution-boundaries.md +28 -0
- data/docs/architecture/execution-metadata-and-values.md +6 -0
- data/docs/architecture/multi-agent-handoff.md +6 -5
- data/docs/architecture/persistence.md +27 -9
- data/docs/architecture/r8-unit1.md +104 -0
- data/docs/architecture/r8-unit10.md +72 -0
- data/docs/architecture/r8-unit11.md +71 -0
- data/docs/architecture/r8-unit12.md +67 -0
- data/docs/architecture/r8-unit13.md +61 -0
- data/docs/architecture/r8-unit14.md +72 -0
- data/docs/architecture/r8-unit15.md +65 -0
- data/docs/architecture/r8-unit16.md +54 -0
- data/docs/architecture/r8-unit17.md +73 -0
- data/docs/architecture/r8-unit18.md +92 -0
- data/docs/architecture/r8-unit19.md +61 -0
- data/docs/architecture/r8-unit2.md +55 -0
- data/docs/architecture/r8-unit20.md +51 -0
- data/docs/architecture/r8-unit21.md +79 -0
- data/docs/architecture/r8-unit22.md +74 -0
- data/docs/architecture/r8-unit23.md +81 -0
- data/docs/architecture/r8-unit3.md +130 -0
- data/docs/architecture/r8-unit4.md +79 -0
- data/docs/architecture/r8-unit5.md +115 -0
- data/docs/architecture/r8-unit6.md +71 -0
- data/docs/architecture/r8-unit7.md +70 -0
- data/docs/architecture/r8-unit8.md +78 -0
- data/docs/architecture/r8-unit9.md +73 -0
- data/docs/architecture/refactoring-closure.md +10 -1
- data/docs/architecture/remaining-refactoring-plan.md +16 -0
- data/docs/architecture/storage-execution-boundaries.md +54 -0
- data/docs/architecture/tool-schema-recording-gap.md +6 -0
- data/docs/architecture/tracing.md +1 -1
- data/docs/architecture/workflow-terminal-ownership-design.md +2 -2
- data/docs/architecture.md +24 -11
- data/docs/decisions/015-tool-public-facade-and-rbs-boundary.md +3 -3
- data/docs/decisions/027-llm-adapter-provider-boundary.md +2 -0
- data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +2 -0
- data/docs/decisions/031-durable-multi-agent-coordination.md +2 -0
- data/docs/decisions/032-storage-backend-composition.md +5 -0
- data/docs/decisions/034-handoff-runner-coordination-ownership.md +2 -0
- data/docs/decisions/035-tool-executor-capability-ownership.md +1 -1
- data/docs/decisions/038-responsibility-based-source-layout.md +14 -0
- data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +8 -0
- data/docs/decisions/040-configuration-default-composition.md +12 -1
- data/docs/decisions/045-worker-input-restriction-ownership.md +4 -0
- data/docs/decisions/051-execution-outcome-worker-ownership.md +2 -0
- data/docs/decisions/056-workflow-terminal-policy-ownership.md +1 -1
- data/docs/decisions/058-neutral-storage-primitives.md +5 -0
- data/docs/decisions/059-backend-contracts-and-async-clients.md +74 -0
- data/docs/decisions/060-runtime-settings-and-application-configuration.md +86 -0
- data/docs/decisions/061-persistence-failure-contracts.md +89 -0
- data/docs/decisions/062-content-store-service.md +52 -0
- data/docs/decisions/063-execution-contracts-and-services.md +88 -0
- data/docs/decisions/064-agent-execution-and-multi-agent-responsibility.md +36 -0
- data/docs/decisions/065-vector-and-embeddings-common-operations.md +38 -0
- data/docs/decisions/066-llm-and-tool-operation-contracts.md +20 -0
- data/docs/decisions/067-agent-progress-and-engine-connection.md +29 -0
- data/docs/decisions/068-agent-tool-child-progress-and-tool-operation.md +36 -0
- data/docs/decisions/README.md +18 -1
- data/docs/execution-boundaries-preview.md +45 -0
- data/docs/features.md +8 -3
- data/docs/getting-started.md +9 -6
- data/docs/migrations/0.22.md +2 -2
- data/docs/migrations/durable-semantic-coordination-v2.md +2 -0
- data/docs/migrations/handoff-runner-multi-agent.md +2 -0
- data/docs/migrations/persistence-failure-contracts.md +67 -0
- data/docs/migrations/r8-unit10.md +36 -0
- data/docs/migrations/r8-unit11.md +33 -0
- data/docs/migrations/r8-unit12.md +31 -0
- data/docs/migrations/r8-unit13.md +36 -0
- data/docs/migrations/r8-unit14.md +31 -0
- data/docs/migrations/r8-unit15.md +31 -0
- data/docs/migrations/r8-unit16.md +26 -0
- data/docs/migrations/r8-unit17.md +28 -0
- data/docs/migrations/r8-unit18.md +29 -0
- data/docs/migrations/r8-unit19.md +28 -0
- data/docs/migrations/r8-unit20.md +24 -0
- data/docs/migrations/r8-unit21.md +28 -0
- data/docs/migrations/r8-unit22.md +29 -0
- data/docs/migrations/r8-unit23.md +20 -0
- data/docs/migrations/r8-unit4.md +67 -0
- data/docs/migrations/r8-unit5.md +75 -0
- data/docs/migrations/r8-unit6.md +109 -0
- data/docs/migrations/r8-unit7.md +31 -0
- data/docs/migrations/r8-unit8.md +33 -0
- data/docs/migrations/r8-unit9.md +28 -0
- data/docs/migrations/storage-async-client.md +103 -0
- data/docs/migrations/vector-async-clients.md +110 -0
- data/docs/persistence-backends.md +38 -21
- data/docs/runtime-and-concurrency.md +26 -1
- data/lib/phronomy/agent/admission.rb +78 -0
- data/lib/phronomy/agent/{lifecycle_contract/agent_already_exists_error.rb → agent_already_exists_error.rb} +1 -1
- data/lib/phronomy/agent/{lifecycle_contract/agent_busy_error.rb → agent_busy_error.rb} +1 -1
- data/lib/phronomy/agent/{lifecycle_contract/agent_purged_error.rb → agent_purged_error.rb} +1 -1
- data/lib/phronomy/{recovery → agent/api}/execution_rehydration_required_error.rb +1 -1
- data/lib/phronomy/agent/async_event_api.rb +37 -32
- data/lib/phronomy/agent/base.rb +74 -72
- data/lib/phronomy/agent/before_llm_input.rb +64 -0
- data/lib/phronomy/agent/composition/run_once.rb +1 -1
- data/lib/phronomy/agent/{context_assembly/context_candidate_resolver.rb → context_candidate_resolver.rb} +2 -2
- data/lib/phronomy/agent/{context_assembly/context_assembler.rb → context_preparation.rb} +35 -221
- data/lib/phronomy/agent/control_request.rb +21 -0
- data/lib/phronomy/agent/execution/agent_execution.rb +1 -1
- data/lib/phronomy/agent/execution/agent_invocation.rb +27 -53
- data/lib/phronomy/agent/execution/approval_resume_commit.rb +1 -1
- data/lib/phronomy/agent/execution/dispatch_preparation.rb +17 -127
- data/lib/phronomy/agent/execution/exact_execution.rb +44 -23
- data/lib/phronomy/agent/execution/execution_cancellation.rb +8 -4
- data/lib/phronomy/agent/execution/execution_coordinator.rb +214 -395
- data/lib/phronomy/agent/execution/execution_metadata.rb +1 -0
- data/lib/phronomy/agent/execution/execution_outcome_committer.rb +70 -126
- data/lib/phronomy/agent/execution/execution_registry.rb +6 -2
- data/lib/phronomy/agent/execution/execution_session_runner.rb +22 -27
- data/lib/phronomy/agent/execution/initial_preparation.rb +18 -116
- data/lib/phronomy/agent/execution/{agent_invocation_session_builder.rb → invocation_actions.rb} +54 -194
- data/lib/phronomy/agent/execution/invocation_transitions.rb +39 -1
- data/lib/phronomy/agent/execution/runtime_record_encoder.rb +7 -7
- data/lib/phronomy/agent/execution_change.rb +161 -0
- data/lib/phronomy/agent/execution_environment.rb +36 -0
- data/lib/phronomy/agent/execution_extension_state.rb +21 -0
- data/lib/phronomy/agent/execution_observation.rb +35 -0
- data/lib/phronomy/agent/filterable.rb +182 -0
- data/lib/phronomy/agent/handoff/{handoff_context.rb → transfer_context.rb} +4 -4
- data/lib/phronomy/agent/handoff/{handoff_projection.rb → transfer_projection.rb} +14 -33
- data/lib/phronomy/{llm_contract/transport_error.rb → agent/handoff_error.rb} +1 -1
- data/lib/phronomy/agent/journal/journal_projection.rb +1 -1
- data/lib/phronomy/agent/knowledge_item.rb +13 -0
- data/lib/phronomy/agent/lifecycle/agent_root.rb +1 -1
- data/lib/phronomy/agent/lifecycle/ownership_registry.rb +18 -23
- data/lib/phronomy/agent/persistence/agent_repository.rb +29 -21
- data/lib/phronomy/agent/persistence/cancellation_repository.rb +51 -0
- data/lib/phronomy/agent/persistence/codec.rb +1 -21
- data/lib/phronomy/agent/persistence/execution_repository.rb +57 -41
- data/lib/phronomy/agent/persistence/journal_repository.rb +27 -18
- data/lib/phronomy/agent/persistence/records.rb +33 -0
- data/lib/phronomy/agent/persistence/retention_repository.rb +70 -0
- data/lib/phronomy/agent/persistence/storage_schema.rb +7 -2
- data/lib/phronomy/agent/persistence/watermark.rb +13 -11
- data/lib/phronomy/agent/recovery/invocation_restorer.rb +13 -21
- data/lib/phronomy/agent/recovery/recovery_coordinator/continuation.rb +10 -9
- data/lib/phronomy/agent/recovery/recovery_coordinator/installation.rb +27 -27
- data/lib/phronomy/agent/recovery/recovery_coordinator/resolution.rb +19 -21
- data/lib/phronomy/agent/recovery/recovery_coordinator.rb +23 -16
- data/lib/phronomy/agent/recovery/recovery_rules.rb +119 -0
- data/lib/phronomy/agent/recovery/recovery_support.rb +8 -11
- data/lib/phronomy/agent/reserved_execution.rb +16 -0
- data/lib/phronomy/agent/retention.rb +17 -0
- data/lib/phronomy/agent/runtime_binding/engine_environment.rb +60 -0
- data/lib/phronomy/agent/runtime_binding/engine_session_builder.rb +34 -0
- data/lib/phronomy/agent/runtime_binding/phase_machine_builder.rb +64 -0
- data/lib/phronomy/agent/runtime_binding/tool_session_builder.rb +34 -0
- data/lib/phronomy/agent/{context_assembly/ruby_llm_materializer.rb → runtime_input.rb} +18 -35
- data/lib/phronomy/agent/{context_assembly/saved_context_reader.rb → saved_context_reader.rb} +6 -4
- data/lib/phronomy/agent/settings.rb +47 -0
- data/lib/phronomy/agent/{context_assembly/state_writer.rb → state_writer.rb} +4 -3
- data/lib/phronomy/agent/store.rb +253 -0
- data/lib/phronomy/agent/{lifecycle_contract/stream_callback_error.rb → stream_callback_error.rb} +1 -1
- data/lib/phronomy/agent/tool_execution/tool_binding.rb +4 -64
- data/lib/phronomy/agent/tool_execution/tool_invocation.rb +50 -219
- data/lib/phronomy/agent/tool_execution/tool_invocation_actions.rb +107 -0
- data/lib/phronomy/agent/tool_execution/tool_invocation_transitions.rb +70 -0
- data/lib/phronomy/configuration/runtime_settings.rb +49 -0
- data/lib/phronomy/content_store/storage_schema.rb +1 -0
- data/lib/phronomy/content_store/stored_contents.rb +2 -1
- data/lib/phronomy/context/assembly.rb +202 -0
- data/lib/phronomy/{llm_contract/context_budget_exceeded_error.rb → context/budget_exceeded_error.rb} +3 -1
- data/lib/phronomy/context/candidate.rb +51 -0
- data/lib/phronomy/context/constraint.rb +47 -0
- data/lib/phronomy/{agent/context_contract → context}/context_plan.rb +1 -1
- data/lib/phronomy/{agent/context_contract → context}/context_plan_validator.rb +3 -3
- data/lib/phronomy/{agent/context_contract → context}/context_policy.rb +1 -1
- data/lib/phronomy/{agent/context_contract → context}/context_policy_input.rb +1 -1
- data/lib/phronomy/{agent/context_assembly → context}/context_policy_input_builder.rb +1 -1
- data/lib/phronomy/context/default_policy.rb +140 -0
- data/lib/phronomy/context/final_budget_validator.rb +33 -0
- data/lib/phronomy/{agent/context_contract → context}/llm_input_build_context.rb +1 -1
- data/lib/phronomy/{agent/context_contract → context}/llm_input_manifest.rb +19 -19
- data/lib/phronomy/{agent/context_contract → context}/llm_input_patch.rb +1 -1
- data/lib/phronomy/context/manifest_error.rb +10 -0
- data/lib/phronomy/context/prompt_template.rb +98 -0
- data/lib/phronomy/{vector_store → documents}/loader/base.rb +1 -1
- data/lib/phronomy/{vector_store → documents}/loader/csv_loader.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/loader/markdown_loader.rb +3 -3
- data/lib/phronomy/{vector_store → documents}/loader/plain_text_loader.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/splitter/base.rb +1 -1
- data/lib/phronomy/{vector_store → documents}/splitter/fixed_size_splitter.rb +2 -2
- data/lib/phronomy/{vector_store → documents}/splitter/recursive_splitter.rb +2 -2
- data/lib/phronomy/embeddings/async/async_client.rb +44 -0
- data/lib/phronomy/embeddings/backends/ruby_llm_embeddings.rb +48 -0
- data/lib/phronomy/embeddings/base.rb +43 -0
- data/lib/phronomy/embeddings/invalid_result_error.rb +11 -0
- data/lib/phronomy/embeddings/transport_error.rb +11 -0
- data/lib/phronomy/engine/concurrency/offload_pool.rb +36 -21
- data/lib/phronomy/engine/concurrency/pool_registry.rb +47 -12
- data/lib/phronomy/engine/diagnostics.rb +1 -1
- data/lib/phronomy/engine/event_loop.rb +7 -7
- data/lib/phronomy/engine/execution_binding.rb +23 -0
- data/lib/phronomy/engine/execution_receiver_binding.rb +43 -0
- data/lib/phronomy/engine/fsm_protocol.rb +1 -6
- data/lib/phronomy/engine/fsm_session.rb +3 -3
- data/lib/phronomy/engine/metrics.rb +16 -14
- data/lib/phronomy/engine/runtime/timer_queue.rb +1 -1
- data/lib/phronomy/engine/runtime.rb +25 -12
- data/lib/phronomy/engine/terminal_decision.rb +7 -0
- data/lib/phronomy/execution/async_operation.rb +41 -0
- data/lib/phronomy/{engine → execution}/blocking.rb +2 -2
- data/lib/phronomy/{engine → execution}/concurrency/cancellation_token.rb +1 -1
- data/lib/phronomy/{engine → execution}/concurrency/deadline.rb +0 -17
- data/lib/phronomy/{engine → execution}/concurrency/physical_completion_task.rb +1 -1
- data/lib/phronomy/{engine → execution}/concurrency/result_collector.rb +1 -1
- data/lib/phronomy/{engine → execution}/concurrency/result_composition.rb +33 -12
- data/lib/phronomy/{engine/concurrency/subscriptions.rb → execution/concurrency/result_subscriptions.rb} +14 -18
- data/lib/phronomy/execution/concurrency/subscriptions.rb +25 -0
- data/lib/phronomy/{engine → execution}/execution.rb +61 -27
- data/lib/phronomy/execution/execution_receiver.rb +78 -0
- data/lib/phronomy/execution/execution_scope_state.rb +35 -0
- data/lib/phronomy/{engine → execution}/invocation_context.rb +0 -15
- data/lib/phronomy/execution/invocation_controls.rb +73 -0
- data/lib/phronomy/execution/runnable.rb +24 -0
- data/lib/phronomy/{engine → execution}/task_result.rb +3 -3
- data/lib/phronomy/execution/wait_policy.rb +24 -0
- data/lib/phronomy/filter/{contract/filter_block_error.rb → filter_block_error.rb} +1 -1
- data/lib/phronomy/generation/generator_verifier/agent_operation.rb +65 -0
- data/lib/phronomy/generation/generator_verifier/default_parser.rb +27 -0
- data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +2 -2
- data/lib/phronomy/generation/generator_verifier.rb +7 -7
- data/lib/phronomy/llm_adapter/async/async_client.rb +47 -0
- data/lib/phronomy/{llm_contract → llm_adapter}/authentication_error.rb +3 -1
- data/lib/phronomy/llm_adapter/backends/ruby_llm.rb +183 -0
- data/lib/phronomy/llm_adapter/base.rb +49 -68
- data/lib/phronomy/{llm_contract → llm_adapter}/context_length_error.rb +3 -1
- data/lib/phronomy/llm_adapter/invalid_result_error.rb +10 -0
- data/lib/phronomy/llm_adapter/message.rb +62 -0
- data/lib/phronomy/{llm_contract → llm_adapter}/rate_limit_error.rb +3 -1
- data/lib/phronomy/llm_adapter/request.rb +79 -0
- data/lib/phronomy/llm_adapter/response.rb +36 -0
- data/lib/phronomy/llm_adapter/stream_chunk.rb +18 -0
- data/lib/phronomy/llm_adapter/token_usage.rb +64 -0
- data/lib/phronomy/llm_adapter/transport_error.rb +9 -0
- data/lib/phronomy/llm_adapter.rb +3 -16
- data/lib/phronomy/multi_agent/admission_registry.rb +1 -8
- data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +108 -84
- data/lib/phronomy/multi_agent/execution_environment.rb +29 -0
- data/lib/phronomy/{agent/handoff → multi_agent}/handoff.rb +2 -2
- data/lib/phronomy/{agent/handoff → multi_agent}/handoff_capability_factory.rb +21 -2
- data/lib/phronomy/multi_agent/handoff_participant.rb +79 -0
- data/lib/phronomy/{agent/handoff → multi_agent}/handoff_policy.rb +1 -1
- data/lib/phronomy/multi_agent/handoff_runner.rb +77 -100
- data/lib/phronomy/{agent/handoff → multi_agent}/handoff_state.rb +1 -1
- data/lib/phronomy/multi_agent/orchestrator.rb +61 -75
- data/lib/phronomy/multi_agent/persistence/codec.rb +20 -0
- data/lib/phronomy/multi_agent/persistence/handoff_state_repository.rb +77 -0
- data/lib/phronomy/multi_agent/persistence/records.rb +27 -0
- data/lib/phronomy/multi_agent/persistence/storage_schema.rb +21 -0
- data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +58 -42
- data/lib/phronomy/multi_agent/persistence/team_repository.rb +29 -21
- data/lib/phronomy/multi_agent/reserved_child_admission.rb +88 -0
- data/lib/phronomy/multi_agent/runtime_binding/engine_environment.rb +35 -0
- data/lib/phronomy/multi_agent/shared_state.rb +2 -2
- data/lib/phronomy/multi_agent/store.rb +156 -0
- data/lib/phronomy/multi_agent/team_coordinator.rb +83 -74
- data/lib/phronomy/multi_agent/team_ownership_registry.rb +2 -12
- data/lib/phronomy/output_parser/parse_error.rb +7 -0
- data/lib/phronomy/persistence/content_repository.rb +26 -0
- data/lib/phronomy/persistence/errors.rb +16 -0
- data/lib/phronomy/persistence/migration/initial_format_migration.rb +2 -2
- data/lib/phronomy/persistence/persistence.rb +23 -0
- data/lib/phronomy/persistence/save_outcome.rb +24 -0
- data/lib/phronomy/persistence/snapshot_comparison.rb +81 -0
- data/lib/phronomy/persistence/storage_boundary.rb +32 -0
- data/lib/phronomy/persistence/transaction.rb +86 -0
- data/lib/phronomy/persistence_composition/storage_schema.rb +9 -4
- data/lib/phronomy/persistence_composition/stores.rb +34 -0
- data/lib/phronomy/persistence_composition/unit4_migration.rb +276 -0
- data/lib/phronomy/runtime_composition/agent_defaults.rb +5 -1
- data/lib/phronomy/runtime_composition/configuration.rb +146 -0
- data/lib/phronomy/runtime_composition/evaluation_defaults.rb +7 -0
- data/lib/phronomy/runtime_composition/execution_defaults.rb +5 -0
- data/lib/phronomy/runtime_composition/generation_defaults.rb +6 -0
- data/lib/phronomy/runtime_composition/global_configuration.rb +38 -0
- data/lib/phronomy/runtime_composition/multi_agent_defaults.rb +9 -0
- data/lib/phronomy/runtime_composition/workflow_defaults.rb +4 -0
- data/lib/phronomy/storage/async/async_client.rb +60 -0
- data/lib/phronomy/storage/backend.rb +8 -2
- data/lib/phronomy/testing/eval/metrics.rb +1 -1
- data/lib/phronomy/testing/eval/scorer/llm_judge.rb +19 -3
- data/lib/phronomy/testing/persistence_contract/a_content_store.rb +2 -2
- data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +6 -6
- data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +55 -52
- data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +3 -3
- data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +7 -7
- data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +11 -11
- data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +57 -51
- data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +1 -1
- data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +23 -22
- data/lib/phronomy/tool/authorization.rb +77 -0
- data/lib/phronomy/tool/base.rb +549 -9
- data/lib/phronomy/tool/call_request.rb +40 -0
- data/lib/phronomy/{agent/tool_execution/tool_definition_set.rb → tool/definition_set.rb} +7 -8
- data/lib/phronomy/tool/operation.rb +88 -0
- data/lib/phronomy/tool/schema.rb +214 -0
- data/lib/phronomy/tool/settings.rb +31 -0
- data/lib/phronomy/tool/tool_error.rb +7 -0
- data/lib/phronomy/tool/tool_executor.rb +58 -0
- data/lib/phronomy/tools/agent.rb +22 -90
- data/lib/phronomy/tools/mcp.rb +21 -14
- data/lib/phronomy/tools/vector_search.rb +4 -4
- data/lib/phronomy/tracing/automatic.rb +11 -6
- data/lib/phronomy/tracing/base.rb +1 -1
- data/lib/phronomy/tracing/observation.rb +37 -0
- data/lib/phronomy/tracing/settings.rb +36 -0
- data/lib/phronomy/vector_store/{async_backend.rb → async/async_client.rb} +35 -29
- data/lib/phronomy/vector_store/{in_memory.rb → backends/in_memory.rb} +12 -10
- data/lib/phronomy/vector_store/{pgvector.rb → backends/pgvector.rb} +44 -16
- data/lib/phronomy/vector_store/{redis_search.rb → backends/redis_search.rb} +74 -37
- data/lib/phronomy/vector_store/base.rb +118 -49
- data/lib/phronomy/vector_store/invalid_result_error.rb +12 -0
- data/lib/phronomy/vector_store.rb +2 -17
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/workflow/completion.rb +11 -0
- data/lib/phronomy/workflow/execution/workflow.rb +7 -7
- data/lib/phronomy/workflow/execution/workflow_action_rules.rb +67 -0
- data/lib/phronomy/workflow/execution/workflow_context.rb +2 -2
- data/lib/phronomy/workflow/execution/workflow_execution_environment.rb +27 -0
- data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +3 -5
- data/lib/phronomy/workflow/execution/workflow_runner.rb +58 -79
- data/lib/phronomy/workflow/execution/workflow_settings.rb +28 -0
- data/lib/phronomy/workflow/persistence/state_repository.rb +28 -22
- data/lib/phronomy/workflow/persistence/storage_schema.rb +13 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_engine_environment.rb +38 -0
- data/lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb +156 -0
- data/lib/phronomy/workflow/{execution → runtime_binding}/workflow_terminal_policy.rb +4 -4
- data/lib/phronomy.rb +57 -16
- data/scripts/api_snapshot.rb +35 -15
- data/sig/_private/phronomy/agent/settings.rbs +19 -0
- data/sig/_private/phronomy/configuration/runtime_settings.rbs +20 -0
- data/sig/_private/phronomy/llm_adapter/async/async_client.rbs +10 -0
- data/sig/_private/phronomy/multi_agent/store.rbs +8 -0
- data/sig/_private/phronomy/persistence/storage_boundary.rbs +7 -0
- data/sig/_private/phronomy/tool/settings.rbs +13 -0
- data/sig/_private/phronomy/tracing/settings.rbs +15 -0
- data/sig/_private/phronomy/workflow/execution/workflow_settings.rbs +13 -0
- data/sig/phronomy/agent/admission.rbs +19 -0
- data/sig/phronomy/agent/execution_contract.rbs +132 -0
- data/sig/phronomy/agent/execution_environment.rbs +20 -0
- data/sig/phronomy/agent/store.rbs +73 -0
- data/sig/phronomy/agent.rbs +14 -138
- data/sig/phronomy/common.rbs +7 -0
- data/sig/phronomy/content_store/contracts.rbs +13 -0
- data/sig/phronomy/context.rbs +152 -0
- data/sig/phronomy/embeddings/async/async_client.rbs +9 -0
- data/sig/phronomy/embeddings/base.rbs +17 -0
- data/sig/phronomy/engine/concurrency/timer_bindings.rbs +22 -0
- data/sig/phronomy/engine/control_bindings.rbs +11 -0
- data/sig/phronomy/engine/control_protocols.rbs +23 -0
- data/sig/phronomy/engine/execution_binding.rbs +6 -0
- data/sig/phronomy/engine/execution_receiver.rbs +15 -0
- data/sig/phronomy/engine/observability.rbs +13 -0
- data/sig/phronomy/execution/async_operation.rbs +7 -0
- data/sig/phronomy/execution/blocking.rbs +5 -0
- data/sig/phronomy/execution/concurrency/cancellation_token.rbs +13 -0
- data/sig/phronomy/execution/concurrency/deadline.rbs +10 -0
- data/sig/phronomy/execution/concurrency/result_composition.rbs +7 -0
- data/sig/phronomy/execution/control_protocols.rbs +18 -0
- data/sig/phronomy/execution/errors.rbs +7 -0
- data/sig/phronomy/execution/execution.rbs +41 -0
- data/sig/phronomy/execution/execution_receiver.rbs +40 -0
- data/sig/phronomy/execution/execution_scope_state.rbs +9 -0
- data/sig/phronomy/execution/invocation_context.rbs +18 -0
- data/sig/phronomy/execution/invocation_controls.rbs +13 -0
- data/sig/phronomy/execution/task_result.rbs +29 -0
- data/sig/phronomy/extensions.rbs +0 -21
- data/sig/phronomy/handoff.rbs +10 -4
- data/sig/phronomy/llm_adapter/backends/ruby_llm.rbs +6 -0
- data/sig/phronomy/llm_adapter/base.rbs +14 -0
- data/sig/phronomy/llm_adapter/usage.rbs +14 -0
- data/sig/phronomy/llm_adapter/values.rbs +43 -0
- data/sig/phronomy/llm_context_window.rbs +10 -0
- data/sig/phronomy/multi_agent/reserved_child_admission.rbs +8 -0
- data/sig/phronomy/multi_agent/store.rbs +54 -0
- data/sig/phronomy/multi_agent.rbs +6 -3
- data/sig/phronomy/persistence/errors.rbs +18 -0
- data/sig/phronomy/persistence/migration.rbs +13 -0
- data/sig/phronomy/persistence/transaction.rbs +19 -0
- data/sig/phronomy/persistence.rbs +1 -99
- data/sig/phronomy/persistence_composition.rbs +19 -0
- data/sig/phronomy/runtime_composition/configuration.rbs +36 -0
- data/sig/phronomy/storage/async/async_client.rbs +8 -0
- data/sig/phronomy/storage/backends/in_memory.rbs +9 -0
- data/sig/phronomy/{storage.rbs → storage/contracts.rbs} +2 -16
- data/sig/phronomy/tool.rbs +48 -4
- data/sig/phronomy/tracing/observation.rbs +7 -0
- data/sig/phronomy/vector_store/async/async_client.rbs +13 -0
- data/sig/phronomy/vector_store/base.rbs +23 -0
- data/sig/phronomy/workflow/execution/workflow_execution_environment.rbs +14 -0
- data/sig/phronomy/workflow/runtime_binding/workflow_terminal_policy.rbs +7 -0
- data/sig/phronomy/workflow/store.rbs +8 -0
- data/sig/phronomy/workflow.rbs +1 -1
- data/sig/phronomy.rbs +0 -49
- metadata +318 -117
- data/lib/phronomy/agent/concerns/before_llm_input.rb +0 -66
- data/lib/phronomy/agent/concerns/error_translation.rb +0 -45
- data/lib/phronomy/agent/concerns/filterable.rb +0 -184
- data/lib/phronomy/agent/context/capability/base.rb +0 -548
- data/lib/phronomy/agent/context/capability/tool_executor.rb +0 -62
- data/lib/phronomy/agent/context/instruction/prompt_template.rb +0 -102
- data/lib/phronomy/agent/context_assembly/runtime_chat_builder.rb +0 -36
- data/lib/phronomy/agent/context_assembly/token_budget_resolver.rb +0 -22
- data/lib/phronomy/agent/context_parts/validators/final_budget_validator.rb +0 -37
- data/lib/phronomy/agent/context_policies/default.rb +0 -142
- data/lib/phronomy/agent/execution/phase_machine_builder.rb +0 -101
- data/lib/phronomy/agent/execution/provider_call_outcome.rb +0 -106
- data/lib/phronomy/agent/handoff/handoff_execution_coordinator.rb +0 -15
- data/lib/phronomy/agent/handoff/handoff_outcome_committer.rb +0 -131
- data/lib/phronomy/agent/handoff/handoff_request.rb +0 -45
- data/lib/phronomy/agent/lifecycle_contract/handoff_error.rb +0 -7
- data/lib/phronomy/agent/persistence/handoff_state_repository.rb +0 -58
- data/lib/phronomy/agent/persistence/queries.rb +0 -61
- data/lib/phronomy/agent/selection/candidate.rb +0 -53
- data/lib/phronomy/agent/selection/constraint.rb +0 -49
- data/lib/phronomy/agent/tool_execution/tool_call_intercepted.rb +0 -29
- data/lib/phronomy/agent/tool_execution/tool_invocation_session_builder.rb +0 -276
- data/lib/phronomy/configuration/configuration.rb +0 -68
- data/lib/phronomy/configuration/global_configuration.rb +0 -26
- data/lib/phronomy/engine/concurrency/cancellation_scope.rb +0 -124
- data/lib/phronomy/engine/concurrency/operation_binding.rb +0 -43
- data/lib/phronomy/engine/execution_receiver.rb +0 -65
- data/lib/phronomy/engine/runnable.rb +0 -54
- data/lib/phronomy/llm_adapter/ruby_llm.rb +0 -47
- data/lib/phronomy/llm_contract/token_usage.rb +0 -85
- data/lib/phronomy/multi_agent/persistence/queries.rb +0 -30
- data/lib/phronomy/multi_agent/storage_contract/team_storage_schema.rb +0 -15
- data/lib/phronomy/output_parser/contract/parse_error.rb +0 -7
- data/lib/phronomy/persistence/api/persistence.rb +0 -140
- data/lib/phronomy/persistence_composition/repositories.rb +0 -77
- data/lib/phronomy/recovery/recovery.rb +0 -186
- data/lib/phronomy/tool/contract/tool_error.rb +0 -7
- data/lib/phronomy/vector_store/embeddings/base.rb +0 -44
- data/lib/phronomy/vector_store/embeddings/ruby_llm_embeddings.rb +0 -47
- data/lib/phronomy/workflow/phase_machine_builder.rb +0 -205
- data/lib/phronomy/workflow/storage_contract/workflow_storage_schema.rb +0 -9
- data/sig/phronomy/execution_receiver.rbs +0 -34
- data/sig/phronomy/llm_adapter.rbs +0 -11
- data/sig/phronomy/runtime.rbs +0 -76
- /data/lib/phronomy/agent/{context_assembly/context_importer.rb → context_importer.rb} +0 -0
- /data/lib/phronomy/{engine → execution}/backpressure_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/cancellation_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/concurrency/worker_input_restricted.rb +0 -0
- /data/lib/phronomy/{engine → execution}/event_loop_reentrancy_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/execution_cancellation_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/execution_timeout_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/invalid_async_entry_action_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/invalid_async_transition_action_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/invalid_async_workflow_action_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/pool_shutdown_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/recursion_limit_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/runtime_shutdown_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/runtime_shutdown_reentrancy_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/scheduler_reentrancy_error.rb +0 -0
- /data/lib/phronomy/{engine → execution}/timeout_error.rb +0 -0
|
@@ -0,0 +1,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,182 @@
|
|
|
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
|
+
@_instance_input_filters ||= []
|
|
96
|
+
@_instance_input_filters << _resolve_filter(filter)
|
|
97
|
+
self
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
# Registers an output filter on this instance.
|
|
101
|
+
# Accepts either a {Phronomy::Filter::Base} instance or a subclass;
|
|
102
|
+
# when a class is given it is instantiated with +.new+.
|
|
103
|
+
# Runs in addition to any class-level output filters.
|
|
104
|
+
# @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>]
|
|
105
|
+
# @return [self]
|
|
106
|
+
# @api public
|
|
107
|
+
def add_output_filter(filter)
|
|
108
|
+
@_instance_output_filters ||= []
|
|
109
|
+
@_instance_output_filters << _resolve_filter(filter)
|
|
110
|
+
self
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Registers a tool result filter on this instance.
|
|
114
|
+
#
|
|
115
|
+
# When called with two arguments, the filter is scoped to the given tool
|
|
116
|
+
# class only. When called with one argument, it applies to all tools.
|
|
117
|
+
# Accepts either a {Phronomy::Filter::Base} instance or a subclass;
|
|
118
|
+
# when a class is given it is instantiated with +.new+.
|
|
119
|
+
#
|
|
120
|
+
# @overload add_tool_result_filter(filter)
|
|
121
|
+
# @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>] applied to every tool
|
|
122
|
+
# @overload add_tool_result_filter(tool_class, filter)
|
|
123
|
+
# @param tool_class [Class] scope the filter to this tool
|
|
124
|
+
# @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>]
|
|
125
|
+
# @return [self]
|
|
126
|
+
# @api public
|
|
127
|
+
def add_tool_result_filter(tool_class_or_filter, filter = nil)
|
|
128
|
+
if filter.nil?
|
|
129
|
+
# Single-argument form: apply to all tools.
|
|
130
|
+
@_instance_tool_result_filters ||= []
|
|
131
|
+
@_instance_tool_result_filters << _resolve_filter(tool_class_or_filter)
|
|
132
|
+
else
|
|
133
|
+
# Two-argument form: scoped to one tool class.
|
|
134
|
+
@_scoped_tool_result_filters ||= {}
|
|
135
|
+
(@_scoped_tool_result_filters[tool_class_or_filter] ||= []) << _resolve_filter(filter)
|
|
136
|
+
end
|
|
137
|
+
self
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
private
|
|
141
|
+
|
|
142
|
+
# Run input filters (class-level then instance-level).
|
|
143
|
+
# @param input [String, Hash] the raw user input
|
|
144
|
+
# @return [String, Hash] the (possibly transformed) input
|
|
145
|
+
# @api private
|
|
146
|
+
def run_input_filters!(input)
|
|
147
|
+
class_filters = self.class._class_input_filters
|
|
148
|
+
inst_filters = @_instance_input_filters || []
|
|
149
|
+
(class_filters + inst_filters).inject(input) { |val, f| f.call(val) }
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
# Run output filters (class-level then instance-level).
|
|
153
|
+
# @param output [String] the LLM output
|
|
154
|
+
# @return [String] the (possibly transformed) output
|
|
155
|
+
# @api private
|
|
156
|
+
def run_output_filters!(output)
|
|
157
|
+
class_filters = self.class._class_output_filters
|
|
158
|
+
inst_filters = @_instance_output_filters || []
|
|
159
|
+
(class_filters + inst_filters).inject(output) { |val, f| f.call(val) }
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Collect all tool-result filters (global + scoped) for a given tool class.
|
|
163
|
+
# @param tool_class [Class]
|
|
164
|
+
# @return [Array<Phronomy::Filter::Base>]
|
|
165
|
+
# @api private
|
|
166
|
+
def _tool_result_filters_for(tool_class)
|
|
167
|
+
global = self.class._class_tool_result_filters + (@_instance_tool_result_filters || [])
|
|
168
|
+
scoped = (@_scoped_tool_result_filters || {})[tool_class] || []
|
|
169
|
+
global + scoped
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Coerce +filter+ to an instance: if a Class is passed, call +.new+;
|
|
173
|
+
# otherwise return the object as-is.
|
|
174
|
+
# @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>]
|
|
175
|
+
# @return [Phronomy::Filter::Base]
|
|
176
|
+
# @api private
|
|
177
|
+
def _resolve_filter(filter)
|
|
178
|
+
filter.is_a?(Class) ? filter.new : filter
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
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
|
|
@@ -12,21 +12,16 @@ module Phronomy
|
|
|
12
12
|
# Candidates have no side effects. Runtime retains the first registration.
|
|
13
13
|
# Existing instances remain available for lookup and transition completion
|
|
14
14
|
# after admission closes; their own gate rejects new ownership changes.
|
|
15
|
-
def self.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
def self.existing_for(runtime)
|
|
22
|
-
runtime.__shutdown_participant(key: self)
|
|
23
|
-
end
|
|
15
|
+
def self.current = ExecutionEnvironment.current.ownership
|
|
16
|
+
def self.existing_current = ExecutionEnvironment.current.existing_ownership
|
|
17
|
+
def self.for(environment) = environment.ownership
|
|
18
|
+
def self.existing_for(environment) = environment.existing_ownership
|
|
24
19
|
|
|
25
20
|
Entry = Data.define(:state, :agent, :token)
|
|
26
21
|
private_constant :Entry
|
|
27
22
|
|
|
28
|
-
def initialize(
|
|
29
|
-
@
|
|
23
|
+
def initialize(environment:)
|
|
24
|
+
@environment = environment
|
|
30
25
|
@mutex = Mutex.new
|
|
31
26
|
@condition = ConditionVariable.new
|
|
32
27
|
@entries = {}
|
|
@@ -38,11 +33,11 @@ module Phronomy
|
|
|
38
33
|
token = reserve_create!(key)
|
|
39
34
|
|
|
40
35
|
begin
|
|
41
|
-
agent = yield(@
|
|
36
|
+
agent = yield(@environment)
|
|
42
37
|
rescue Phronomy::AgentAlreadyExistsError,
|
|
43
|
-
Phronomy::
|
|
44
|
-
Phronomy::
|
|
45
|
-
Phronomy::
|
|
38
|
+
Phronomy::Persistence::ConflictError,
|
|
39
|
+
Phronomy::Persistence::NotFoundError,
|
|
40
|
+
Phronomy::Persistence::SerializationError,
|
|
46
41
|
ArgumentError,
|
|
47
42
|
Phronomy::ConfigurationError
|
|
48
43
|
release_construction!(key, token)
|
|
@@ -98,7 +93,7 @@ module Phronomy
|
|
|
98
93
|
end
|
|
99
94
|
|
|
100
95
|
begin
|
|
101
|
-
agent = yield(@
|
|
96
|
+
agent = yield(@environment)
|
|
102
97
|
publish_constructed!(
|
|
103
98
|
key,
|
|
104
99
|
token,
|
|
@@ -161,7 +156,7 @@ module Phronomy
|
|
|
161
156
|
end
|
|
162
157
|
@entries[key] = Entry.new(state: :purging, agent: agent, token: token)
|
|
163
158
|
end
|
|
164
|
-
agent.send(:__mark_purging!, @
|
|
159
|
+
agent.send(:__mark_purging!, @environment)
|
|
165
160
|
token
|
|
166
161
|
end
|
|
167
162
|
|
|
@@ -170,7 +165,7 @@ module Phronomy
|
|
|
170
165
|
@mutex.synchronize do
|
|
171
166
|
entry = @entries[key]
|
|
172
167
|
validate_purge_entry!(entry, agent, token, key)
|
|
173
|
-
agent.send(:__mark_purged!, @
|
|
168
|
+
agent.send(:__mark_purged!, @environment)
|
|
174
169
|
@entries.delete(key)
|
|
175
170
|
@condition.broadcast
|
|
176
171
|
end
|
|
@@ -183,7 +178,7 @@ module Phronomy
|
|
|
183
178
|
@mutex.synchronize do
|
|
184
179
|
entry = @entries[key]
|
|
185
180
|
validate_purge_entry!(entry, agent, token, key)
|
|
186
|
-
agent.send(:__restore_live_after_purge_abort!, @
|
|
181
|
+
agent.send(:__restore_live_after_purge_abort!, @environment)
|
|
187
182
|
@entries[key] = Entry.new(state: :live, agent: agent, token: nil)
|
|
188
183
|
@condition.broadcast
|
|
189
184
|
end
|
|
@@ -197,7 +192,7 @@ module Phronomy
|
|
|
197
192
|
key = normalize_agent_id(agent.agent_id)
|
|
198
193
|
@mutex.synchronize do
|
|
199
194
|
validate_purge_entry!(@entries[key], agent, token, key)
|
|
200
|
-
agent.send(:__mark_ownership_recovery_required!, @
|
|
195
|
+
agent.send(:__mark_ownership_recovery_required!, @environment)
|
|
201
196
|
@entries[key] = Entry.new(
|
|
202
197
|
state: :recovery_required,
|
|
203
198
|
agent: agent,
|
|
@@ -239,7 +234,7 @@ module Phronomy
|
|
|
239
234
|
@condition.broadcast
|
|
240
235
|
owned
|
|
241
236
|
end
|
|
242
|
-
agents.each { |agent| agent.send(:__release_runtime_owner!, @
|
|
237
|
+
agents.each { |agent| agent.send(:__release_runtime_owner!, @environment) }
|
|
243
238
|
true
|
|
244
239
|
end
|
|
245
240
|
|
|
@@ -267,7 +262,7 @@ module Phronomy
|
|
|
267
262
|
"constructed Agent identity mismatch: reserved #{key.inspect}, got #{agent.agent_id.inspect}"
|
|
268
263
|
end
|
|
269
264
|
|
|
270
|
-
agent.send(:__bind_runtime_owner!, @
|
|
265
|
+
agent.send(:__bind_runtime_owner!, @environment)
|
|
271
266
|
@mutex.synchronize do
|
|
272
267
|
entry = @entries[key]
|
|
273
268
|
unless entry&.state == :constructing && entry.token.equal?(token)
|
|
@@ -282,7 +277,7 @@ module Phronomy
|
|
|
282
277
|
# :nocov:
|
|
283
278
|
if fail_closed
|
|
284
279
|
fail_construction_closed!(key, token, agent: agent)
|
|
285
|
-
agent&.send(:__mark_ownership_recovery_required!, @
|
|
280
|
+
agent&.send(:__mark_ownership_recovery_required!, @environment)
|
|
286
281
|
else
|
|
287
282
|
release_construction!(key, token)
|
|
288
283
|
end
|
|
@@ -9,39 +9,47 @@ module Phronomy
|
|
|
9
9
|
def initialize(view) = @view = view
|
|
10
10
|
|
|
11
11
|
def create(root)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
Phronomy::Persistence::StorageBoundary.call do
|
|
13
|
+
record = Codec.encode_agent_root(root)
|
|
14
|
+
@view.atomic do |bound|
|
|
15
|
+
entry = bound.records(StorageSchema::ROOTS).insert(key: root.agent_id.to_s,
|
|
16
|
+
revision: Integer(root.agent_revision), attributes: {}, record: record)
|
|
17
|
+
decode(entry, root.agent_id, revision: root.agent_revision)
|
|
18
|
+
end
|
|
17
19
|
end
|
|
18
20
|
end
|
|
19
21
|
|
|
20
22
|
def load(agent_id)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
Phronomy::Persistence::StorageBoundary.call do
|
|
24
|
+
value = @view.atomic do |bound|
|
|
25
|
+
entry = bound.records(StorageSchema::ROOTS).read(agent_id.to_s)
|
|
26
|
+
entry && decode(entry, agent_id)
|
|
27
|
+
end
|
|
28
|
+
value || raise(Phronomy::Storage::NotFoundError, "Agent not found: #{agent_id}")
|
|
24
29
|
end
|
|
25
|
-
value || raise(Phronomy::Storage::NotFoundError, "Agent not found: #{agent_id}")
|
|
26
30
|
end
|
|
27
31
|
|
|
28
32
|
def save(agent_id, expected_revision:, root:)
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
33
|
+
Phronomy::Persistence::StorageBoundary.call do
|
|
34
|
+
expected = Integer(expected_revision)
|
|
35
|
+
revision = Integer(root.agent_revision)
|
|
36
|
+
raise Phronomy::Storage::ConflictError, "root save must advance revision exactly once" unless revision == expected + 1
|
|
37
|
+
unless root.agent_id.to_s == agent_id.to_s
|
|
38
|
+
raise Phronomy::Storage::SerializationError, "Agent root identity mismatch"
|
|
39
|
+
end
|
|
40
|
+
record = Codec.encode_agent_root(root)
|
|
41
|
+
@view.atomic do |bound|
|
|
42
|
+
entry = bound.records(StorageSchema::ROOTS).replace(key: agent_id.to_s,
|
|
43
|
+
expected_revision: expected, next_revision: revision, attributes: {}, record: record)
|
|
44
|
+
decode(entry, agent_id, revision: revision)
|
|
45
|
+
end
|
|
40
46
|
end
|
|
41
47
|
end
|
|
42
48
|
|
|
43
49
|
def delete(agent_id)
|
|
44
|
-
|
|
50
|
+
Phronomy::Persistence::StorageBoundary.call do
|
|
51
|
+
@view.records(StorageSchema::ROOTS).delete(key: agent_id.to_s)
|
|
52
|
+
end
|
|
45
53
|
end
|
|
46
54
|
|
|
47
55
|
private
|