phronomy 0.26.0 → 0.27.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/CHANGELOG.md +40 -0
- data/README.md +4 -0
- data/VERIFY.sh +27 -27
- data/benchmark/bench_agent_invoke.rb +26 -22
- data/benchmark/bench_context_assembler.rb +4 -5
- data/docs/architecture/agent-chat-and-state-ownership.md +147 -0
- data/docs/architecture/agent-configuration-and-tool-binding.md +139 -0
- data/docs/architecture/agent-context.md +4 -2
- data/docs/architecture/agent-transition-ownership.md +89 -0
- data/docs/architecture/before-llm-input.md +6 -0
- data/docs/architecture/context-management.md +30 -0
- data/docs/architecture/context-preparation-steps.md +90 -0
- data/docs/architecture/entry-action-and-team-wording.md +72 -0
- data/docs/architecture/execution-metadata-and-values.md +80 -0
- data/docs/architecture/generator-verifier-ownership.md +111 -0
- data/docs/architecture/multi-agent-handoff.md +8 -2
- data/docs/architecture/persistence-refactoring-plan.md +50 -0
- data/docs/architecture/persistence.md +27 -4
- data/docs/architecture/refactoring-closure.md +334 -0
- data/docs/architecture/remaining-refactoring-plan.md +374 -0
- data/docs/architecture/rubyllm-2-token-ownership.md +82 -0
- data/docs/architecture/tool-schema-recording-gap.md +50 -0
- data/docs/architecture/tracing.md +1 -1
- data/docs/architecture/workflow-terminal-ownership-design.md +104 -0
- data/docs/architecture.md +209 -0
- data/docs/decisions/014-unified-persistence-durable-state.md +11 -0
- data/docs/decisions/024-event-loop-single-writer-agent-runtime.md +5 -1
- data/docs/decisions/025-process-local-agent-ownership-and-runtime-admission.md +13 -3
- data/docs/decisions/026-workflow-runtime-admission-and-durable-terminal-barrier.md +5 -1
- data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +5 -0
- data/docs/decisions/031-durable-multi-agent-coordination.md +5 -0
- data/docs/decisions/032-storage-backend-composition.md +80 -0
- data/docs/decisions/033-domain-persistence-ownership.md +79 -0
- data/docs/decisions/034-handoff-runner-coordination-ownership.md +76 -0
- data/docs/decisions/035-tool-executor-capability-ownership.md +68 -0
- data/docs/decisions/036-context-contract-ownership.md +86 -0
- data/docs/decisions/037-common-definition-ownership.md +62 -0
- data/docs/decisions/038-responsibility-based-source-layout.md +131 -0
- data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +74 -0
- data/docs/decisions/040-configuration-default-composition.md +94 -0
- data/docs/decisions/041-feature-owned-identity-registries.md +100 -0
- data/docs/decisions/042-feature-owned-execution-state.md +106 -0
- data/docs/decisions/043-storage-execution-constraint-notifications.md +75 -0
- data/docs/decisions/044-agent-default-and-one-shot-composition.md +106 -0
- data/docs/decisions/045-worker-input-restriction-ownership.md +100 -0
- data/docs/decisions/046-agent-responsibility-layout-and-shared-records.md +110 -0
- data/docs/decisions/047-recovered-execution-continuation-contract.md +116 -0
- data/docs/decisions/048-dispatch-preparation-worker-ownership.md +120 -0
- data/docs/decisions/049-initial-preparation-worker-ownership.md +111 -0
- data/docs/decisions/050-approval-resume-snapshot-and-commit-ownership.md +98 -0
- data/docs/decisions/051-execution-outcome-worker-ownership.md +151 -0
- data/docs/decisions/052-tool-invocation-restoration-ownership.md +92 -0
- data/docs/decisions/053-shared-state-coordination-ownership.md +71 -0
- data/docs/decisions/054-workflow-terminal-save-single-owner.md +76 -0
- data/docs/decisions/055-terminal-observer-failure-settlement.md +60 -0
- data/docs/decisions/056-workflow-terminal-policy-ownership.md +87 -0
- data/docs/decisions/057-storage-transaction-boundaries.md +73 -0
- data/docs/decisions/058-neutral-storage-primitives.md +78 -0
- data/docs/decisions/README.md +35 -6
- data/docs/design/durable-semantic-coordination/IMPLEMENTATION_DESIGN_V2.md +6 -0
- data/docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md +6 -0
- data/docs/features.md +9 -3
- data/docs/getting-started.md +4 -4
- data/docs/migrations/0.15.md +5 -0
- data/docs/migrations/durable-semantic-coordination-v2.md +6 -0
- data/docs/migrations/handoff-runner-multi-agent.md +35 -0
- data/docs/migrations/neutral-storage-spi.md +43 -0
- data/docs/migrations/parallel-tool-chat-removal.md +49 -0
- data/docs/migrations/shared-state-multi-agent.md +44 -0
- data/docs/migrations/storage-backend-composition.md +117 -0
- data/docs/migrations/storage-transaction-boundaries.md +74 -0
- data/docs/persistence-backends.md +162 -204
- data/docs/runtime-and-concurrency.md +117 -32
- data/lib/phronomy/agent/api/agent.rb +17 -0
- data/lib/phronomy/agent/async_event_api.rb +2 -2
- data/lib/phronomy/agent/base.rb +51 -241
- data/lib/phronomy/{agent.rb → agent/composition/run_once.rb} +4 -13
- data/lib/phronomy/agent/context/capability/base.rb +67 -26
- data/lib/phronomy/agent/context/capability/tool_executor.rb +62 -0
- data/lib/phronomy/agent/{context_assembler.rb → context_assembly/context_assembler.rb} +143 -88
- data/lib/phronomy/agent/{context_importer.rb → context_assembly/context_importer.rb} +2 -2
- data/lib/phronomy/agent/{ruby_llm_materializer.rb → context_assembly/ruby_llm_materializer.rb} +4 -7
- data/lib/phronomy/agent/context_assembly/runtime_chat_builder.rb +36 -0
- data/lib/phronomy/agent/context_assembly/saved_context_reader.rb +53 -0
- data/lib/phronomy/agent/context_assembly/state_writer.rb +165 -0
- data/lib/phronomy/agent/context_assembly/token_budget_resolver.rb +22 -0
- data/lib/phronomy/agent/{context_plan.rb → context_contract/context_plan.rb} +1 -1
- data/lib/phronomy/agent/{context_policy_input.rb → context_contract/context_policy_input.rb} +6 -6
- data/lib/phronomy/agent/{llm_input_build_context.rb → context_contract/llm_input_build_context.rb} +1 -1
- data/lib/phronomy/agent/{llm_input_manifest.rb → context_contract/llm_input_manifest.rb} +20 -20
- data/lib/phronomy/agent/{llm_input_patch.rb → context_contract/llm_input_patch.rb} +2 -2
- data/lib/phronomy/agent/{agent_execution.rb → execution/agent_execution.rb} +8 -4
- data/lib/phronomy/agent/{agent_invocation.rb → execution/agent_invocation.rb} +8 -13
- data/lib/phronomy/agent/{agent_invocation_session_builder.rb → execution/agent_invocation_session_builder.rb} +19 -95
- data/lib/phronomy/agent/execution/approval_resume_commit.rb +108 -0
- data/lib/phronomy/agent/execution/dispatch_preparation.rb +305 -0
- data/lib/phronomy/agent/{exact_execution.rb → execution/exact_execution.rb} +9 -10
- data/lib/phronomy/agent/{execution_cancellation.rb → execution/execution_cancellation.rb} +2 -3
- data/lib/phronomy/agent/execution/execution_coordinator.rb +1925 -0
- data/lib/phronomy/agent/execution/execution_failure.rb +30 -0
- data/lib/phronomy/agent/execution/execution_metadata.rb +53 -0
- data/lib/phronomy/agent/execution/execution_outcome_committer.rb +344 -0
- data/lib/phronomy/agent/execution/execution_registry.rb +459 -0
- data/lib/phronomy/agent/execution/execution_session_runner.rb +118 -0
- data/lib/phronomy/agent/execution/initial_preparation.rb +421 -0
- data/lib/phronomy/agent/execution/invocation_transitions.rb +86 -0
- data/lib/phronomy/agent/{phase_machine_builder.rb → execution/phase_machine_builder.rb} +20 -71
- data/lib/phronomy/agent/{provider_call_outcome.rb → execution/provider_call_outcome.rb} +9 -9
- data/lib/phronomy/agent/execution/runtime_record_encoder.rb +210 -0
- data/lib/phronomy/agent/{handoff_context.rb → handoff/handoff_context.rb} +2 -2
- data/lib/phronomy/agent/handoff/handoff_execution_coordinator.rb +15 -0
- data/lib/phronomy/agent/handoff/handoff_outcome_committer.rb +131 -0
- data/lib/phronomy/agent/{handoff_state.rb → handoff/handoff_state.rb} +1 -1
- data/lib/phronomy/agent/{journal_projection.rb → journal/journal_projection.rb} +4 -0
- data/lib/phronomy/agent/{journal_record.rb → journal/journal_record.rb} +3 -3
- data/lib/phronomy/agent/{llm_call_record.rb → journal/llm_call_record.rb} +2 -2
- data/lib/phronomy/agent/{agent_root.rb → lifecycle/agent_root.rb} +6 -2
- data/lib/phronomy/agent/lifecycle/default_persistence.rb +29 -0
- data/lib/phronomy/{engine/runtime/agent_ownership_registry.rb → agent/lifecycle/ownership_registry.rb} +24 -10
- data/lib/phronomy/{agent_already_exists_error.rb → agent/lifecycle_contract/agent_already_exists_error.rb} +2 -0
- data/lib/phronomy/{agent_busy_error.rb → agent/lifecycle_contract/agent_busy_error.rb} +2 -0
- data/lib/phronomy/{agent_purged_error.rb → agent/lifecycle_contract/agent_purged_error.rb} +2 -0
- data/lib/phronomy/agent/lifecycle_contract/handoff_error.rb +7 -0
- data/lib/phronomy/{stream_callback_error.rb → agent/lifecycle_contract/stream_callback_error.rb} +2 -0
- data/lib/phronomy/agent/persistence/agent_repository.rb +61 -0
- data/lib/phronomy/agent/persistence/codec.rb +358 -0
- data/lib/phronomy/agent/persistence/execution_repository.rb +108 -0
- data/lib/phronomy/agent/persistence/handoff_state_repository.rb +58 -0
- data/lib/phronomy/agent/persistence/journal_repository.rb +54 -0
- data/lib/phronomy/agent/persistence/queries.rb +61 -0
- data/lib/phronomy/agent/persistence/storage_schema.rb +24 -0
- data/lib/phronomy/agent/persistence/watermark.rb +27 -0
- data/lib/phronomy/agent/recovery/invocation_restorer.rb +132 -0
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/continuation.rb +21 -45
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/installation.rb +32 -37
- data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/resolution.rb +37 -40
- data/lib/phronomy/agent/{recovery_coordinator.rb → recovery/recovery_coordinator.rb} +6 -10
- data/lib/phronomy/agent/recovery/recovery_support.rb +227 -0
- data/lib/phronomy/agent/selection/candidate.rb +1 -1
- data/lib/phronomy/agent/{approval_evaluation_request.rb → tool_execution/approval_evaluation_request.rb} +1 -12
- data/lib/phronomy/agent/{tool_approval_request.rb → tool_execution/tool_approval_request.rb} +1 -10
- data/lib/phronomy/agent/tool_execution/tool_binding.rb +90 -0
- data/lib/phronomy/agent/{tool_call_intercepted.rb → tool_execution/tool_call_intercepted.rb} +2 -2
- data/lib/phronomy/agent/{tool_definition_set.rb → tool_execution/tool_definition_set.rb} +9 -4
- data/lib/phronomy/agent/{tool_invocation.rb → tool_execution/tool_invocation.rb} +69 -34
- data/lib/phronomy/common/configuration_error.rb +7 -0
- data/lib/phronomy/common/error.rb +5 -0
- data/lib/phronomy/{agent → common/values}/immutable.rb +9 -1
- data/lib/phronomy/common/values/serializable.rb +32 -0
- data/lib/phronomy/{configuration.rb → configuration/configuration.rb} +14 -5
- data/lib/phronomy/configuration/global_configuration.rb +26 -0
- data/lib/phronomy/content_store/storage_schema.rb +11 -0
- data/lib/phronomy/content_store/stored_contents.rb +43 -0
- data/lib/phronomy/engine/backpressure_error.rb +7 -0
- data/lib/phronomy/{blocking.rb → engine/blocking.rb} +1 -1
- data/lib/phronomy/engine/cancellation_error.rb +7 -0
- data/lib/phronomy/engine/concurrency/cancellation_token.rb +4 -0
- data/lib/phronomy/engine/concurrency/offload_pool.rb +4 -0
- data/lib/phronomy/engine/concurrency/operation_binding.rb +1 -1
- data/lib/phronomy/engine/concurrency/worker_input_restricted.rb +14 -0
- data/lib/phronomy/engine/event_loop.rb +158 -624
- data/lib/phronomy/engine/event_loop_reentrancy_error.rb +8 -0
- data/lib/phronomy/{execution.rb → engine/execution.rb} +0 -7
- data/lib/phronomy/{execution_cancellation_error.rb → engine/execution_cancellation_error.rb} +2 -0
- data/lib/phronomy/engine/execution_receiver.rb +65 -0
- data/lib/phronomy/{execution_timeout_error.rb → engine/execution_timeout_error.rb} +2 -0
- data/lib/phronomy/engine/fsm_protocol.rb +14 -0
- data/lib/phronomy/engine/fsm_session.rb +35 -28
- data/lib/phronomy/{invalid_async_entry_action_error.rb → engine/invalid_async_entry_action_error.rb} +2 -0
- data/lib/phronomy/{invalid_async_transition_action_error.rb → engine/invalid_async_transition_action_error.rb} +2 -0
- data/lib/phronomy/{invalid_async_workflow_action_error.rb → engine/invalid_async_workflow_action_error.rb} +2 -0
- data/lib/phronomy/engine/pool_shutdown_error.rb +7 -0
- data/lib/phronomy/engine/recursion_limit_error.rb +7 -0
- data/lib/phronomy/engine/runtime.rb +80 -84
- data/lib/phronomy/engine/runtime_shutdown_error.rb +7 -0
- data/lib/phronomy/engine/runtime_shutdown_reentrancy_error.rb +7 -0
- data/lib/phronomy/engine/scheduler_reentrancy_error.rb +9 -0
- data/lib/phronomy/engine/task_result.rb +4 -0
- data/lib/phronomy/engine/timeout_error.rb +7 -0
- data/lib/phronomy/filter/contract/filter_block_error.rb +14 -0
- data/lib/phronomy/generation/generator_verifier/agent_result_receiver.rb +89 -0
- data/lib/phronomy/generation/generator_verifier/pipeline_state.rb +57 -0
- data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +112 -0
- data/lib/phronomy/generation/generator_verifier.rb +118 -0
- data/lib/phronomy/generation/low_confidence_error.rb +14 -0
- data/lib/phronomy/llm_context_window/token_budget.rb +6 -7
- data/lib/phronomy/llm_contract/authentication_error.rb +7 -0
- data/lib/phronomy/{context_budget_exceeded_error.rb → llm_contract/context_budget_exceeded_error.rb} +2 -0
- data/lib/phronomy/llm_contract/context_length_error.rb +7 -0
- data/lib/phronomy/llm_contract/rate_limit_error.rb +7 -0
- data/lib/phronomy/{token_usage.rb → llm_contract/token_usage.rb} +2 -2
- data/lib/phronomy/llm_contract/transport_error.rb +7 -0
- data/lib/phronomy/multi_agent/admission_registry.rb +22 -2
- data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +5 -5
- data/lib/phronomy/{agent → multi_agent}/handoff_runner.rb +18 -17
- data/lib/phronomy/multi_agent/orchestrator.rb +2 -2
- data/lib/phronomy/multi_agent/persistence/codec.rb +55 -0
- data/lib/phronomy/multi_agent/persistence/queries.rb +30 -0
- data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +108 -0
- data/lib/phronomy/multi_agent/persistence/team_repository.rb +61 -0
- data/lib/phronomy/{agent → multi_agent}/shared_state.rb +56 -39
- data/lib/phronomy/multi_agent/storage_contract/team_storage_schema.rb +15 -0
- data/lib/phronomy/multi_agent/team_coordinator.rb +21 -18
- data/lib/phronomy/multi_agent/team_execution.rb +1 -1
- data/lib/phronomy/{engine/runtime → multi_agent}/team_ownership_registry.rb +14 -4
- data/lib/phronomy/multi_agent/team_root.rb +1 -1
- data/lib/phronomy/output_parser/contract/parse_error.rb +7 -0
- data/lib/phronomy/persistence/api/persistence.rb +140 -0
- data/lib/phronomy/persistence/migration/initial_format_migration.rb +19 -19
- data/lib/phronomy/persistence_composition/repositories.rb +77 -0
- data/lib/phronomy/persistence_composition/storage_schema.rb +24 -0
- data/lib/phronomy/{execution_rehydration_required_error.rb → recovery/execution_rehydration_required_error.rb} +2 -0
- data/lib/phronomy/{recovery.rb → recovery/recovery.rb} +1 -1
- data/lib/phronomy/runtime_composition/agent_defaults.rb +7 -0
- data/lib/phronomy/runtime_composition/configuration_defaults.rb +9 -0
- data/lib/phronomy/runtime_composition/global_runtime.rb +19 -0
- data/lib/phronomy/storage/backend.rb +101 -0
- data/lib/phronomy/storage/backends/in_memory.rb +157 -0
- data/lib/phronomy/storage/blob_conflict_error.rb +10 -0
- data/lib/phronomy/storage/blobs.rb +31 -0
- data/lib/phronomy/storage/condition.rb +25 -0
- data/lib/phronomy/storage/condition_failed_error.rb +16 -0
- data/lib/phronomy/storage/conflict_error.rb +9 -0
- data/lib/phronomy/{persistence → storage}/durable_record.rb +12 -12
- data/lib/phronomy/storage/entry.rb +33 -0
- data/lib/phronomy/storage/guard_ref.rb +13 -0
- data/lib/phronomy/storage/not_found_error.rb +9 -0
- data/lib/phronomy/storage/record_codec.rb +177 -0
- data/lib/phronomy/storage/records.rb +61 -0
- data/lib/phronomy/storage/resource.rb +126 -0
- data/lib/phronomy/storage/scope.rb +25 -0
- data/lib/phronomy/storage/serialization_error.rb +9 -0
- data/lib/phronomy/storage/streams.rb +44 -0
- data/lib/phronomy/storage/transaction_error.rb +10 -0
- data/lib/phronomy/storage/unique_constraint_error.rb +17 -0
- data/lib/phronomy/storage/unsupported_backend_error.rb +9 -0
- data/lib/phronomy/storage/validation.rb +53 -0
- data/lib/phronomy/storage/view.rb +131 -0
- data/lib/phronomy/testing/persistence_contract/a_content_store.rb +1 -1
- data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +4 -4
- data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +10 -7
- data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +2 -2
- data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +6 -6
- data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +6 -6
- data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +8 -8
- data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +263 -0
- data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +123 -0
- data/lib/phronomy/testing/persistence_contract.rb +4 -0
- data/lib/phronomy/tool/contract/tool_error.rb +7 -0
- data/lib/phronomy/tools/agent.rb +1 -1
- data/lib/phronomy/version.rb +1 -1
- data/lib/phronomy/{workflow.rb → workflow/execution/workflow.rb} +8 -6
- data/lib/phronomy/{workflow_context.rb → workflow/execution/workflow_context.rb} +4 -0
- data/lib/phronomy/workflow/execution/workflow_context_ownership_error.rb +7 -0
- data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +188 -0
- data/lib/phronomy/{workflow_runner.rb → workflow/execution/workflow_runner.rb} +122 -71
- data/lib/phronomy/workflow/execution/workflow_terminal_policy.rb +40 -0
- data/lib/phronomy/workflow/persistence/codec.rb +153 -0
- data/lib/phronomy/workflow/persistence/state_repository.rb +57 -0
- data/lib/phronomy/workflow/phase_machine_builder.rb +2 -2
- data/lib/phronomy/workflow/storage_contract/workflow_storage_schema.rb +9 -0
- data/lib/phronomy.rb +52 -94
- data/scripts/api_snapshot.rb +1 -1
- data/scripts/storage_spi_snapshot.rb +36 -0
- data/sig/phronomy/agent.rbs +0 -1
- data/sig/phronomy/execution_receiver.rbs +34 -0
- data/sig/phronomy/handoff.rbs +4 -2
- data/sig/phronomy/multi_agent.rbs +18 -0
- data/sig/phronomy/persistence.rbs +7 -91
- data/sig/phronomy/storage.rbs +174 -0
- data/sig/phronomy/tool.rbs +10 -1
- metadata +230 -99
- data/lib/phronomy/agent/execution_coordinator.rb +0 -3151
- data/lib/phronomy/agent/handoff_execution_coordinator.rb +0 -143
- data/lib/phronomy/agent/recovery_support.rb +0 -504
- data/lib/phronomy/agent/token_budget_resolver.rb +0 -70
- data/lib/phronomy/agent/tool_executor.rb +0 -55
- data/lib/phronomy/generator_verifier.rb +0 -369
- data/lib/phronomy/invalid_context_budget_configuration_error.rb +0 -8
- data/lib/phronomy/multi_agent/parallel_tool_chat.rb +0 -116
- data/lib/phronomy/persistence/durable_codec.rb +0 -706
- data/lib/phronomy/persistence/in_memory.rb +0 -690
- data/lib/phronomy/persistence/repository_facades.rb +0 -535
- data/lib/phronomy/persistence.rb +0 -276
- data/lib/phronomy/ruby_llm_patches.rb +0 -24
- data/lib/phronomy/workflow_recovery.rb +0 -123
- /data/lib/phronomy/agent/{context_candidate_resolver.rb → context_assembly/context_candidate_resolver.rb} +0 -0
- /data/lib/phronomy/agent/{context_policy_input_builder.rb → context_assembly/context_policy_input_builder.rb} +0 -0
- /data/lib/phronomy/agent/{context_plan_validator.rb → context_contract/context_plan_validator.rb} +0 -0
- /data/lib/phronomy/agent/{context_policy.rb → context_contract/context_policy.rb} +0 -0
- /data/lib/phronomy/agent/{llm_operation_result.rb → execution/llm_operation_result.rb} +0 -0
- /data/lib/phronomy/agent/{handoff.rb → handoff/handoff.rb} +0 -0
- /data/lib/phronomy/agent/{handoff_capability_factory.rb → handoff/handoff_capability_factory.rb} +0 -0
- /data/lib/phronomy/agent/{handoff_policy.rb → handoff/handoff_policy.rb} +0 -0
- /data/lib/phronomy/agent/{handoff_projection.rb → handoff/handoff_projection.rb} +0 -0
- /data/lib/phronomy/agent/{handoff_request.rb → handoff/handoff_request.rb} +0 -0
- /data/lib/phronomy/agent/{tool_invocation_session_builder.rb → tool_execution/tool_invocation_session_builder.rb} +0 -0
- /data/lib/phronomy/{canonical_json.rb → common/canonical_json.rb} +0 -0
- /data/lib/phronomy/{diagnostics.rb → engine/diagnostics.rb} +0 -0
- /data/lib/phronomy/{event.rb → engine/event.rb} +0 -0
- /data/lib/phronomy/{invocation_context.rb → engine/invocation_context.rb} +0 -0
- /data/lib/phronomy/{metrics.rb → engine/metrics.rb} +0 -0
- /data/lib/phronomy/{runnable.rb → engine/runnable.rb} +0 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Agent
|
|
5
|
+
# Reads saved manifests and Provider output for continuation preparation.
|
|
6
|
+
# Callers perform these reads before applying live state on EventLoop.
|
|
7
|
+
# @api private
|
|
8
|
+
module SavedContextReader
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
def manifest_from_ref(agent, ref)
|
|
12
|
+
raw = agent.persistence.contents.fetch_json(ref)
|
|
13
|
+
Phronomy::Agent::LLMInputManifest.from_h(raw)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def materialize_projection(agent, manifest_ref)
|
|
17
|
+
manifest = manifest_from_ref(agent, manifest_ref)
|
|
18
|
+
projection = Phronomy::Agent::RubyLLMMaterializer.new(
|
|
19
|
+
agent: agent,
|
|
20
|
+
persistence: agent.persistence
|
|
21
|
+
).materialize(manifest: manifest, manifest_ref: manifest_ref)
|
|
22
|
+
[manifest, projection]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def latest_assistant_record(execution, llm_call_id: nil)
|
|
26
|
+
Array(execution.working_records).reverse.find do |record|
|
|
27
|
+
record.kind.to_sym == :assistant_message &&
|
|
28
|
+
(llm_call_id.nil? || record.llm_call_id.to_s == llm_call_id.to_s)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def provider_output_and_usage(agent, execution)
|
|
33
|
+
assistant = latest_assistant_record(execution)
|
|
34
|
+
payload = assistant ? agent.persistence.contents.fetch_json(assistant.content_ref) : {}
|
|
35
|
+
output = payload["content"]
|
|
36
|
+
|
|
37
|
+
call = execution.llm_calls.last
|
|
38
|
+
usage_hash = if call&.usage_ref
|
|
39
|
+
agent.persistence.contents.fetch_json(call.usage_ref)
|
|
40
|
+
else
|
|
41
|
+
{}
|
|
42
|
+
end
|
|
43
|
+
usage = Phronomy::TokenUsage.new(
|
|
44
|
+
input: usage_hash["input"] || usage_hash[:input] || usage_hash["input_tokens"] || usage_hash[:input_tokens],
|
|
45
|
+
output: usage_hash["output"] || usage_hash[:output] || usage_hash["output_tokens"] || usage_hash[:output_tokens],
|
|
46
|
+
cached: usage_hash["cached"] || usage_hash[:cached] || usage_hash["cache_read_tokens"] || usage_hash[:cache_read_tokens] || usage_hash["cached_tokens"] || usage_hash[:cached_tokens],
|
|
47
|
+
cache_creation: usage_hash["cache_creation"] || usage_hash[:cache_creation] || usage_hash["cache_write_tokens"] || usage_hash[:cache_write_tokens] || usage_hash["cache_creation_tokens"] || usage_hash[:cache_creation_tokens]
|
|
48
|
+
)
|
|
49
|
+
[output, usage]
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Agent
|
|
5
|
+
# Persists initial state and explicit idle-Agent context/lifecycle changes.
|
|
6
|
+
# Base owns live-state publication; execution commits have separate owners.
|
|
7
|
+
# @api private
|
|
8
|
+
class StateWriter
|
|
9
|
+
Update = Data.define(:root, :records)
|
|
10
|
+
private_constant :Update
|
|
11
|
+
|
|
12
|
+
def initialize(persistence:, agent_id:)
|
|
13
|
+
@persistence = persistence
|
|
14
|
+
@agent_id = agent_id
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def create_root(definition:, context:, knowledge:, metadata:)
|
|
18
|
+
root = Agent::AgentRoot.create(
|
|
19
|
+
agent_id: agent_id,
|
|
20
|
+
agent_definition_id: definition.fetch(:id),
|
|
21
|
+
agent_definition_version: definition.fetch(:version),
|
|
22
|
+
metadata: metadata
|
|
23
|
+
)
|
|
24
|
+
persistence.transaction do |tx|
|
|
25
|
+
tx.agents.create(root)
|
|
26
|
+
records = initial_context_records(tx: tx, root: root, context: context)
|
|
27
|
+
records.concat(initial_knowledge_records(tx: tx, root: root, knowledge: knowledge))
|
|
28
|
+
unless records.empty?
|
|
29
|
+
appended = tx.journals.append(agent_id, expected_position: 0, records: records)
|
|
30
|
+
root = root.with(
|
|
31
|
+
agent_revision: 1,
|
|
32
|
+
context_revision: 1,
|
|
33
|
+
journal_position: appended.length
|
|
34
|
+
)
|
|
35
|
+
tx.agents.save(agent_id, expected_revision: 0, root: root)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
root
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def add_knowledge(root:, content:, metadata:)
|
|
42
|
+
current = root
|
|
43
|
+
next_root = nil
|
|
44
|
+
appended = nil
|
|
45
|
+
persistence.transaction do |tx|
|
|
46
|
+
tx.executions.assert_idle!(agent_id)
|
|
47
|
+
record = build_knowledge_record(
|
|
48
|
+
tx: tx,
|
|
49
|
+
root: current,
|
|
50
|
+
content: content,
|
|
51
|
+
metadata: metadata
|
|
52
|
+
)
|
|
53
|
+
appended = tx.journals.append(
|
|
54
|
+
agent_id,
|
|
55
|
+
expected_position: current.journal_position,
|
|
56
|
+
records: [record]
|
|
57
|
+
)
|
|
58
|
+
next_root = current.with(
|
|
59
|
+
agent_revision: current.agent_revision + 1,
|
|
60
|
+
context_revision: current.context_revision + 1,
|
|
61
|
+
journal_position: current.journal_position + appended.length
|
|
62
|
+
)
|
|
63
|
+
tx.agents.save(
|
|
64
|
+
agent_id,
|
|
65
|
+
expected_revision: current.agent_revision,
|
|
66
|
+
root: next_root
|
|
67
|
+
)
|
|
68
|
+
end
|
|
69
|
+
Update.new(root: next_root, records: appended)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def mutate_context(root:, kind:, context_affecting:)
|
|
73
|
+
current = root
|
|
74
|
+
next_root = nil
|
|
75
|
+
appended = nil
|
|
76
|
+
persistence.transaction do |tx|
|
|
77
|
+
tx.executions.assert_idle!(agent_id)
|
|
78
|
+
record = Agent::JournalRecord.new(
|
|
79
|
+
agent_id: agent_id,
|
|
80
|
+
kind: kind,
|
|
81
|
+
channel: :state,
|
|
82
|
+
context_generation: current.transcript_generation,
|
|
83
|
+
context_candidate: false
|
|
84
|
+
)
|
|
85
|
+
appended = tx.journals.append(
|
|
86
|
+
agent_id,
|
|
87
|
+
expected_position: current.journal_position,
|
|
88
|
+
records: [record]
|
|
89
|
+
)
|
|
90
|
+
proposed = yield(current)
|
|
91
|
+
next_root = proposed.with(
|
|
92
|
+
journal_position: current.journal_position + appended.length,
|
|
93
|
+
context_revision: context_affecting ?
|
|
94
|
+
yield_context_revision(current, proposed) : current.context_revision
|
|
95
|
+
)
|
|
96
|
+
tx.agents.save(
|
|
97
|
+
agent_id,
|
|
98
|
+
expected_revision: current.agent_revision,
|
|
99
|
+
root: next_root
|
|
100
|
+
)
|
|
101
|
+
end
|
|
102
|
+
Update.new(root: next_root, records: appended)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
private
|
|
106
|
+
|
|
107
|
+
attr_reader :persistence, :agent_id
|
|
108
|
+
|
|
109
|
+
def initial_context_records(tx:, root:, context:)
|
|
110
|
+
return [] unless context
|
|
111
|
+
|
|
112
|
+
imported = context.respond_to?(:records) ? context :
|
|
113
|
+
Agent::ContextImporter.import_messages(context)
|
|
114
|
+
imported.records.map do |record|
|
|
115
|
+
content_ref = case record.content_format
|
|
116
|
+
when :text then tx.contents.put_text(record.content)
|
|
117
|
+
when :json then tx.contents.put_json(record.content)
|
|
118
|
+
else
|
|
119
|
+
raise ArgumentError,
|
|
120
|
+
"unsupported imported content format: #{record.content_format.inspect}"
|
|
121
|
+
end
|
|
122
|
+
Agent::JournalRecord.new(
|
|
123
|
+
agent_id: agent_id,
|
|
124
|
+
kind: record.kind,
|
|
125
|
+
channel: record.channel,
|
|
126
|
+
role: record.role,
|
|
127
|
+
content_ref: content_ref,
|
|
128
|
+
context_generation: root.transcript_generation,
|
|
129
|
+
context_candidate: true,
|
|
130
|
+
metadata: record.metadata
|
|
131
|
+
)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def initial_knowledge_records(tx:, root:, knowledge:)
|
|
136
|
+
Array(knowledge).map do |content|
|
|
137
|
+
build_knowledge_record(
|
|
138
|
+
tx: tx,
|
|
139
|
+
root: root,
|
|
140
|
+
content: content,
|
|
141
|
+
metadata: {}
|
|
142
|
+
)
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def build_knowledge_record(tx:, root:, content:, metadata:)
|
|
147
|
+
Agent::JournalRecord.new(
|
|
148
|
+
agent_id: agent_id,
|
|
149
|
+
kind: :knowledge,
|
|
150
|
+
channel: :context,
|
|
151
|
+
role: :user,
|
|
152
|
+
content_ref: tx.contents.put_text(String(content)),
|
|
153
|
+
context_generation: root.transcript_generation,
|
|
154
|
+
context_candidate: true,
|
|
155
|
+
metadata: metadata || {}
|
|
156
|
+
)
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
def yield_context_revision(current, proposed)
|
|
160
|
+
(proposed.context_revision == current.context_revision) ?
|
|
161
|
+
current.context_revision + 1 : proposed.context_revision
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Phronomy
|
|
4
|
+
module Agent
|
|
5
|
+
# Resolves the input limit from RubyLLM's authoritative model registry.
|
|
6
|
+
class TokenBudgetResolver
|
|
7
|
+
def resolve(model_config)
|
|
8
|
+
config = model_config.to_h.transform_keys(&:to_s)
|
|
9
|
+
model_name = config["model"]
|
|
10
|
+
return nil unless model_name
|
|
11
|
+
|
|
12
|
+
model = RubyLLM.models.find(model_name, provider: config["provider"])
|
|
13
|
+
limit = model&.context_window
|
|
14
|
+
return nil unless limit.is_a?(Integer) && limit.positive?
|
|
15
|
+
|
|
16
|
+
Phronomy::LlmContextWindow::TokenBudget.new(max_input_tokens: limit)
|
|
17
|
+
rescue RubyLLM::ModelNotFoundError
|
|
18
|
+
nil
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -21,7 +21,7 @@ module Phronomy
|
|
|
21
21
|
knowledge: Array(knowledge).freeze,
|
|
22
22
|
tools: Array(tools).freeze,
|
|
23
23
|
conversation: Array(conversation).map { |group| Array(group).freeze }.freeze,
|
|
24
|
-
metadata: Immutable.copy(metadata || {})
|
|
24
|
+
metadata: Phronomy::Values::Immutable.copy(metadata || {})
|
|
25
25
|
)
|
|
26
26
|
freeze
|
|
27
27
|
end
|
data/lib/phronomy/agent/{context_policy_input.rb → context_contract/context_policy_input.rb}
RENAMED
|
@@ -50,8 +50,8 @@ module Phronomy
|
|
|
50
50
|
knowledge: normalized_knowledge,
|
|
51
51
|
tools: normalized_tools,
|
|
52
52
|
conversation: normalized_conversation,
|
|
53
|
-
model_config: Immutable.copy(values[:model_config] || {}),
|
|
54
|
-
metadata: Immutable.copy(values[:metadata] || {})
|
|
53
|
+
model_config: Phronomy::Values::Immutable.copy(values[:model_config] || {}),
|
|
54
|
+
metadata: Phronomy::Values::Immutable.copy(values[:metadata] || {})
|
|
55
55
|
))
|
|
56
56
|
freeze
|
|
57
57
|
end
|
|
@@ -145,7 +145,7 @@ module Phronomy
|
|
|
145
145
|
) do
|
|
146
146
|
def initialize(**values)
|
|
147
147
|
provenance = ContextPolicyInput.send(:normalize_provenance, values[:provenance])
|
|
148
|
-
definition = Immutable.copy(values.fetch(:definition))
|
|
148
|
+
definition = Phronomy::Values::Immutable.copy(values.fetch(:definition))
|
|
149
149
|
unless definition.is_a?(Hash) && definition["name"].to_s != ""
|
|
150
150
|
raise ArgumentError, "ContextPolicyInput ToolItem definition requires a non-empty name"
|
|
151
151
|
end
|
|
@@ -156,7 +156,7 @@ module Phronomy
|
|
|
156
156
|
estimated_tokens: ContextPolicyInput.send(:normalize_estimated_tokens, values[:estimated_tokens]),
|
|
157
157
|
required: !!values[:required],
|
|
158
158
|
provenance: provenance,
|
|
159
|
-
metadata: Immutable.copy(values[:metadata] || {})
|
|
159
|
+
metadata: Phronomy::Values::Immutable.copy(values[:metadata] || {})
|
|
160
160
|
)
|
|
161
161
|
raise ArgumentError, "ContextPolicyInput ToolItem id must not be empty" if id.empty?
|
|
162
162
|
freeze
|
|
@@ -212,12 +212,12 @@ module Phronomy
|
|
|
212
212
|
id: id,
|
|
213
213
|
kind: kind,
|
|
214
214
|
role: role,
|
|
215
|
-
content: Immutable.copy(values[:content]),
|
|
215
|
+
content: Phronomy::Values::Immutable.copy(values[:content]),
|
|
216
216
|
content_format: content_format,
|
|
217
217
|
estimated_tokens: normalize_estimated_tokens(values[:estimated_tokens]),
|
|
218
218
|
required: !!values[:required],
|
|
219
219
|
provenance: normalize_provenance(values[:provenance]),
|
|
220
|
-
metadata: Immutable.copy(values[:metadata] || {})
|
|
220
|
+
metadata: Phronomy::Values::Immutable.copy(values[:metadata] || {})
|
|
221
221
|
}
|
|
222
222
|
end
|
|
223
223
|
|
data/lib/phronomy/agent/{llm_input_build_context.rb → context_contract/llm_input_build_context.rb}
RENAMED
|
@@ -9,7 +9,7 @@ module Phronomy
|
|
|
9
9
|
:config, :call_sequence
|
|
10
10
|
) do
|
|
11
11
|
def initialize(**values)
|
|
12
|
-
super(**values.merge(config: Immutable.copy(values.fetch(:config, {}))))
|
|
12
|
+
super(**values.merge(config: Phronomy::Values::Immutable.copy(values.fetch(:config, {}))))
|
|
13
13
|
freeze
|
|
14
14
|
end
|
|
15
15
|
end
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
module Phronomy
|
|
4
4
|
module Agent
|
|
5
5
|
# ContentStore-backed durable codec boundary for one Provider input manifest.
|
|
6
|
-
# It is intentionally separate from
|
|
6
|
+
# It is intentionally separate from Storage::DurableRecord.
|
|
7
7
|
class LLMInputManifest
|
|
8
8
|
VERSION = "0.1"
|
|
9
9
|
CALL_MODES = %i[ask complete].freeze
|
|
@@ -18,7 +18,7 @@ module Phronomy
|
|
|
18
18
|
if delivery.nil? || !SEGMENT_DELIVERIES.include?(delivery.to_s)
|
|
19
19
|
raise ArgumentError, "unknown Segment delivery: #{delivery.inspect}"
|
|
20
20
|
end
|
|
21
|
-
super(**values.merge(metadata: Immutable.copy(values[:metadata] || {})))
|
|
21
|
+
super(**values.merge(metadata: Phronomy::Values::Immutable.copy(values[:metadata] || {})))
|
|
22
22
|
freeze
|
|
23
23
|
end
|
|
24
24
|
|
|
@@ -62,7 +62,7 @@ module Phronomy
|
|
|
62
62
|
label: label
|
|
63
63
|
)
|
|
64
64
|
unless SEGMENT_DELIVERIES.include?(delivery)
|
|
65
|
-
raise Phronomy::
|
|
65
|
+
raise Phronomy::Storage::SerializationError,
|
|
66
66
|
"LLMInputManifest segment delivery must be one of #{SEGMENT_DELIVERIES.inspect}, got #{delivery.inspect}"
|
|
67
67
|
end
|
|
68
68
|
tool_call_id = LLMInputManifest.send(
|
|
@@ -87,10 +87,10 @@ module Phronomy
|
|
|
87
87
|
tool_call_id: tool_call_id,
|
|
88
88
|
metadata: metadata
|
|
89
89
|
)
|
|
90
|
-
rescue Phronomy::
|
|
90
|
+
rescue Phronomy::Storage::SerializationError
|
|
91
91
|
raise
|
|
92
92
|
rescue => error
|
|
93
|
-
raise Phronomy::
|
|
93
|
+
raise Phronomy::Storage::SerializationError,
|
|
94
94
|
"invalid LLMInputManifest segment: #{error.class}: #{error.message}"
|
|
95
95
|
end
|
|
96
96
|
|
|
@@ -162,7 +162,7 @@ module Phronomy
|
|
|
162
162
|
label = "LLMInputManifest"
|
|
163
163
|
version = source.fetch("version")
|
|
164
164
|
unless version.is_a?(String) && version == VERSION
|
|
165
|
-
raise Phronomy::
|
|
165
|
+
raise Phronomy::Storage::SerializationError,
|
|
166
166
|
"unsupported LLMInputManifest version: #{version.inspect}; " \
|
|
167
167
|
"current version is #{VERSION.inspect}"
|
|
168
168
|
end
|
|
@@ -229,10 +229,10 @@ module Phronomy
|
|
|
229
229
|
adapter_name: adapter_name,
|
|
230
230
|
adapter_version: adapter_version
|
|
231
231
|
)
|
|
232
|
-
rescue Phronomy::
|
|
232
|
+
rescue Phronomy::Storage::SerializationError
|
|
233
233
|
raise
|
|
234
234
|
rescue => error
|
|
235
|
-
raise Phronomy::
|
|
235
|
+
raise Phronomy::Storage::SerializationError,
|
|
236
236
|
"invalid LLMInputManifest: #{error.class}: #{error.message}"
|
|
237
237
|
end
|
|
238
238
|
|
|
@@ -262,11 +262,11 @@ module Phronomy
|
|
|
262
262
|
|
|
263
263
|
def strict_source!(hash, required:, optional:, label:)
|
|
264
264
|
unless hash.is_a?(Hash)
|
|
265
|
-
raise Phronomy::
|
|
265
|
+
raise Phronomy::Storage::SerializationError,
|
|
266
266
|
"#{label} must be a Hash"
|
|
267
267
|
end
|
|
268
268
|
unless hash.keys.all? { |key| key.is_a?(String) }
|
|
269
|
-
raise Phronomy::
|
|
269
|
+
raise Phronomy::Storage::SerializationError,
|
|
270
270
|
"#{label} keys must all be String"
|
|
271
271
|
end
|
|
272
272
|
|
|
@@ -277,13 +277,13 @@ module Phronomy
|
|
|
277
277
|
details = []
|
|
278
278
|
details << "missing=#{missing.inspect}" unless missing.empty?
|
|
279
279
|
details << "unknown=#{unknown.inspect}" unless unknown.empty?
|
|
280
|
-
raise Phronomy::
|
|
280
|
+
raise Phronomy::Storage::SerializationError,
|
|
281
281
|
"#{label} schema mismatch (#{details.join(", ")})"
|
|
282
282
|
end
|
|
283
283
|
Phronomy::CanonicalJSON.dump(hash)
|
|
284
284
|
hash
|
|
285
285
|
rescue ArgumentError => error
|
|
286
|
-
raise Phronomy::
|
|
286
|
+
raise Phronomy::Storage::SerializationError,
|
|
287
287
|
"#{label} is not canonical JSON compatible: #{error.message}"
|
|
288
288
|
end
|
|
289
289
|
|
|
@@ -291,7 +291,7 @@ module Phronomy
|
|
|
291
291
|
value = hash.fetch(key)
|
|
292
292
|
return value if value.is_a?(Integer)
|
|
293
293
|
|
|
294
|
-
raise Phronomy::
|
|
294
|
+
raise Phronomy::Storage::SerializationError,
|
|
295
295
|
"#{label} #{key} must be an Integer"
|
|
296
296
|
end
|
|
297
297
|
|
|
@@ -299,7 +299,7 @@ module Phronomy
|
|
|
299
299
|
value = hash.fetch(key)
|
|
300
300
|
return value if value.is_a?(Integer) && value.positive?
|
|
301
301
|
|
|
302
|
-
raise Phronomy::
|
|
302
|
+
raise Phronomy::Storage::SerializationError,
|
|
303
303
|
"#{label} #{key} must be a positive Integer"
|
|
304
304
|
end
|
|
305
305
|
|
|
@@ -307,7 +307,7 @@ module Phronomy
|
|
|
307
307
|
value = hash.fetch(key)
|
|
308
308
|
return value if value.is_a?(Integer) && value >= 0
|
|
309
309
|
|
|
310
|
-
raise Phronomy::
|
|
310
|
+
raise Phronomy::Storage::SerializationError,
|
|
311
311
|
"#{label} #{key} must be a non-negative Integer"
|
|
312
312
|
end
|
|
313
313
|
|
|
@@ -315,7 +315,7 @@ module Phronomy
|
|
|
315
315
|
value = hash.fetch(key)
|
|
316
316
|
return value if value.is_a?(String) && !value.empty?
|
|
317
317
|
|
|
318
|
-
raise Phronomy::
|
|
318
|
+
raise Phronomy::Storage::SerializationError,
|
|
319
319
|
"#{label} #{key} must be a non-empty String"
|
|
320
320
|
end
|
|
321
321
|
|
|
@@ -323,7 +323,7 @@ module Phronomy
|
|
|
323
323
|
value = hash[key]
|
|
324
324
|
return value if value.nil? || value.is_a?(String)
|
|
325
325
|
|
|
326
|
-
raise Phronomy::
|
|
326
|
+
raise Phronomy::Storage::SerializationError,
|
|
327
327
|
"#{label} #{key} must be a String or nil"
|
|
328
328
|
end
|
|
329
329
|
|
|
@@ -331,7 +331,7 @@ module Phronomy
|
|
|
331
331
|
value = hash.fetch(key)
|
|
332
332
|
return value if value.is_a?(String) && allowed.include?(value)
|
|
333
333
|
|
|
334
|
-
raise Phronomy::
|
|
334
|
+
raise Phronomy::Storage::SerializationError,
|
|
335
335
|
"#{label} #{key} must be one of #{allowed.inspect}"
|
|
336
336
|
end
|
|
337
337
|
|
|
@@ -339,7 +339,7 @@ module Phronomy
|
|
|
339
339
|
value = hash.fetch(key)
|
|
340
340
|
return value if value.is_a?(Array)
|
|
341
341
|
|
|
342
|
-
raise Phronomy::
|
|
342
|
+
raise Phronomy::Storage::SerializationError,
|
|
343
343
|
"#{label} #{key} must be an Array"
|
|
344
344
|
end
|
|
345
345
|
|
|
@@ -347,7 +347,7 @@ module Phronomy
|
|
|
347
347
|
value = hash.fetch(key)
|
|
348
348
|
return value if value.is_a?(Hash)
|
|
349
349
|
|
|
350
|
-
raise Phronomy::
|
|
350
|
+
raise Phronomy::Storage::SerializationError,
|
|
351
351
|
"#{label} #{key} must be a Hash"
|
|
352
352
|
end
|
|
353
353
|
end
|
|
@@ -7,8 +7,8 @@ module Phronomy
|
|
|
7
7
|
LLMInputPatch = Data.define(:model_config_patch, :segment_candidates) do
|
|
8
8
|
def initialize(model_config_patch: nil, segment_candidates: nil)
|
|
9
9
|
super(
|
|
10
|
-
model_config_patch: model_config_patch && Immutable.copy(model_config_patch),
|
|
11
|
-
segment_candidates: segment_candidates && Immutable.copy(Array(segment_candidates))
|
|
10
|
+
model_config_patch: model_config_patch && Phronomy::Values::Immutable.copy(model_config_patch),
|
|
11
|
+
segment_candidates: segment_candidates && Phronomy::Values::Immutable.copy(Array(segment_candidates))
|
|
12
12
|
)
|
|
13
13
|
freeze
|
|
14
14
|
end
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "../../engine/concurrency/worker_input_restricted"
|
|
4
|
+
|
|
3
5
|
require "securerandom"
|
|
4
6
|
require "time"
|
|
5
7
|
|
|
6
8
|
module Phronomy
|
|
7
9
|
module Agent
|
|
8
10
|
class AgentExecution
|
|
11
|
+
include Phronomy::Concurrency::WorkerInputRestricted
|
|
12
|
+
|
|
9
13
|
ACTIVE_STATUSES = %i[preparing active suspended].freeze
|
|
10
14
|
TERMINAL_STATUSES = %i[completed handed_off failed cancelled rejected blocked].freeze
|
|
11
15
|
TRANSITIONS = {
|
|
@@ -55,13 +59,13 @@ module Phronomy
|
|
|
55
59
|
ATTRIBUTES.each do |name|
|
|
56
60
|
value = attributes.fetch(name)
|
|
57
61
|
value = value.to_sym if %i[status phase].include?(name)
|
|
58
|
-
instance_variable_set("@#{name}", Immutable.copy(value))
|
|
62
|
+
instance_variable_set("@#{name}", Phronomy::Values::Immutable.copy(value))
|
|
59
63
|
end
|
|
60
64
|
raise ArgumentError, "unknown execution status: #{status.inspect}" unless TRANSITIONS.key?(status)
|
|
61
65
|
raise ArgumentError, "execution_revision must be non-negative" if execution_revision.negative?
|
|
62
|
-
Immutable.validate_canonical_json!(metadata, label: "Execution metadata")
|
|
66
|
+
Phronomy::Values::Immutable.validate_canonical_json!(metadata, label: "Execution metadata")
|
|
63
67
|
if approval_request
|
|
64
|
-
Immutable.validate_canonical_json!(approval_request, label: "Approval request")
|
|
68
|
+
Phronomy::Values::Immutable.validate_canonical_json!(approval_request, label: "Approval request")
|
|
65
69
|
end
|
|
66
70
|
freeze
|
|
67
71
|
end
|
|
@@ -86,7 +90,7 @@ module Phronomy
|
|
|
86
90
|
end
|
|
87
91
|
|
|
88
92
|
# Current semantic payload representation. Persistence format identity and
|
|
89
|
-
# compatibility validation are owned by Persistence::
|
|
93
|
+
# compatibility validation are owned by Agent::Persistence::Codec.
|
|
90
94
|
# @api public
|
|
91
95
|
def to_h
|
|
92
96
|
ATTRIBUTES.to_h do |name|
|
|
@@ -1,22 +1,17 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require_relative "../../engine/concurrency/worker_input_restricted"
|
|
4
|
+
|
|
3
5
|
require "securerandom"
|
|
4
6
|
require "time"
|
|
5
7
|
|
|
6
8
|
module Phronomy
|
|
7
9
|
module Agent
|
|
8
10
|
class AgentInvocation
|
|
11
|
+
include Phronomy::Concurrency::WorkerInputRestricted
|
|
12
|
+
|
|
9
13
|
# execution_id is the logical Agent execution parent. Concrete Runtime
|
|
10
14
|
# routing identity belongs only to the owning FSMSession incarnation.
|
|
11
|
-
TOOL_EVENT_TYPES = %i[
|
|
12
|
-
tool_authorized
|
|
13
|
-
tool_approval_required
|
|
14
|
-
tool_completed
|
|
15
|
-
tool_failed
|
|
16
|
-
tool_rejected
|
|
17
|
-
tool_cancelled
|
|
18
|
-
].freeze
|
|
19
|
-
|
|
20
15
|
LLM_EVENT_TYPES = %i[llm_completed llm_failed].freeze
|
|
21
16
|
CALLBACK_FAILED_EVENTS = %i[application_callback_failed].freeze
|
|
22
17
|
SETUP_FAILED_EVENTS = %i[llm_setup_failed tool_setup_failed].freeze
|
|
@@ -147,11 +142,11 @@ module Phronomy
|
|
|
147
142
|
if durable_id.nil? &&
|
|
148
143
|
Phronomy::Runtime.instance.event_loop.current?
|
|
149
144
|
state =
|
|
150
|
-
Phronomy::Runtime.instance.event_loop.agent_execution_state(
|
|
145
|
+
Phronomy::Agent::ExecutionRegistry.for(Phronomy::Runtime.instance.event_loop).agent_execution_state(
|
|
151
146
|
execution_id
|
|
152
147
|
)
|
|
153
148
|
durable_id = state&.execution&.metadata&.fetch(
|
|
154
|
-
|
|
149
|
+
ExecutionMetadata::PENDING_LLM_ID_KEY,
|
|
155
150
|
nil
|
|
156
151
|
)
|
|
157
152
|
end
|
|
@@ -209,7 +204,7 @@ module Phronomy
|
|
|
209
204
|
return true
|
|
210
205
|
end
|
|
211
206
|
|
|
212
|
-
return false unless
|
|
207
|
+
return false unless InvocationTransitions::TOOL_EVENTS.include?(event.type)
|
|
213
208
|
|
|
214
209
|
invocation = tool_invocation(event.payload&.fetch(:tool_invocation_id, nil))
|
|
215
210
|
return true unless invocation
|
|
@@ -540,7 +535,7 @@ module Phronomy
|
|
|
540
535
|
# listener receives the notification event separately.
|
|
541
536
|
@runtime_events << StreamEvent.new(
|
|
542
537
|
type: event.type,
|
|
543
|
-
payload: Immutable.copy(event.payload)
|
|
538
|
+
payload: Phronomy::Values::Immutable.copy(event.payload)
|
|
544
539
|
)
|
|
545
540
|
listener = @callback_failure ? nil : @event_listener
|
|
546
541
|
return unless listener
|