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.
Files changed (304) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +40 -0
  3. data/README.md +4 -0
  4. data/VERIFY.sh +27 -27
  5. data/benchmark/bench_agent_invoke.rb +26 -22
  6. data/benchmark/bench_context_assembler.rb +4 -5
  7. data/docs/architecture/agent-chat-and-state-ownership.md +147 -0
  8. data/docs/architecture/agent-configuration-and-tool-binding.md +139 -0
  9. data/docs/architecture/agent-context.md +4 -2
  10. data/docs/architecture/agent-transition-ownership.md +89 -0
  11. data/docs/architecture/before-llm-input.md +6 -0
  12. data/docs/architecture/context-management.md +30 -0
  13. data/docs/architecture/context-preparation-steps.md +90 -0
  14. data/docs/architecture/entry-action-and-team-wording.md +72 -0
  15. data/docs/architecture/execution-metadata-and-values.md +80 -0
  16. data/docs/architecture/generator-verifier-ownership.md +111 -0
  17. data/docs/architecture/multi-agent-handoff.md +8 -2
  18. data/docs/architecture/persistence-refactoring-plan.md +50 -0
  19. data/docs/architecture/persistence.md +27 -4
  20. data/docs/architecture/refactoring-closure.md +334 -0
  21. data/docs/architecture/remaining-refactoring-plan.md +374 -0
  22. data/docs/architecture/rubyllm-2-token-ownership.md +82 -0
  23. data/docs/architecture/tool-schema-recording-gap.md +50 -0
  24. data/docs/architecture/tracing.md +1 -1
  25. data/docs/architecture/workflow-terminal-ownership-design.md +104 -0
  26. data/docs/architecture.md +209 -0
  27. data/docs/decisions/014-unified-persistence-durable-state.md +11 -0
  28. data/docs/decisions/024-event-loop-single-writer-agent-runtime.md +5 -1
  29. data/docs/decisions/025-process-local-agent-ownership-and-runtime-admission.md +13 -3
  30. data/docs/decisions/026-workflow-runtime-admission-and-durable-terminal-barrier.md +5 -1
  31. data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +5 -0
  32. data/docs/decisions/031-durable-multi-agent-coordination.md +5 -0
  33. data/docs/decisions/032-storage-backend-composition.md +80 -0
  34. data/docs/decisions/033-domain-persistence-ownership.md +79 -0
  35. data/docs/decisions/034-handoff-runner-coordination-ownership.md +76 -0
  36. data/docs/decisions/035-tool-executor-capability-ownership.md +68 -0
  37. data/docs/decisions/036-context-contract-ownership.md +86 -0
  38. data/docs/decisions/037-common-definition-ownership.md +62 -0
  39. data/docs/decisions/038-responsibility-based-source-layout.md +131 -0
  40. data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +74 -0
  41. data/docs/decisions/040-configuration-default-composition.md +94 -0
  42. data/docs/decisions/041-feature-owned-identity-registries.md +100 -0
  43. data/docs/decisions/042-feature-owned-execution-state.md +106 -0
  44. data/docs/decisions/043-storage-execution-constraint-notifications.md +75 -0
  45. data/docs/decisions/044-agent-default-and-one-shot-composition.md +106 -0
  46. data/docs/decisions/045-worker-input-restriction-ownership.md +100 -0
  47. data/docs/decisions/046-agent-responsibility-layout-and-shared-records.md +110 -0
  48. data/docs/decisions/047-recovered-execution-continuation-contract.md +116 -0
  49. data/docs/decisions/048-dispatch-preparation-worker-ownership.md +120 -0
  50. data/docs/decisions/049-initial-preparation-worker-ownership.md +111 -0
  51. data/docs/decisions/050-approval-resume-snapshot-and-commit-ownership.md +98 -0
  52. data/docs/decisions/051-execution-outcome-worker-ownership.md +151 -0
  53. data/docs/decisions/052-tool-invocation-restoration-ownership.md +92 -0
  54. data/docs/decisions/053-shared-state-coordination-ownership.md +71 -0
  55. data/docs/decisions/054-workflow-terminal-save-single-owner.md +76 -0
  56. data/docs/decisions/055-terminal-observer-failure-settlement.md +60 -0
  57. data/docs/decisions/056-workflow-terminal-policy-ownership.md +87 -0
  58. data/docs/decisions/057-storage-transaction-boundaries.md +73 -0
  59. data/docs/decisions/058-neutral-storage-primitives.md +78 -0
  60. data/docs/decisions/README.md +35 -6
  61. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_DESIGN_V2.md +6 -0
  62. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md +6 -0
  63. data/docs/features.md +9 -3
  64. data/docs/getting-started.md +4 -4
  65. data/docs/migrations/0.15.md +5 -0
  66. data/docs/migrations/durable-semantic-coordination-v2.md +6 -0
  67. data/docs/migrations/handoff-runner-multi-agent.md +35 -0
  68. data/docs/migrations/neutral-storage-spi.md +43 -0
  69. data/docs/migrations/parallel-tool-chat-removal.md +49 -0
  70. data/docs/migrations/shared-state-multi-agent.md +44 -0
  71. data/docs/migrations/storage-backend-composition.md +117 -0
  72. data/docs/migrations/storage-transaction-boundaries.md +74 -0
  73. data/docs/persistence-backends.md +162 -204
  74. data/docs/runtime-and-concurrency.md +117 -32
  75. data/lib/phronomy/agent/api/agent.rb +17 -0
  76. data/lib/phronomy/agent/async_event_api.rb +2 -2
  77. data/lib/phronomy/agent/base.rb +51 -241
  78. data/lib/phronomy/{agent.rb → agent/composition/run_once.rb} +4 -13
  79. data/lib/phronomy/agent/context/capability/base.rb +67 -26
  80. data/lib/phronomy/agent/context/capability/tool_executor.rb +62 -0
  81. data/lib/phronomy/agent/{context_assembler.rb → context_assembly/context_assembler.rb} +143 -88
  82. data/lib/phronomy/agent/{context_importer.rb → context_assembly/context_importer.rb} +2 -2
  83. data/lib/phronomy/agent/{ruby_llm_materializer.rb → context_assembly/ruby_llm_materializer.rb} +4 -7
  84. data/lib/phronomy/agent/context_assembly/runtime_chat_builder.rb +36 -0
  85. data/lib/phronomy/agent/context_assembly/saved_context_reader.rb +53 -0
  86. data/lib/phronomy/agent/context_assembly/state_writer.rb +165 -0
  87. data/lib/phronomy/agent/context_assembly/token_budget_resolver.rb +22 -0
  88. data/lib/phronomy/agent/{context_plan.rb → context_contract/context_plan.rb} +1 -1
  89. data/lib/phronomy/agent/{context_policy_input.rb → context_contract/context_policy_input.rb} +6 -6
  90. data/lib/phronomy/agent/{llm_input_build_context.rb → context_contract/llm_input_build_context.rb} +1 -1
  91. data/lib/phronomy/agent/{llm_input_manifest.rb → context_contract/llm_input_manifest.rb} +20 -20
  92. data/lib/phronomy/agent/{llm_input_patch.rb → context_contract/llm_input_patch.rb} +2 -2
  93. data/lib/phronomy/agent/{agent_execution.rb → execution/agent_execution.rb} +8 -4
  94. data/lib/phronomy/agent/{agent_invocation.rb → execution/agent_invocation.rb} +8 -13
  95. data/lib/phronomy/agent/{agent_invocation_session_builder.rb → execution/agent_invocation_session_builder.rb} +19 -95
  96. data/lib/phronomy/agent/execution/approval_resume_commit.rb +108 -0
  97. data/lib/phronomy/agent/execution/dispatch_preparation.rb +305 -0
  98. data/lib/phronomy/agent/{exact_execution.rb → execution/exact_execution.rb} +9 -10
  99. data/lib/phronomy/agent/{execution_cancellation.rb → execution/execution_cancellation.rb} +2 -3
  100. data/lib/phronomy/agent/execution/execution_coordinator.rb +1925 -0
  101. data/lib/phronomy/agent/execution/execution_failure.rb +30 -0
  102. data/lib/phronomy/agent/execution/execution_metadata.rb +53 -0
  103. data/lib/phronomy/agent/execution/execution_outcome_committer.rb +344 -0
  104. data/lib/phronomy/agent/execution/execution_registry.rb +459 -0
  105. data/lib/phronomy/agent/execution/execution_session_runner.rb +118 -0
  106. data/lib/phronomy/agent/execution/initial_preparation.rb +421 -0
  107. data/lib/phronomy/agent/execution/invocation_transitions.rb +86 -0
  108. data/lib/phronomy/agent/{phase_machine_builder.rb → execution/phase_machine_builder.rb} +20 -71
  109. data/lib/phronomy/agent/{provider_call_outcome.rb → execution/provider_call_outcome.rb} +9 -9
  110. data/lib/phronomy/agent/execution/runtime_record_encoder.rb +210 -0
  111. data/lib/phronomy/agent/{handoff_context.rb → handoff/handoff_context.rb} +2 -2
  112. data/lib/phronomy/agent/handoff/handoff_execution_coordinator.rb +15 -0
  113. data/lib/phronomy/agent/handoff/handoff_outcome_committer.rb +131 -0
  114. data/lib/phronomy/agent/{handoff_state.rb → handoff/handoff_state.rb} +1 -1
  115. data/lib/phronomy/agent/{journal_projection.rb → journal/journal_projection.rb} +4 -0
  116. data/lib/phronomy/agent/{journal_record.rb → journal/journal_record.rb} +3 -3
  117. data/lib/phronomy/agent/{llm_call_record.rb → journal/llm_call_record.rb} +2 -2
  118. data/lib/phronomy/agent/{agent_root.rb → lifecycle/agent_root.rb} +6 -2
  119. data/lib/phronomy/agent/lifecycle/default_persistence.rb +29 -0
  120. data/lib/phronomy/{engine/runtime/agent_ownership_registry.rb → agent/lifecycle/ownership_registry.rb} +24 -10
  121. data/lib/phronomy/{agent_already_exists_error.rb → agent/lifecycle_contract/agent_already_exists_error.rb} +2 -0
  122. data/lib/phronomy/{agent_busy_error.rb → agent/lifecycle_contract/agent_busy_error.rb} +2 -0
  123. data/lib/phronomy/{agent_purged_error.rb → agent/lifecycle_contract/agent_purged_error.rb} +2 -0
  124. data/lib/phronomy/agent/lifecycle_contract/handoff_error.rb +7 -0
  125. data/lib/phronomy/{stream_callback_error.rb → agent/lifecycle_contract/stream_callback_error.rb} +2 -0
  126. data/lib/phronomy/agent/persistence/agent_repository.rb +61 -0
  127. data/lib/phronomy/agent/persistence/codec.rb +358 -0
  128. data/lib/phronomy/agent/persistence/execution_repository.rb +108 -0
  129. data/lib/phronomy/agent/persistence/handoff_state_repository.rb +58 -0
  130. data/lib/phronomy/agent/persistence/journal_repository.rb +54 -0
  131. data/lib/phronomy/agent/persistence/queries.rb +61 -0
  132. data/lib/phronomy/agent/persistence/storage_schema.rb +24 -0
  133. data/lib/phronomy/agent/persistence/watermark.rb +27 -0
  134. data/lib/phronomy/agent/recovery/invocation_restorer.rb +132 -0
  135. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/continuation.rb +21 -45
  136. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/installation.rb +32 -37
  137. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/resolution.rb +37 -40
  138. data/lib/phronomy/agent/{recovery_coordinator.rb → recovery/recovery_coordinator.rb} +6 -10
  139. data/lib/phronomy/agent/recovery/recovery_support.rb +227 -0
  140. data/lib/phronomy/agent/selection/candidate.rb +1 -1
  141. data/lib/phronomy/agent/{approval_evaluation_request.rb → tool_execution/approval_evaluation_request.rb} +1 -12
  142. data/lib/phronomy/agent/{tool_approval_request.rb → tool_execution/tool_approval_request.rb} +1 -10
  143. data/lib/phronomy/agent/tool_execution/tool_binding.rb +90 -0
  144. data/lib/phronomy/agent/{tool_call_intercepted.rb → tool_execution/tool_call_intercepted.rb} +2 -2
  145. data/lib/phronomy/agent/{tool_definition_set.rb → tool_execution/tool_definition_set.rb} +9 -4
  146. data/lib/phronomy/agent/{tool_invocation.rb → tool_execution/tool_invocation.rb} +69 -34
  147. data/lib/phronomy/common/configuration_error.rb +7 -0
  148. data/lib/phronomy/common/error.rb +5 -0
  149. data/lib/phronomy/{agent → common/values}/immutable.rb +9 -1
  150. data/lib/phronomy/common/values/serializable.rb +32 -0
  151. data/lib/phronomy/{configuration.rb → configuration/configuration.rb} +14 -5
  152. data/lib/phronomy/configuration/global_configuration.rb +26 -0
  153. data/lib/phronomy/content_store/storage_schema.rb +11 -0
  154. data/lib/phronomy/content_store/stored_contents.rb +43 -0
  155. data/lib/phronomy/engine/backpressure_error.rb +7 -0
  156. data/lib/phronomy/{blocking.rb → engine/blocking.rb} +1 -1
  157. data/lib/phronomy/engine/cancellation_error.rb +7 -0
  158. data/lib/phronomy/engine/concurrency/cancellation_token.rb +4 -0
  159. data/lib/phronomy/engine/concurrency/offload_pool.rb +4 -0
  160. data/lib/phronomy/engine/concurrency/operation_binding.rb +1 -1
  161. data/lib/phronomy/engine/concurrency/worker_input_restricted.rb +14 -0
  162. data/lib/phronomy/engine/event_loop.rb +158 -624
  163. data/lib/phronomy/engine/event_loop_reentrancy_error.rb +8 -0
  164. data/lib/phronomy/{execution.rb → engine/execution.rb} +0 -7
  165. data/lib/phronomy/{execution_cancellation_error.rb → engine/execution_cancellation_error.rb} +2 -0
  166. data/lib/phronomy/engine/execution_receiver.rb +65 -0
  167. data/lib/phronomy/{execution_timeout_error.rb → engine/execution_timeout_error.rb} +2 -0
  168. data/lib/phronomy/engine/fsm_protocol.rb +14 -0
  169. data/lib/phronomy/engine/fsm_session.rb +35 -28
  170. data/lib/phronomy/{invalid_async_entry_action_error.rb → engine/invalid_async_entry_action_error.rb} +2 -0
  171. data/lib/phronomy/{invalid_async_transition_action_error.rb → engine/invalid_async_transition_action_error.rb} +2 -0
  172. data/lib/phronomy/{invalid_async_workflow_action_error.rb → engine/invalid_async_workflow_action_error.rb} +2 -0
  173. data/lib/phronomy/engine/pool_shutdown_error.rb +7 -0
  174. data/lib/phronomy/engine/recursion_limit_error.rb +7 -0
  175. data/lib/phronomy/engine/runtime.rb +80 -84
  176. data/lib/phronomy/engine/runtime_shutdown_error.rb +7 -0
  177. data/lib/phronomy/engine/runtime_shutdown_reentrancy_error.rb +7 -0
  178. data/lib/phronomy/engine/scheduler_reentrancy_error.rb +9 -0
  179. data/lib/phronomy/engine/task_result.rb +4 -0
  180. data/lib/phronomy/engine/timeout_error.rb +7 -0
  181. data/lib/phronomy/filter/contract/filter_block_error.rb +14 -0
  182. data/lib/phronomy/generation/generator_verifier/agent_result_receiver.rb +89 -0
  183. data/lib/phronomy/generation/generator_verifier/pipeline_state.rb +57 -0
  184. data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +112 -0
  185. data/lib/phronomy/generation/generator_verifier.rb +118 -0
  186. data/lib/phronomy/generation/low_confidence_error.rb +14 -0
  187. data/lib/phronomy/llm_context_window/token_budget.rb +6 -7
  188. data/lib/phronomy/llm_contract/authentication_error.rb +7 -0
  189. data/lib/phronomy/{context_budget_exceeded_error.rb → llm_contract/context_budget_exceeded_error.rb} +2 -0
  190. data/lib/phronomy/llm_contract/context_length_error.rb +7 -0
  191. data/lib/phronomy/llm_contract/rate_limit_error.rb +7 -0
  192. data/lib/phronomy/{token_usage.rb → llm_contract/token_usage.rb} +2 -2
  193. data/lib/phronomy/llm_contract/transport_error.rb +7 -0
  194. data/lib/phronomy/multi_agent/admission_registry.rb +22 -2
  195. data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +5 -5
  196. data/lib/phronomy/{agent → multi_agent}/handoff_runner.rb +18 -17
  197. data/lib/phronomy/multi_agent/orchestrator.rb +2 -2
  198. data/lib/phronomy/multi_agent/persistence/codec.rb +55 -0
  199. data/lib/phronomy/multi_agent/persistence/queries.rb +30 -0
  200. data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +108 -0
  201. data/lib/phronomy/multi_agent/persistence/team_repository.rb +61 -0
  202. data/lib/phronomy/{agent → multi_agent}/shared_state.rb +56 -39
  203. data/lib/phronomy/multi_agent/storage_contract/team_storage_schema.rb +15 -0
  204. data/lib/phronomy/multi_agent/team_coordinator.rb +21 -18
  205. data/lib/phronomy/multi_agent/team_execution.rb +1 -1
  206. data/lib/phronomy/{engine/runtime → multi_agent}/team_ownership_registry.rb +14 -4
  207. data/lib/phronomy/multi_agent/team_root.rb +1 -1
  208. data/lib/phronomy/output_parser/contract/parse_error.rb +7 -0
  209. data/lib/phronomy/persistence/api/persistence.rb +140 -0
  210. data/lib/phronomy/persistence/migration/initial_format_migration.rb +19 -19
  211. data/lib/phronomy/persistence_composition/repositories.rb +77 -0
  212. data/lib/phronomy/persistence_composition/storage_schema.rb +24 -0
  213. data/lib/phronomy/{execution_rehydration_required_error.rb → recovery/execution_rehydration_required_error.rb} +2 -0
  214. data/lib/phronomy/{recovery.rb → recovery/recovery.rb} +1 -1
  215. data/lib/phronomy/runtime_composition/agent_defaults.rb +7 -0
  216. data/lib/phronomy/runtime_composition/configuration_defaults.rb +9 -0
  217. data/lib/phronomy/runtime_composition/global_runtime.rb +19 -0
  218. data/lib/phronomy/storage/backend.rb +101 -0
  219. data/lib/phronomy/storage/backends/in_memory.rb +157 -0
  220. data/lib/phronomy/storage/blob_conflict_error.rb +10 -0
  221. data/lib/phronomy/storage/blobs.rb +31 -0
  222. data/lib/phronomy/storage/condition.rb +25 -0
  223. data/lib/phronomy/storage/condition_failed_error.rb +16 -0
  224. data/lib/phronomy/storage/conflict_error.rb +9 -0
  225. data/lib/phronomy/{persistence → storage}/durable_record.rb +12 -12
  226. data/lib/phronomy/storage/entry.rb +33 -0
  227. data/lib/phronomy/storage/guard_ref.rb +13 -0
  228. data/lib/phronomy/storage/not_found_error.rb +9 -0
  229. data/lib/phronomy/storage/record_codec.rb +177 -0
  230. data/lib/phronomy/storage/records.rb +61 -0
  231. data/lib/phronomy/storage/resource.rb +126 -0
  232. data/lib/phronomy/storage/scope.rb +25 -0
  233. data/lib/phronomy/storage/serialization_error.rb +9 -0
  234. data/lib/phronomy/storage/streams.rb +44 -0
  235. data/lib/phronomy/storage/transaction_error.rb +10 -0
  236. data/lib/phronomy/storage/unique_constraint_error.rb +17 -0
  237. data/lib/phronomy/storage/unsupported_backend_error.rb +9 -0
  238. data/lib/phronomy/storage/validation.rb +53 -0
  239. data/lib/phronomy/storage/view.rb +131 -0
  240. data/lib/phronomy/testing/persistence_contract/a_content_store.rb +1 -1
  241. data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +4 -4
  242. data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +10 -7
  243. data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +2 -2
  244. data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +6 -6
  245. data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +6 -6
  246. data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +8 -8
  247. data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +263 -0
  248. data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +123 -0
  249. data/lib/phronomy/testing/persistence_contract.rb +4 -0
  250. data/lib/phronomy/tool/contract/tool_error.rb +7 -0
  251. data/lib/phronomy/tools/agent.rb +1 -1
  252. data/lib/phronomy/version.rb +1 -1
  253. data/lib/phronomy/{workflow.rb → workflow/execution/workflow.rb} +8 -6
  254. data/lib/phronomy/{workflow_context.rb → workflow/execution/workflow_context.rb} +4 -0
  255. data/lib/phronomy/workflow/execution/workflow_context_ownership_error.rb +7 -0
  256. data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +188 -0
  257. data/lib/phronomy/{workflow_runner.rb → workflow/execution/workflow_runner.rb} +122 -71
  258. data/lib/phronomy/workflow/execution/workflow_terminal_policy.rb +40 -0
  259. data/lib/phronomy/workflow/persistence/codec.rb +153 -0
  260. data/lib/phronomy/workflow/persistence/state_repository.rb +57 -0
  261. data/lib/phronomy/workflow/phase_machine_builder.rb +2 -2
  262. data/lib/phronomy/workflow/storage_contract/workflow_storage_schema.rb +9 -0
  263. data/lib/phronomy.rb +52 -94
  264. data/scripts/api_snapshot.rb +1 -1
  265. data/scripts/storage_spi_snapshot.rb +36 -0
  266. data/sig/phronomy/agent.rbs +0 -1
  267. data/sig/phronomy/execution_receiver.rbs +34 -0
  268. data/sig/phronomy/handoff.rbs +4 -2
  269. data/sig/phronomy/multi_agent.rbs +18 -0
  270. data/sig/phronomy/persistence.rbs +7 -91
  271. data/sig/phronomy/storage.rbs +174 -0
  272. data/sig/phronomy/tool.rbs +10 -1
  273. metadata +230 -99
  274. data/lib/phronomy/agent/execution_coordinator.rb +0 -3151
  275. data/lib/phronomy/agent/handoff_execution_coordinator.rb +0 -143
  276. data/lib/phronomy/agent/recovery_support.rb +0 -504
  277. data/lib/phronomy/agent/token_budget_resolver.rb +0 -70
  278. data/lib/phronomy/agent/tool_executor.rb +0 -55
  279. data/lib/phronomy/generator_verifier.rb +0 -369
  280. data/lib/phronomy/invalid_context_budget_configuration_error.rb +0 -8
  281. data/lib/phronomy/multi_agent/parallel_tool_chat.rb +0 -116
  282. data/lib/phronomy/persistence/durable_codec.rb +0 -706
  283. data/lib/phronomy/persistence/in_memory.rb +0 -690
  284. data/lib/phronomy/persistence/repository_facades.rb +0 -535
  285. data/lib/phronomy/persistence.rb +0 -276
  286. data/lib/phronomy/ruby_llm_patches.rb +0 -24
  287. data/lib/phronomy/workflow_recovery.rb +0 -123
  288. /data/lib/phronomy/agent/{context_candidate_resolver.rb → context_assembly/context_candidate_resolver.rb} +0 -0
  289. /data/lib/phronomy/agent/{context_policy_input_builder.rb → context_assembly/context_policy_input_builder.rb} +0 -0
  290. /data/lib/phronomy/agent/{context_plan_validator.rb → context_contract/context_plan_validator.rb} +0 -0
  291. /data/lib/phronomy/agent/{context_policy.rb → context_contract/context_policy.rb} +0 -0
  292. /data/lib/phronomy/agent/{llm_operation_result.rb → execution/llm_operation_result.rb} +0 -0
  293. /data/lib/phronomy/agent/{handoff.rb → handoff/handoff.rb} +0 -0
  294. /data/lib/phronomy/agent/{handoff_capability_factory.rb → handoff/handoff_capability_factory.rb} +0 -0
  295. /data/lib/phronomy/agent/{handoff_policy.rb → handoff/handoff_policy.rb} +0 -0
  296. /data/lib/phronomy/agent/{handoff_projection.rb → handoff/handoff_projection.rb} +0 -0
  297. /data/lib/phronomy/agent/{handoff_request.rb → handoff/handoff_request.rb} +0 -0
  298. /data/lib/phronomy/agent/{tool_invocation_session_builder.rb → tool_execution/tool_invocation_session_builder.rb} +0 -0
  299. /data/lib/phronomy/{canonical_json.rb → common/canonical_json.rb} +0 -0
  300. /data/lib/phronomy/{diagnostics.rb → engine/diagnostics.rb} +0 -0
  301. /data/lib/phronomy/{event.rb → engine/event.rb} +0 -0
  302. /data/lib/phronomy/{invocation_context.rb → engine/invocation_context.rb} +0 -0
  303. /data/lib/phronomy/{metrics.rb → engine/metrics.rb} +0 -0
  304. /data/lib/phronomy/{runnable.rb → engine/runnable.rb} +0 -0
@@ -1,535 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phronomy
4
- class Persistence
5
- # Runtime/domain-facing repositories layered over record-oriented Backend SPI
6
- # repositories. Backend implementations never receive AgentRoot,
7
- # AgentExecution, JournalRecord, or Workflow snapshot domain values directly.
8
- #
9
- # Backend repositories receive DurableRecord plus explicit identity/revision/
10
- # admission metadata needed for indexing and compare-and-swap. They must not
11
- # inspect DurableRecord#payload to rediscover those semantics.
12
- #
13
- # @api private
14
- module RepositoryFacades
15
- class Agents
16
- def initialize(backend_repository)
17
- @backend_repository = backend_repository
18
- end
19
-
20
- def create(root)
21
- record = DurableCodec.encode_agent_root(root)
22
- stored = @backend_repository.create(
23
- agent_id: root.agent_id.to_s,
24
- agent_revision: Integer(root.agent_revision),
25
- record: record
26
- )
27
- decode_for_agent(stored, root.agent_id, revision: root.agent_revision)
28
- end
29
-
30
- def load(agent_id)
31
- decode_for_agent(@backend_repository.load(agent_id.to_s), agent_id)
32
- end
33
-
34
- def save(agent_id, expected_revision:, root:)
35
- expected = Integer(expected_revision)
36
- next_revision = Integer(root.agent_revision)
37
- unless next_revision == expected + 1
38
- raise Phronomy::Persistence::ConflictError,
39
- "agent save must advance revision exactly once: " \
40
- "expected #{expected + 1}, got #{next_revision}"
41
- end
42
- unless root.agent_id.to_s == agent_id.to_s
43
- raise Phronomy::Persistence::SerializationError,
44
- "Agent root identity mismatch: #{root.agent_id} != #{agent_id}"
45
- end
46
-
47
- record = DurableCodec.encode_agent_root(root)
48
- stored = @backend_repository.save(
49
- agent_id.to_s,
50
- expected_revision: expected,
51
- next_revision: next_revision,
52
- record: record
53
- )
54
- decode_for_agent(stored, agent_id, revision: next_revision)
55
- end
56
-
57
- def delete(agent_id)
58
- @backend_repository.delete(agent_id.to_s)
59
- end
60
-
61
- private
62
-
63
- def decode_for_agent(record, agent_id, revision: nil)
64
- root = DurableCodec.decode_agent_root(record)
65
- unless root.agent_id == agent_id.to_s
66
- raise Phronomy::Persistence::SerializationError,
67
- "backend returned Agent root for #{root.agent_id.inspect}; expected #{agent_id.to_s.inspect}"
68
- end
69
- if revision && root.agent_revision != revision
70
- raise Phronomy::Persistence::SerializationError,
71
- "backend returned Agent revision #{root.agent_revision}; expected #{revision}"
72
- end
73
- root
74
- end
75
- end
76
-
77
- class Journals
78
- def initialize(backend_repository)
79
- @backend_repository = backend_repository
80
- end
81
-
82
- def append(agent_id, expected_position:, records:)
83
- expected = Integer(expected_position)
84
- sequenced = Array(records).each_with_index.map do |record, index|
85
- unless record.agent_id.to_s == agent_id.to_s
86
- raise Phronomy::Persistence::SerializationError,
87
- "Journal record Agent mismatch: #{record.agent_id} != #{agent_id}"
88
- end
89
- record.with_sequence(expected + index + 1)
90
- end
91
- encoded = sequenced.map { |record| DurableCodec.encode_journal_record(record) }
92
- stored = @backend_repository.append(
93
- agent_id.to_s,
94
- expected_position: expected,
95
- records: encoded,
96
- record_ids: sequenced.map { |record| record.record_id.to_s }.freeze
97
- )
98
- decoded = Array(stored).map { |record| DurableCodec.decode_journal_record(record) }
99
- validate_read!(decoded, agent_id, start_sequence: expected + 1)
100
- end
101
-
102
- def read(agent_id, after: nil, limit: nil)
103
- after_value = after.nil? ? nil : Integer(after)
104
- limit_value = limit.nil? ? nil : Integer(limit)
105
- stored = @backend_repository.read(
106
- agent_id.to_s,
107
- after: after_value,
108
- limit: limit_value
109
- )
110
- decoded = Array(stored).map { |record| DurableCodec.decode_journal_record(record) }
111
- validate_read!(decoded, agent_id, start_sequence: (after_value || 0) + 1)
112
- end
113
-
114
- def head(agent_id)
115
- Integer(@backend_repository.head(agent_id.to_s))
116
- end
117
-
118
- def delete(agent_id)
119
- @backend_repository.delete(agent_id.to_s)
120
- end
121
-
122
- private
123
-
124
- def validate_read!(records, agent_id, start_sequence:)
125
- records.each_with_index do |record, index|
126
- unless record.agent_id == agent_id.to_s
127
- raise Phronomy::Persistence::SerializationError,
128
- "backend returned Journal record for #{record.agent_id.inspect}; expected #{agent_id.to_s.inspect}"
129
- end
130
- expected_sequence = start_sequence + index
131
- unless record.sequence == expected_sequence
132
- raise Phronomy::Persistence::SerializationError,
133
- "backend returned Journal sequence #{record.sequence.inspect}; expected #{expected_sequence}"
134
- end
135
- end
136
- records.freeze
137
- end
138
- end
139
-
140
- class Executions
141
- def initialize(backend_repository)
142
- @backend_repository = backend_repository
143
- end
144
-
145
- def create_active(execution)
146
- unless execution.active?
147
- raise Phronomy::Persistence::SerializationError,
148
- "create_active requires an active AgentExecution"
149
- end
150
- record = DurableCodec.encode_agent_execution(execution)
151
- stored = @backend_repository.create_active(
152
- execution_id: execution.execution_id.to_s,
153
- agent_id: execution.agent_id.to_s,
154
- execution_revision: Integer(execution.execution_revision),
155
- record: record
156
- )
157
- decode_for_execution(
158
- stored,
159
- execution.execution_id,
160
- agent_id: execution.agent_id,
161
- revision: execution.execution_revision,
162
- active: true
163
- )
164
- end
165
-
166
- def load(execution_id)
167
- decode_for_execution(@backend_repository.load(execution_id.to_s), execution_id)
168
- end
169
-
170
- def save(execution_id, expected_revision:, execution:)
171
- expected = Integer(expected_revision)
172
- next_revision = Integer(execution.execution_revision)
173
- unless next_revision == expected + 1
174
- raise Phronomy::Persistence::ConflictError,
175
- "execution save must advance revision exactly once: " \
176
- "expected #{expected + 1}, got #{next_revision}"
177
- end
178
- unless execution.execution_id.to_s == execution_id.to_s
179
- raise Phronomy::Persistence::SerializationError,
180
- "Execution identity mismatch: #{execution.execution_id} != #{execution_id}"
181
- end
182
-
183
- record = DurableCodec.encode_agent_execution(execution)
184
- stored = @backend_repository.save(
185
- execution_id.to_s,
186
- expected_revision: expected,
187
- next_revision: next_revision,
188
- agent_id: execution.agent_id.to_s,
189
- active: execution.active?,
190
- record: record
191
- )
192
- decode_for_execution(
193
- stored,
194
- execution_id,
195
- agent_id: execution.agent_id,
196
- revision: next_revision,
197
- active: execution.active?
198
- )
199
- end
200
-
201
- def list_active(agent_id)
202
- Array(@backend_repository.list_active(agent_id.to_s)).map do |record|
203
- decode_for_execution(record, nil, agent_id: agent_id, active: true)
204
- end.freeze
205
- end
206
-
207
- def list(agent_id, after: nil, limit: 100)
208
- raise ArgumentError, "limit must be a positive Integer" unless limit.is_a?(Integer) && limit.positive?
209
- Array(@backend_repository.list(agent_id.to_s, after: after&.to_s, limit: limit)).map do |record|
210
- decode_for_execution(record, nil, agent_id: agent_id)
211
- end.freeze
212
- end
213
-
214
- def delete(execution_id)
215
- @backend_repository.delete(execution_id.to_s)
216
- end
217
-
218
- def delete_for_agent(agent_id)
219
- @backend_repository.delete_for_agent(agent_id.to_s)
220
- end
221
-
222
- def assert_idle!(agent_id)
223
- @backend_repository.assert_idle!(agent_id.to_s)
224
- end
225
-
226
- private
227
-
228
- def decode_for_execution(record, execution_id, agent_id: nil, revision: nil, active: nil)
229
- execution = DurableCodec.decode_agent_execution(record)
230
- if execution_id && execution.execution_id != execution_id.to_s
231
- raise Phronomy::Persistence::SerializationError,
232
- "backend returned Execution #{execution.execution_id.inspect}; expected #{execution_id.to_s.inspect}"
233
- end
234
- if agent_id && execution.agent_id != agent_id.to_s
235
- raise Phronomy::Persistence::SerializationError,
236
- "backend returned Execution for Agent #{execution.agent_id.inspect}; expected #{agent_id.to_s.inspect}"
237
- end
238
- if revision && execution.execution_revision != revision
239
- raise Phronomy::Persistence::SerializationError,
240
- "backend returned Execution revision #{execution.execution_revision}; expected #{revision}"
241
- end
242
- if !active.nil? && execution.active? != active
243
- raise Phronomy::Persistence::SerializationError,
244
- "backend returned Execution active=#{execution.active?}; expected #{active}"
245
- end
246
- execution
247
- end
248
- end
249
-
250
- class Teams
251
- def initialize(backend_repository)
252
- @backend_repository = backend_repository
253
- end
254
-
255
- def create(root)
256
- record = DurableCodec.encode_team_root(root)
257
- stored = @backend_repository.create(
258
- team_id: root.team_id.to_s,
259
- team_revision: Integer(root.team_revision),
260
- record: record
261
- )
262
- decode_for_team(stored, root.team_id, revision: root.team_revision)
263
- end
264
-
265
- def load(team_id)
266
- decode_for_team(@backend_repository.load(team_id.to_s), team_id)
267
- end
268
-
269
- def save(team_id, expected_revision:, root:)
270
- expected = Integer(expected_revision)
271
- next_revision = Integer(root.team_revision)
272
- unless next_revision == expected + 1
273
- raise Phronomy::Persistence::ConflictError,
274
- "agent save must advance revision exactly once: " \
275
- "expected #{expected + 1}, got #{next_revision}"
276
- end
277
- unless root.team_id.to_s == team_id.to_s
278
- raise Phronomy::Persistence::SerializationError,
279
- "Team root identity mismatch: #{root.team_id} != #{team_id}"
280
- end
281
-
282
- record = DurableCodec.encode_team_root(root)
283
- stored = @backend_repository.save(
284
- team_id.to_s,
285
- expected_revision: expected,
286
- next_revision: next_revision,
287
- record: record
288
- )
289
- decode_for_team(stored, team_id, revision: next_revision)
290
- end
291
-
292
- def delete(team_id)
293
- @backend_repository.delete(team_id.to_s)
294
- end
295
-
296
- private
297
-
298
- def decode_for_team(record, team_id, revision: nil)
299
- root = DurableCodec.decode_team_root(record)
300
- unless root.team_id == team_id.to_s
301
- raise Phronomy::Persistence::SerializationError,
302
- "backend returned Team root for #{root.team_id.inspect}; expected #{team_id.to_s.inspect}"
303
- end
304
- if revision && root.team_revision != revision
305
- raise Phronomy::Persistence::SerializationError,
306
- "backend returned Team revision #{root.team_revision}; expected #{revision}"
307
- end
308
- root
309
- end
310
- end
311
-
312
- class TeamExecutions
313
- def initialize(backend_repository)
314
- @backend_repository = backend_repository
315
- end
316
-
317
- def create_active(execution)
318
- unless execution.active?
319
- raise Phronomy::Persistence::SerializationError,
320
- "create_active requires an active TeamExecution"
321
- end
322
- record = DurableCodec.encode_team_execution(execution)
323
- stored = @backend_repository.create_active(
324
- team_execution_id: execution.team_execution_id.to_s,
325
- team_id: execution.team_id.to_s,
326
- execution_revision: Integer(execution.execution_revision),
327
- record: record
328
- )
329
- decode_for_execution(
330
- stored,
331
- execution.team_execution_id,
332
- team_id: execution.team_id,
333
- revision: execution.execution_revision,
334
- active: true
335
- )
336
- end
337
-
338
- def load(team_execution_id)
339
- decode_for_execution(@backend_repository.load(team_execution_id.to_s), team_execution_id)
340
- end
341
-
342
- def save(team_execution_id, expected_revision:, execution:)
343
- expected = Integer(expected_revision)
344
- next_revision = Integer(execution.execution_revision)
345
- unless next_revision == expected + 1
346
- raise Phronomy::Persistence::ConflictError,
347
- "execution save must advance revision exactly once: " \
348
- "expected #{expected + 1}, got #{next_revision}"
349
- end
350
- unless execution.team_execution_id.to_s == team_execution_id.to_s
351
- raise Phronomy::Persistence::SerializationError,
352
- "Execution identity mismatch: #{execution.team_execution_id} != #{team_execution_id}"
353
- end
354
-
355
- record = DurableCodec.encode_team_execution(execution)
356
- stored = @backend_repository.save(
357
- team_execution_id.to_s,
358
- expected_revision: expected,
359
- next_revision: next_revision,
360
- team_id: execution.team_id.to_s,
361
- active: execution.active?,
362
- record: record
363
- )
364
- decode_for_execution(
365
- stored,
366
- team_execution_id,
367
- team_id: execution.team_id,
368
- revision: next_revision,
369
- active: execution.active?
370
- )
371
- end
372
-
373
- def list_active(team_id)
374
- Array(@backend_repository.list_active(team_id.to_s)).map do |record|
375
- decode_for_execution(record, nil, team_id: team_id, active: true)
376
- end.freeze
377
- end
378
-
379
- def list(team_id, after: nil, limit: 100)
380
- raise ArgumentError, "limit must be a positive Integer" unless limit.is_a?(Integer) && limit.positive?
381
- Array(@backend_repository.list(team_id.to_s, after: after&.to_s, limit: limit)).map do |record|
382
- decode_for_execution(record, nil, team_id: team_id)
383
- end.freeze
384
- end
385
-
386
- def delete(team_execution_id)
387
- @backend_repository.delete(team_execution_id.to_s)
388
- end
389
-
390
- def delete_for_team(team_id)
391
- @backend_repository.delete_for_team(team_id.to_s)
392
- end
393
-
394
- def assert_idle!(team_id)
395
- @backend_repository.assert_idle!(team_id.to_s)
396
- end
397
-
398
- private
399
-
400
- def decode_for_execution(record, team_execution_id, team_id: nil, revision: nil, active: nil)
401
- execution = DurableCodec.decode_team_execution(record)
402
- if team_execution_id && execution.team_execution_id != team_execution_id.to_s
403
- raise Phronomy::Persistence::SerializationError,
404
- "backend returned Execution #{execution.team_execution_id.inspect}; expected #{team_execution_id.to_s.inspect}"
405
- end
406
- if team_id && execution.team_id != team_id.to_s
407
- raise Phronomy::Persistence::SerializationError,
408
- "backend returned Execution for Agent #{execution.team_id.inspect}; expected #{team_id.to_s.inspect}"
409
- end
410
- if revision && execution.execution_revision != revision
411
- raise Phronomy::Persistence::SerializationError,
412
- "backend returned Execution revision #{execution.execution_revision}; expected #{revision}"
413
- end
414
- if !active.nil? && execution.active? != active
415
- raise Phronomy::Persistence::SerializationError,
416
- "backend returned Execution active=#{execution.active?}; expected #{active}"
417
- end
418
- execution
419
- end
420
- end
421
-
422
- class HandoffStates
423
- def initialize(backend_repository) = @backend_repository = backend_repository
424
-
425
- def load(main_agent_id)
426
- record = @backend_repository.load(main_agent_id.to_s)
427
- record && decode(record, main_agent_id)
428
- end
429
-
430
- def save(main_agent_id, expected_revision:, state:)
431
- expected = expected_revision.nil? ? nil : Integer(expected_revision)
432
- next_revision = expected.nil? ? 1 : expected + 1
433
- unless state.main_agent_id == main_agent_id.to_s && state.handoff_revision == next_revision
434
- raise Phronomy::Persistence::SerializationError, "Handoff identity/revision mismatch"
435
- end
436
- record = @backend_repository.save(main_agent_id.to_s,
437
- expected_revision: expected, next_revision: next_revision,
438
- active_agent_id: state.active_agent_id, record: DurableCodec.encode_handoff_state(state))
439
- decode(record, main_agent_id, revision: next_revision)
440
- end
441
-
442
- def delete(main_agent_id, expected_revision:)
443
- @backend_repository.delete(main_agent_id.to_s, expected_revision: Integer(expected_revision))
444
- end
445
-
446
- private
447
-
448
- def decode(record, main_agent_id, revision: nil)
449
- state = DurableCodec.decode_handoff_state(record)
450
- identity_matches = state.main_agent_id == main_agent_id.to_s
451
- revision_matches = revision.nil? || state.handoff_revision == revision
452
- unless identity_matches && revision_matches
453
- raise Phronomy::Persistence::SerializationError, "Backend returned another Handoff identity/revision"
454
- end
455
- state
456
- end
457
- end
458
-
459
- class WorkflowStates
460
- def initialize(backend_repository)
461
- @backend_repository = backend_repository
462
- end
463
-
464
- def load(workflow_instance_id)
465
- record = @backend_repository.load(workflow_instance_id.to_s)
466
- return nil unless record
467
-
468
- DurableCodec.decode_workflow_state(
469
- record,
470
- expected_workflow_instance_id: workflow_instance_id
471
- )
472
- end
473
-
474
- def save(workflow_instance_id, expected_revision:, snapshot:)
475
- expected = expected_revision.nil? ? nil : Integer(expected_revision)
476
- next_revision = expected.nil? ? 1 : expected + 1
477
- record = DurableCodec.encode_workflow_state(
478
- workflow_instance_id: workflow_instance_id,
479
- workflow_revision: next_revision,
480
- snapshot: snapshot
481
- )
482
- stored = @backend_repository.save(
483
- workflow_instance_id.to_s,
484
- expected_revision: expected,
485
- next_revision: next_revision,
486
- record: record
487
- )
488
- decoded = DurableCodec.decode_workflow_state(
489
- stored,
490
- expected_workflow_instance_id: workflow_instance_id
491
- )
492
- unless decoded.fetch(:revision) == next_revision
493
- raise Phronomy::Persistence::SerializationError,
494
- "backend returned Workflow revision #{decoded.fetch(:revision)}; expected #{next_revision}"
495
- end
496
- decoded.fetch(:revision)
497
- end
498
-
499
- def delete(workflow_instance_id, expected_revision:)
500
- @backend_repository.delete(
501
- workflow_instance_id.to_s,
502
- expected_revision: Integer(expected_revision)
503
- )
504
- end
505
- end
506
-
507
- # Transaction-scoped domain-facing Persistence view built from raw backend
508
- # repositories. Backend implementations can use this through
509
- # Persistence#build_transaction_view without duplicating facade logic.
510
- class View
511
- attr_reader :contents, :agents, :journals, :executions, :workflow_states, :handoff_states, :teams, :team_executions
512
-
513
- def initialize(contents:, agents:, journals:, executions:, workflow_states:, handoff_states:, teams:, team_executions:, watermark:)
514
- @contents = contents
515
- @agents = Agents.new(agents)
516
- @journals = Journals.new(journals)
517
- @executions = Executions.new(executions)
518
- @workflow_states = WorkflowStates.new(workflow_states)
519
- @handoff_states = HandoffStates.new(handoff_states)
520
- @teams = Teams.new(teams)
521
- @team_executions = TeamExecutions.new(team_executions)
522
- @watermark = watermark
523
- end
524
-
525
- def assert_agent_watermark!(agent_id:, agent_revision:, journal_position:)
526
- @watermark.assert_agent_watermark!(
527
- agent_id: agent_id.to_s,
528
- agent_revision: Integer(agent_revision),
529
- journal_position: Integer(journal_position)
530
- )
531
- end
532
- end
533
- end
534
- end
535
- end