phronomy 0.27.0 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (517) hide show
  1. checksums.yaml +4 -4
  2. data/.mutant.yml +9 -9
  3. data/CHANGELOG.md +52 -0
  4. data/CONTRIBUTING.md +8 -8
  5. data/README.md +28 -7
  6. data/benchmark/bench_agent_invoke.rb +25 -50
  7. data/benchmark/bench_context_assembler.rb +14 -11
  8. data/benchmark/bench_regression.rb +1 -1
  9. data/benchmark/bench_tool_schema.rb +1 -1
  10. data/docs/application-recipes.md +229 -0
  11. data/docs/architecture/agent-chat-and-state-ownership.md +7 -1
  12. data/docs/architecture/agent-transition-ownership.md +13 -89
  13. data/docs/architecture/backend-groups.md +152 -0
  14. data/docs/architecture/before-llm-input.md +3 -3
  15. data/docs/architecture/context-management.md +1 -1
  16. data/docs/architecture/dx05-agent-scope.md +119 -0
  17. data/docs/architecture/dx05-workflow-task-state.md +134 -0
  18. data/docs/architecture/error-api-review.md +245 -0
  19. data/docs/architecture/execution-abstraction-review.md +88 -0
  20. data/docs/architecture/execution-boundaries.md +28 -0
  21. data/docs/architecture/execution-metadata-and-values.md +6 -0
  22. data/docs/architecture/multi-agent-handoff.md +6 -5
  23. data/docs/architecture/persistence.md +27 -9
  24. data/docs/architecture/r8-unit1.md +104 -0
  25. data/docs/architecture/r8-unit10.md +72 -0
  26. data/docs/architecture/r8-unit11.md +71 -0
  27. data/docs/architecture/r8-unit12.md +67 -0
  28. data/docs/architecture/r8-unit13.md +61 -0
  29. data/docs/architecture/r8-unit14.md +72 -0
  30. data/docs/architecture/r8-unit15.md +65 -0
  31. data/docs/architecture/r8-unit16.md +54 -0
  32. data/docs/architecture/r8-unit17.md +73 -0
  33. data/docs/architecture/r8-unit18.md +92 -0
  34. data/docs/architecture/r8-unit19.md +61 -0
  35. data/docs/architecture/r8-unit2.md +55 -0
  36. data/docs/architecture/r8-unit20.md +51 -0
  37. data/docs/architecture/r8-unit21.md +79 -0
  38. data/docs/architecture/r8-unit22.md +74 -0
  39. data/docs/architecture/r8-unit23.md +81 -0
  40. data/docs/architecture/r8-unit3.md +130 -0
  41. data/docs/architecture/r8-unit4.md +79 -0
  42. data/docs/architecture/r8-unit5.md +115 -0
  43. data/docs/architecture/r8-unit6.md +71 -0
  44. data/docs/architecture/r8-unit7.md +70 -0
  45. data/docs/architecture/r8-unit8.md +78 -0
  46. data/docs/architecture/r8-unit9.md +73 -0
  47. data/docs/architecture/refactoring-closure.md +10 -1
  48. data/docs/architecture/remaining-refactoring-plan.md +16 -0
  49. data/docs/architecture/storage-execution-boundaries.md +54 -0
  50. data/docs/architecture/tool-schema-recording-gap.md +6 -0
  51. data/docs/architecture/tracing.md +1 -1
  52. data/docs/architecture/workflow-terminal-ownership-design.md +2 -2
  53. data/docs/architecture.md +24 -11
  54. data/docs/decisions/015-tool-public-facade-and-rbs-boundary.md +3 -3
  55. data/docs/decisions/027-llm-adapter-provider-boundary.md +2 -0
  56. data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +2 -0
  57. data/docs/decisions/031-durable-multi-agent-coordination.md +2 -0
  58. data/docs/decisions/032-storage-backend-composition.md +5 -0
  59. data/docs/decisions/034-handoff-runner-coordination-ownership.md +2 -0
  60. data/docs/decisions/035-tool-executor-capability-ownership.md +1 -1
  61. data/docs/decisions/038-responsibility-based-source-layout.md +14 -0
  62. data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +8 -0
  63. data/docs/decisions/040-configuration-default-composition.md +12 -1
  64. data/docs/decisions/045-worker-input-restriction-ownership.md +4 -0
  65. data/docs/decisions/051-execution-outcome-worker-ownership.md +2 -0
  66. data/docs/decisions/056-workflow-terminal-policy-ownership.md +1 -1
  67. data/docs/decisions/058-neutral-storage-primitives.md +5 -0
  68. data/docs/decisions/059-backend-contracts-and-async-clients.md +74 -0
  69. data/docs/decisions/060-runtime-settings-and-application-configuration.md +86 -0
  70. data/docs/decisions/061-persistence-failure-contracts.md +89 -0
  71. data/docs/decisions/062-content-store-service.md +52 -0
  72. data/docs/decisions/063-execution-contracts-and-services.md +88 -0
  73. data/docs/decisions/064-agent-execution-and-multi-agent-responsibility.md +36 -0
  74. data/docs/decisions/065-vector-and-embeddings-common-operations.md +38 -0
  75. data/docs/decisions/066-llm-and-tool-operation-contracts.md +20 -0
  76. data/docs/decisions/067-agent-progress-and-engine-connection.md +29 -0
  77. data/docs/decisions/068-agent-tool-child-progress-and-tool-operation.md +36 -0
  78. data/docs/decisions/README.md +18 -1
  79. data/docs/execution-boundaries-preview.md +45 -0
  80. data/docs/failure-reason-codes.md +45 -0
  81. data/docs/features.md +12 -4
  82. data/docs/getting-started.md +52 -48
  83. data/docs/migrations/0.22.md +2 -2
  84. data/docs/migrations/durable-semantic-coordination-v2.md +2 -0
  85. data/docs/migrations/handoff-runner-multi-agent.md +2 -0
  86. data/docs/migrations/persistence-failure-contracts.md +67 -0
  87. data/docs/migrations/r8-unit10.md +36 -0
  88. data/docs/migrations/r8-unit11.md +33 -0
  89. data/docs/migrations/r8-unit12.md +31 -0
  90. data/docs/migrations/r8-unit13.md +36 -0
  91. data/docs/migrations/r8-unit14.md +31 -0
  92. data/docs/migrations/r8-unit15.md +31 -0
  93. data/docs/migrations/r8-unit16.md +26 -0
  94. data/docs/migrations/r8-unit17.md +28 -0
  95. data/docs/migrations/r8-unit18.md +29 -0
  96. data/docs/migrations/r8-unit19.md +28 -0
  97. data/docs/migrations/r8-unit20.md +24 -0
  98. data/docs/migrations/r8-unit21.md +28 -0
  99. data/docs/migrations/r8-unit22.md +29 -0
  100. data/docs/migrations/r8-unit23.md +20 -0
  101. data/docs/migrations/r8-unit4.md +67 -0
  102. data/docs/migrations/r8-unit5.md +75 -0
  103. data/docs/migrations/r8-unit6.md +109 -0
  104. data/docs/migrations/r8-unit7.md +31 -0
  105. data/docs/migrations/r8-unit8.md +33 -0
  106. data/docs/migrations/r8-unit9.md +28 -0
  107. data/docs/migrations/storage-async-client.md +103 -0
  108. data/docs/migrations/vector-async-clients.md +110 -0
  109. data/docs/ordered-event-delivery.md +54 -0
  110. data/docs/persistence-backends.md +38 -21
  111. data/docs/runtime-and-concurrency.md +26 -1
  112. data/docs/workflow-completion.md +92 -0
  113. data/gemfiles/http_minimum.gemfile +5 -0
  114. data/lib/phronomy/agent/admission.rb +78 -0
  115. data/lib/phronomy/agent/{lifecycle_contract/agent_already_exists_error.rb → agent_already_exists_error.rb} +1 -1
  116. data/lib/phronomy/agent/{lifecycle_contract/agent_busy_error.rb → agent_busy_error.rb} +1 -1
  117. data/lib/phronomy/agent/agent_not_found_error.rb +16 -0
  118. data/lib/phronomy/agent/{lifecycle_contract/agent_purged_error.rb → agent_purged_error.rb} +1 -1
  119. data/lib/phronomy/agent/agent_unloaded_error.rb +14 -0
  120. data/lib/phronomy/agent/api/agent.rb +13 -0
  121. data/lib/phronomy/{recovery → agent/api}/execution_rehydration_required_error.rb +1 -1
  122. data/lib/phronomy/agent/async_event_api.rb +132 -103
  123. data/lib/phronomy/agent/base.rb +146 -113
  124. data/lib/phronomy/agent/before_llm_input.rb +70 -0
  125. data/lib/phronomy/agent/composition/run_once.rb +1 -1
  126. data/lib/phronomy/agent/{context_assembly/context_candidate_resolver.rb → context_candidate_resolver.rb} +2 -2
  127. data/lib/phronomy/agent/{context_assembly/context_assembler.rb → context_preparation.rb} +35 -221
  128. data/lib/phronomy/agent/control_request.rb +21 -0
  129. data/lib/phronomy/agent/execution/agent_execution.rb +1 -1
  130. data/lib/phronomy/agent/execution/agent_invocation.rb +27 -53
  131. data/lib/phronomy/agent/execution/approval_resume_commit.rb +1 -1
  132. data/lib/phronomy/agent/execution/dispatch_preparation.rb +17 -127
  133. data/lib/phronomy/agent/execution/exact_execution.rb +55 -32
  134. data/lib/phronomy/agent/execution/execution_cancellation.rb +8 -4
  135. data/lib/phronomy/agent/execution/execution_coordinator.rb +229 -407
  136. data/lib/phronomy/agent/execution/execution_metadata.rb +1 -0
  137. data/lib/phronomy/agent/execution/execution_outcome_committer.rb +71 -127
  138. data/lib/phronomy/agent/execution/execution_registry.rb +6 -2
  139. data/lib/phronomy/agent/execution/execution_session_runner.rb +22 -27
  140. data/lib/phronomy/agent/execution/initial_preparation.rb +19 -120
  141. data/lib/phronomy/agent/execution/{agent_invocation_session_builder.rb → invocation_actions.rb} +54 -194
  142. data/lib/phronomy/agent/execution/invocation_transitions.rb +39 -1
  143. data/lib/phronomy/agent/execution/runtime_record_encoder.rb +8 -11
  144. data/lib/phronomy/agent/execution_change.rb +161 -0
  145. data/lib/phronomy/agent/execution_environment.rb +36 -0
  146. data/lib/phronomy/agent/execution_extension_state.rb +21 -0
  147. data/lib/phronomy/agent/execution_observation.rb +35 -0
  148. data/lib/phronomy/agent/filterable.rb +188 -0
  149. data/lib/phronomy/agent/handoff/{handoff_context.rb → transfer_context.rb} +4 -4
  150. data/lib/phronomy/agent/handoff/{handoff_projection.rb → transfer_projection.rb} +14 -33
  151. data/lib/phronomy/{llm_contract/transport_error.rb → agent/handoff_error.rb} +1 -1
  152. data/lib/phronomy/agent/journal/journal_projection.rb +1 -1
  153. data/lib/phronomy/agent/knowledge_item.rb +13 -0
  154. data/lib/phronomy/agent/lifecycle/agent_root.rb +1 -1
  155. data/lib/phronomy/agent/lifecycle/lifetime_api.rb +76 -0
  156. data/lib/phronomy/agent/lifecycle/operation_lease.rb +33 -0
  157. data/lib/phronomy/agent/lifecycle/operation_result.rb +53 -0
  158. data/lib/phronomy/agent/lifecycle/ownership_registry.rb +117 -31
  159. data/lib/phronomy/agent/lifecycle/scope.rb +198 -0
  160. data/lib/phronomy/agent/lifecycle/scope_cleanup_error.rb +19 -0
  161. data/lib/phronomy/agent/lifecycle/unload_transition.rb +78 -0
  162. data/lib/phronomy/agent/persistence/agent_repository.rb +29 -21
  163. data/lib/phronomy/agent/persistence/cancellation_repository.rb +51 -0
  164. data/lib/phronomy/agent/persistence/codec.rb +1 -21
  165. data/lib/phronomy/agent/persistence/execution_repository.rb +57 -41
  166. data/lib/phronomy/agent/persistence/journal_repository.rb +27 -18
  167. data/lib/phronomy/agent/persistence/records.rb +33 -0
  168. data/lib/phronomy/agent/persistence/retention_repository.rb +70 -0
  169. data/lib/phronomy/agent/persistence/storage_schema.rb +7 -2
  170. data/lib/phronomy/agent/persistence/watermark.rb +13 -11
  171. data/lib/phronomy/agent/recovery/invocation_restorer.rb +13 -21
  172. data/lib/phronomy/agent/recovery/recovery_coordinator/continuation.rb +13 -12
  173. data/lib/phronomy/agent/recovery/recovery_coordinator/installation.rb +34 -34
  174. data/lib/phronomy/agent/recovery/recovery_coordinator/resolution.rb +33 -39
  175. data/lib/phronomy/agent/recovery/recovery_coordinator.rb +28 -25
  176. data/lib/phronomy/agent/recovery/recovery_rules.rb +119 -0
  177. data/lib/phronomy/agent/recovery/recovery_support.rb +10 -19
  178. data/lib/phronomy/agent/reserved_execution.rb +16 -0
  179. data/lib/phronomy/agent/retention.rb +17 -0
  180. data/lib/phronomy/agent/runtime_binding/engine_environment.rb +60 -0
  181. data/lib/phronomy/agent/runtime_binding/engine_session_builder.rb +34 -0
  182. data/lib/phronomy/agent/runtime_binding/phase_machine_builder.rb +64 -0
  183. data/lib/phronomy/agent/runtime_binding/tool_session_builder.rb +34 -0
  184. data/lib/phronomy/agent/{context_assembly/ruby_llm_materializer.rb → runtime_input.rb} +18 -35
  185. data/lib/phronomy/agent/{context_assembly/saved_context_reader.rb → saved_context_reader.rb} +6 -4
  186. data/lib/phronomy/agent/settings.rb +47 -0
  187. data/lib/phronomy/agent/{context_assembly/state_writer.rb → state_writer.rb} +4 -3
  188. data/lib/phronomy/agent/store.rb +253 -0
  189. data/lib/phronomy/agent/{lifecycle_contract/stream_callback_error.rb → stream_callback_error.rb} +3 -3
  190. data/lib/phronomy/agent/tool_execution/tool_binding.rb +4 -64
  191. data/lib/phronomy/agent/tool_execution/tool_invocation.rb +50 -219
  192. data/lib/phronomy/agent/tool_execution/tool_invocation_actions.rb +107 -0
  193. data/lib/phronomy/agent/tool_execution/tool_invocation_transitions.rb +70 -0
  194. data/lib/phronomy/common/error.rb +65 -1
  195. data/lib/phronomy/configuration/runtime_settings.rb +49 -0
  196. data/lib/phronomy/content_store/base.rb +7 -2
  197. data/lib/phronomy/content_store/storage_schema.rb +1 -0
  198. data/lib/phronomy/content_store/stored_contents.rb +2 -1
  199. data/lib/phronomy/context/assembly.rb +195 -0
  200. data/lib/phronomy/{llm_contract/context_budget_exceeded_error.rb → context/budget_exceeded_error.rb} +3 -1
  201. data/lib/phronomy/context/candidate.rb +51 -0
  202. data/lib/phronomy/context/constraint.rb +47 -0
  203. data/lib/phronomy/{agent/context_contract → context}/context_plan.rb +1 -1
  204. data/lib/phronomy/{agent/context_contract → context}/context_plan_validator.rb +3 -3
  205. data/lib/phronomy/{agent/context_contract → context}/context_policy.rb +1 -1
  206. data/lib/phronomy/{agent/context_contract → context}/context_policy_input.rb +1 -1
  207. data/lib/phronomy/{agent/context_assembly → context}/context_policy_input_builder.rb +1 -1
  208. data/lib/phronomy/context/default_policy.rb +140 -0
  209. data/lib/phronomy/context/final_budget_validator.rb +33 -0
  210. data/lib/phronomy/{agent/context_contract → context}/llm_input_build_context.rb +1 -1
  211. data/lib/phronomy/{agent/context_contract → context}/llm_input_manifest.rb +19 -19
  212. data/lib/phronomy/{agent/context_contract → context}/llm_input_patch.rb +1 -1
  213. data/lib/phronomy/context/manifest_error.rb +10 -0
  214. data/lib/phronomy/context/prompt_template.rb +98 -0
  215. data/lib/phronomy/{vector_store → documents}/loader/base.rb +1 -1
  216. data/lib/phronomy/{vector_store → documents}/loader/csv_loader.rb +2 -2
  217. data/lib/phronomy/{vector_store → documents}/loader/markdown_loader.rb +3 -3
  218. data/lib/phronomy/{vector_store → documents}/loader/plain_text_loader.rb +2 -2
  219. data/lib/phronomy/{vector_store → documents}/splitter/base.rb +1 -1
  220. data/lib/phronomy/{vector_store → documents}/splitter/fixed_size_splitter.rb +2 -2
  221. data/lib/phronomy/{vector_store → documents}/splitter/recursive_splitter.rb +2 -2
  222. data/lib/phronomy/embeddings/async/async_client.rb +44 -0
  223. data/lib/phronomy/embeddings/authentication_error.rb +11 -0
  224. data/lib/phronomy/embeddings/backends/ruby_llm_embeddings.rb +52 -0
  225. data/lib/phronomy/embeddings/base.rb +43 -0
  226. data/lib/phronomy/embeddings/invalid_result_error.rb +11 -0
  227. data/lib/phronomy/embeddings/rate_limit_error.rb +11 -0
  228. data/lib/phronomy/embeddings/transport_error.rb +11 -0
  229. data/lib/phronomy/engine/concurrency/offload_pool.rb +36 -21
  230. data/lib/phronomy/engine/concurrency/pool_registry.rb +47 -12
  231. data/lib/phronomy/engine/diagnostics.rb +1 -1
  232. data/lib/phronomy/engine/event_loop.rb +7 -7
  233. data/lib/phronomy/engine/execution_binding.rb +23 -0
  234. data/lib/phronomy/engine/execution_receiver_binding.rb +43 -0
  235. data/lib/phronomy/engine/fsm_protocol.rb +1 -6
  236. data/lib/phronomy/engine/fsm_session.rb +3 -3
  237. data/lib/phronomy/engine/metrics.rb +16 -14
  238. data/lib/phronomy/engine/runtime/timer_queue.rb +1 -1
  239. data/lib/phronomy/engine/runtime.rb +25 -12
  240. data/lib/phronomy/engine/terminal_decision.rb +7 -0
  241. data/lib/phronomy/execution/async_operation.rb +41 -0
  242. data/lib/phronomy/{engine → execution}/blocking.rb +2 -2
  243. data/lib/phronomy/{engine → execution}/concurrency/cancellation_token.rb +1 -1
  244. data/lib/phronomy/{engine → execution}/concurrency/deadline.rb +0 -17
  245. data/lib/phronomy/{engine → execution}/concurrency/physical_completion_task.rb +1 -1
  246. data/lib/phronomy/{engine → execution}/concurrency/result_collector.rb +1 -1
  247. data/lib/phronomy/{engine → execution}/concurrency/result_composition.rb +33 -12
  248. data/lib/phronomy/{engine/concurrency/subscriptions.rb → execution/concurrency/result_subscriptions.rb} +14 -18
  249. data/lib/phronomy/execution/concurrency/subscriptions.rb +25 -0
  250. data/lib/phronomy/{engine → execution}/execution.rb +61 -27
  251. data/lib/phronomy/{engine → execution}/execution_cancellation_error.rb +2 -2
  252. data/lib/phronomy/execution/execution_receiver.rb +78 -0
  253. data/lib/phronomy/execution/execution_scope_state.rb +35 -0
  254. data/lib/phronomy/{engine → execution}/execution_timeout_error.rb +2 -2
  255. data/lib/phronomy/{engine → execution}/invocation_context.rb +0 -15
  256. data/lib/phronomy/execution/invocation_controls.rb +73 -0
  257. data/lib/phronomy/execution/runnable.rb +24 -0
  258. data/lib/phronomy/{engine → execution}/task_result.rb +9 -4
  259. data/lib/phronomy/execution/wait_policy.rb +24 -0
  260. data/lib/phronomy/filter/{contract/filter_block_error.rb → filter_block_error.rb} +3 -3
  261. data/lib/phronomy/generation/generator_verifier/agent_operation.rb +65 -0
  262. data/lib/phronomy/generation/generator_verifier/default_parser.rb +27 -0
  263. data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +2 -2
  264. data/lib/phronomy/generation/generator_verifier.rb +7 -7
  265. data/lib/phronomy/generation/low_confidence_error.rb +2 -2
  266. data/lib/phronomy/integrations/ordered_event_delivery.rb +208 -0
  267. data/lib/phronomy/llm_adapter/async/async_client.rb +47 -0
  268. data/lib/phronomy/{llm_contract → llm_adapter}/authentication_error.rb +3 -1
  269. data/lib/phronomy/llm_adapter/backends/ruby_llm.rb +183 -0
  270. data/lib/phronomy/llm_adapter/base.rb +49 -68
  271. data/lib/phronomy/{llm_contract → llm_adapter}/context_length_error.rb +3 -1
  272. data/lib/phronomy/llm_adapter/invalid_result_error.rb +10 -0
  273. data/lib/phronomy/llm_adapter/message.rb +62 -0
  274. data/lib/phronomy/{llm_contract → llm_adapter}/rate_limit_error.rb +3 -1
  275. data/lib/phronomy/llm_adapter/request.rb +79 -0
  276. data/lib/phronomy/llm_adapter/response.rb +36 -0
  277. data/lib/phronomy/llm_adapter/stream_chunk.rb +18 -0
  278. data/lib/phronomy/llm_adapter/token_usage.rb +64 -0
  279. data/lib/phronomy/llm_adapter/transport_error.rb +9 -0
  280. data/lib/phronomy/llm_adapter.rb +3 -16
  281. data/lib/phronomy/multi_agent/admission_registry.rb +1 -8
  282. data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +108 -84
  283. data/lib/phronomy/multi_agent/execution_environment.rb +29 -0
  284. data/lib/phronomy/{agent/handoff → multi_agent}/handoff.rb +2 -2
  285. data/lib/phronomy/{agent/handoff → multi_agent}/handoff_capability_factory.rb +21 -2
  286. data/lib/phronomy/multi_agent/handoff_participant.rb +79 -0
  287. data/lib/phronomy/{agent/handoff → multi_agent}/handoff_policy.rb +1 -1
  288. data/lib/phronomy/multi_agent/handoff_runner.rb +77 -100
  289. data/lib/phronomy/{agent/handoff → multi_agent}/handoff_state.rb +1 -1
  290. data/lib/phronomy/multi_agent/orchestrator.rb +61 -75
  291. data/lib/phronomy/multi_agent/persistence/codec.rb +20 -0
  292. data/lib/phronomy/multi_agent/persistence/handoff_state_repository.rb +77 -0
  293. data/lib/phronomy/multi_agent/persistence/records.rb +27 -0
  294. data/lib/phronomy/multi_agent/persistence/storage_schema.rb +21 -0
  295. data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +60 -44
  296. data/lib/phronomy/multi_agent/persistence/team_repository.rb +29 -21
  297. data/lib/phronomy/multi_agent/reserved_child_admission.rb +88 -0
  298. data/lib/phronomy/multi_agent/runtime_binding/engine_environment.rb +35 -0
  299. data/lib/phronomy/multi_agent/shared_state.rb +2 -2
  300. data/lib/phronomy/multi_agent/store.rb +156 -0
  301. data/lib/phronomy/multi_agent/team_busy_error.rb +18 -0
  302. data/lib/phronomy/multi_agent/team_coordinator.rb +99 -77
  303. data/lib/phronomy/multi_agent/team_not_found_error.rb +18 -0
  304. data/lib/phronomy/multi_agent/team_ownership_registry.rb +2 -12
  305. data/lib/phronomy/output_parser/parse_error.rb +7 -0
  306. data/lib/phronomy/persistence/content_repository.rb +26 -0
  307. data/lib/phronomy/persistence/errors.rb +16 -0
  308. data/lib/phronomy/persistence/migration/initial_format_migration.rb +2 -2
  309. data/lib/phronomy/persistence/persistence.rb +23 -0
  310. data/lib/phronomy/persistence/save_outcome.rb +24 -0
  311. data/lib/phronomy/persistence/snapshot_comparison.rb +81 -0
  312. data/lib/phronomy/persistence/storage_boundary.rb +32 -0
  313. data/lib/phronomy/persistence/transaction.rb +86 -0
  314. data/lib/phronomy/persistence_composition/storage_schema.rb +9 -4
  315. data/lib/phronomy/persistence_composition/stores.rb +34 -0
  316. data/lib/phronomy/persistence_composition/unit4_migration.rb +276 -0
  317. data/lib/phronomy/runtime_composition/agent_defaults.rb +5 -1
  318. data/lib/phronomy/runtime_composition/configuration.rb +146 -0
  319. data/lib/phronomy/runtime_composition/evaluation_defaults.rb +7 -0
  320. data/lib/phronomy/runtime_composition/execution_defaults.rb +5 -0
  321. data/lib/phronomy/runtime_composition/generation_defaults.rb +6 -0
  322. data/lib/phronomy/runtime_composition/global_configuration.rb +38 -0
  323. data/lib/phronomy/runtime_composition/multi_agent_defaults.rb +9 -0
  324. data/lib/phronomy/runtime_composition/workflow_defaults.rb +4 -0
  325. data/lib/phronomy/storage/async/async_client.rb +60 -0
  326. data/lib/phronomy/storage/backend.rb +8 -2
  327. data/lib/phronomy/storage/condition_failed_error.rb +2 -2
  328. data/lib/phronomy/storage/unique_constraint_error.rb +2 -2
  329. data/lib/phronomy/testing/eval/metrics.rb +1 -1
  330. data/lib/phronomy/testing/eval/scorer/llm_judge.rb +19 -3
  331. data/lib/phronomy/testing/persistence_contract/a_content_store.rb +2 -2
  332. data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +6 -6
  333. data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +55 -52
  334. data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +3 -3
  335. data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +7 -7
  336. data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +11 -11
  337. data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +57 -51
  338. data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +1 -1
  339. data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +23 -22
  340. data/lib/phronomy/tool/authorization.rb +77 -0
  341. data/lib/phronomy/tool/base.rb +551 -9
  342. data/lib/phronomy/tool/call_request.rb +40 -0
  343. data/lib/phronomy/{agent/tool_execution/tool_definition_set.rb → tool/definition_set.rb} +7 -8
  344. data/lib/phronomy/tool/operation.rb +88 -0
  345. data/lib/phronomy/tool/schema.rb +214 -0
  346. data/lib/phronomy/tool/settings.rb +31 -0
  347. data/lib/phronomy/tool/tool_error.rb +7 -0
  348. data/lib/phronomy/tool/tool_executor.rb +58 -0
  349. data/lib/phronomy/tools/agent.rb +22 -90
  350. data/lib/phronomy/tools/mcp.rb +21 -14
  351. data/lib/phronomy/tools/vector_search.rb +4 -4
  352. data/lib/phronomy/tracing/automatic.rb +11 -6
  353. data/lib/phronomy/tracing/base.rb +36 -7
  354. data/lib/phronomy/tracing/observation.rb +37 -0
  355. data/lib/phronomy/tracing/open_telemetry_tracer.rb +4 -29
  356. data/lib/phronomy/tracing/settings.rb +36 -0
  357. data/lib/phronomy/vector_store/{async_backend.rb → async/async_client.rb} +35 -29
  358. data/lib/phronomy/vector_store/{in_memory.rb → backends/in_memory.rb} +12 -10
  359. data/lib/phronomy/vector_store/{pgvector.rb → backends/pgvector.rb} +44 -16
  360. data/lib/phronomy/vector_store/{redis_search.rb → backends/redis_search.rb} +74 -37
  361. data/lib/phronomy/vector_store/base.rb +118 -49
  362. data/lib/phronomy/vector_store/invalid_result_error.rb +12 -0
  363. data/lib/phronomy/vector_store.rb +2 -17
  364. data/lib/phronomy/version.rb +1 -1
  365. data/lib/phronomy/workflow/completion.rb +11 -0
  366. data/lib/phronomy/workflow/execution/workflow.rb +37 -9
  367. data/lib/phronomy/workflow/execution/workflow_action_rules.rb +67 -0
  368. data/lib/phronomy/workflow/execution/workflow_context.rb +2 -2
  369. data/lib/phronomy/workflow/execution/workflow_execution_environment.rb +28 -0
  370. data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +77 -8
  371. data/lib/phronomy/workflow/execution/workflow_runner.rb +105 -92
  372. data/lib/phronomy/workflow/execution/workflow_settings.rb +28 -0
  373. data/lib/phronomy/workflow/execution/workflow_task_execution.rb +153 -0
  374. data/lib/phronomy/workflow/execution/workflow_task_reception.rb +72 -0
  375. data/lib/phronomy/workflow/execution/workflow_task_resume_error.rb +14 -0
  376. data/lib/phronomy/workflow/persistence/state_repository.rb +28 -22
  377. data/lib/phronomy/workflow/persistence/storage_schema.rb +13 -0
  378. data/lib/phronomy/workflow/runtime_binding/workflow_engine_environment.rb +61 -0
  379. data/lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb +158 -0
  380. data/lib/phronomy/workflow/runtime_binding/workflow_task_session.rb +76 -0
  381. data/lib/phronomy/workflow/{execution → runtime_binding}/workflow_terminal_policy.rb +4 -4
  382. data/lib/phronomy.rb +63 -16
  383. data/scripts/api_snapshot.rb +47 -15
  384. data/scripts/check_readme_ruby.rb +24 -29
  385. data/scripts/check_readme_runnable.rb +92 -81
  386. data/sig/_private/phronomy/agent/settings.rbs +19 -0
  387. data/sig/_private/phronomy/configuration/runtime_settings.rbs +20 -0
  388. data/sig/_private/phronomy/llm_adapter/async/async_client.rbs +10 -0
  389. data/sig/_private/phronomy/multi_agent/store.rbs +8 -0
  390. data/sig/_private/phronomy/persistence/storage_boundary.rbs +7 -0
  391. data/sig/_private/phronomy/tool/settings.rbs +13 -0
  392. data/sig/_private/phronomy/tracing/settings.rbs +15 -0
  393. data/sig/_private/phronomy/workflow/execution/workflow_settings.rbs +13 -0
  394. data/sig/phronomy/agent/admission.rbs +19 -0
  395. data/sig/phronomy/agent/agent_not_found_error.rbs +6 -0
  396. data/sig/phronomy/agent/agent_unloaded_error.rbs +6 -0
  397. data/sig/phronomy/agent/execution_contract.rbs +132 -0
  398. data/sig/phronomy/agent/execution_environment.rbs +20 -0
  399. data/sig/phronomy/agent/store.rbs +73 -0
  400. data/sig/phronomy/agent/stream_callback_error.rbs +8 -0
  401. data/sig/phronomy/agent.rbs +29 -135
  402. data/sig/phronomy/common.rbs +7 -0
  403. data/sig/phronomy/content_store/contracts.rbs +13 -0
  404. data/sig/phronomy/context.rbs +152 -0
  405. data/sig/phronomy/embeddings/async/async_client.rbs +9 -0
  406. data/sig/phronomy/embeddings/base.rbs +23 -0
  407. data/sig/phronomy/engine/concurrency/timer_bindings.rbs +22 -0
  408. data/sig/phronomy/engine/control_bindings.rbs +11 -0
  409. data/sig/phronomy/engine/control_protocols.rbs +23 -0
  410. data/sig/phronomy/engine/execution_binding.rbs +6 -0
  411. data/sig/phronomy/engine/execution_receiver.rbs +15 -0
  412. data/sig/phronomy/engine/observability.rbs +13 -0
  413. data/sig/phronomy/execution/async_operation.rbs +7 -0
  414. data/sig/phronomy/execution/blocking.rbs +5 -0
  415. data/sig/phronomy/execution/concurrency/cancellation_token.rbs +13 -0
  416. data/sig/phronomy/execution/concurrency/deadline.rbs +10 -0
  417. data/sig/phronomy/execution/concurrency/result_composition.rbs +7 -0
  418. data/sig/phronomy/execution/control_protocols.rbs +18 -0
  419. data/sig/phronomy/execution/errors.rbs +7 -0
  420. data/sig/phronomy/execution/execution.rbs +41 -0
  421. data/sig/phronomy/execution/execution_receiver.rbs +40 -0
  422. data/sig/phronomy/execution/execution_scope_state.rbs +9 -0
  423. data/sig/phronomy/execution/invocation_context.rbs +18 -0
  424. data/sig/phronomy/execution/invocation_controls.rbs +13 -0
  425. data/sig/phronomy/execution/task_result.rbs +29 -0
  426. data/sig/phronomy/extensions.rbs +1 -21
  427. data/sig/phronomy/filter/filter_block_error.rbs +6 -0
  428. data/sig/phronomy/generation/low_confidence_error.rbs +6 -0
  429. data/sig/phronomy/handoff.rbs +10 -4
  430. data/sig/phronomy/integrations.rbs +14 -0
  431. data/sig/phronomy/llm_adapter/backends/ruby_llm.rbs +6 -0
  432. data/sig/phronomy/llm_adapter/base.rbs +14 -0
  433. data/sig/phronomy/llm_adapter/usage.rbs +14 -0
  434. data/sig/phronomy/llm_adapter/values.rbs +43 -0
  435. data/sig/phronomy/llm_context_window.rbs +10 -0
  436. data/sig/phronomy/multi_agent/reserved_child_admission.rbs +8 -0
  437. data/sig/phronomy/multi_agent/store.rbs +54 -0
  438. data/sig/phronomy/multi_agent/team_busy_error.rbs +8 -0
  439. data/sig/phronomy/multi_agent/team_not_found_error.rbs +8 -0
  440. data/sig/phronomy/multi_agent.rbs +6 -3
  441. data/sig/phronomy/persistence/errors.rbs +18 -0
  442. data/sig/phronomy/persistence/migration.rbs +13 -0
  443. data/sig/phronomy/persistence/transaction.rbs +19 -0
  444. data/sig/phronomy/persistence.rbs +1 -99
  445. data/sig/phronomy/persistence_composition.rbs +19 -0
  446. data/sig/phronomy/runtime_composition/configuration.rbs +36 -0
  447. data/sig/phronomy/storage/async/async_client.rbs +8 -0
  448. data/sig/phronomy/storage/backends/in_memory.rbs +9 -0
  449. data/sig/phronomy/{storage.rbs → storage/contracts.rbs} +4 -18
  450. data/sig/phronomy/tool.rbs +48 -4
  451. data/sig/phronomy/tracing/observation.rbs +7 -0
  452. data/sig/phronomy/vector_store/async/async_client.rbs +13 -0
  453. data/sig/phronomy/vector_store/base.rbs +23 -0
  454. data/sig/phronomy/workflow/execution/workflow_execution_environment.rbs +15 -0
  455. data/sig/phronomy/workflow/runtime_binding/workflow_terminal_policy.rbs +7 -0
  456. data/sig/phronomy/workflow/store.rbs +8 -0
  457. data/sig/phronomy/workflow.rbs +5 -1
  458. data/sig/phronomy.rbs +4 -49
  459. metadata +365 -117
  460. data/lib/phronomy/agent/concerns/before_llm_input.rb +0 -66
  461. data/lib/phronomy/agent/concerns/error_translation.rb +0 -45
  462. data/lib/phronomy/agent/concerns/filterable.rb +0 -184
  463. data/lib/phronomy/agent/context/capability/base.rb +0 -548
  464. data/lib/phronomy/agent/context/capability/tool_executor.rb +0 -62
  465. data/lib/phronomy/agent/context/instruction/prompt_template.rb +0 -102
  466. data/lib/phronomy/agent/context_assembly/runtime_chat_builder.rb +0 -36
  467. data/lib/phronomy/agent/context_assembly/token_budget_resolver.rb +0 -22
  468. data/lib/phronomy/agent/context_parts/validators/final_budget_validator.rb +0 -37
  469. data/lib/phronomy/agent/context_policies/default.rb +0 -142
  470. data/lib/phronomy/agent/execution/phase_machine_builder.rb +0 -101
  471. data/lib/phronomy/agent/execution/provider_call_outcome.rb +0 -106
  472. data/lib/phronomy/agent/handoff/handoff_execution_coordinator.rb +0 -15
  473. data/lib/phronomy/agent/handoff/handoff_outcome_committer.rb +0 -131
  474. data/lib/phronomy/agent/handoff/handoff_request.rb +0 -45
  475. data/lib/phronomy/agent/lifecycle_contract/handoff_error.rb +0 -7
  476. data/lib/phronomy/agent/persistence/handoff_state_repository.rb +0 -58
  477. data/lib/phronomy/agent/persistence/queries.rb +0 -61
  478. data/lib/phronomy/agent/selection/candidate.rb +0 -53
  479. data/lib/phronomy/agent/selection/constraint.rb +0 -49
  480. data/lib/phronomy/agent/tool_execution/tool_call_intercepted.rb +0 -29
  481. data/lib/phronomy/agent/tool_execution/tool_invocation_session_builder.rb +0 -276
  482. data/lib/phronomy/configuration/configuration.rb +0 -68
  483. data/lib/phronomy/configuration/global_configuration.rb +0 -26
  484. data/lib/phronomy/engine/concurrency/cancellation_scope.rb +0 -124
  485. data/lib/phronomy/engine/concurrency/operation_binding.rb +0 -43
  486. data/lib/phronomy/engine/execution_receiver.rb +0 -65
  487. data/lib/phronomy/engine/runnable.rb +0 -54
  488. data/lib/phronomy/llm_adapter/ruby_llm.rb +0 -47
  489. data/lib/phronomy/llm_contract/token_usage.rb +0 -85
  490. data/lib/phronomy/multi_agent/persistence/queries.rb +0 -30
  491. data/lib/phronomy/multi_agent/storage_contract/team_storage_schema.rb +0 -15
  492. data/lib/phronomy/output_parser/contract/parse_error.rb +0 -7
  493. data/lib/phronomy/persistence/api/persistence.rb +0 -140
  494. data/lib/phronomy/persistence_composition/repositories.rb +0 -77
  495. data/lib/phronomy/recovery/recovery.rb +0 -186
  496. data/lib/phronomy/tool/contract/tool_error.rb +0 -7
  497. data/lib/phronomy/vector_store/embeddings/base.rb +0 -44
  498. data/lib/phronomy/vector_store/embeddings/ruby_llm_embeddings.rb +0 -47
  499. data/lib/phronomy/workflow/phase_machine_builder.rb +0 -205
  500. data/lib/phronomy/workflow/storage_contract/workflow_storage_schema.rb +0 -9
  501. data/sig/phronomy/execution_receiver.rbs +0 -34
  502. data/sig/phronomy/llm_adapter.rbs +0 -11
  503. data/sig/phronomy/runtime.rbs +0 -76
  504. /data/lib/phronomy/agent/{context_assembly/context_importer.rb → context_importer.rb} +0 -0
  505. /data/lib/phronomy/{engine → execution}/backpressure_error.rb +0 -0
  506. /data/lib/phronomy/{engine → execution}/cancellation_error.rb +0 -0
  507. /data/lib/phronomy/{engine → execution}/concurrency/worker_input_restricted.rb +0 -0
  508. /data/lib/phronomy/{engine → execution}/event_loop_reentrancy_error.rb +0 -0
  509. /data/lib/phronomy/{engine → execution}/invalid_async_entry_action_error.rb +0 -0
  510. /data/lib/phronomy/{engine → execution}/invalid_async_transition_action_error.rb +0 -0
  511. /data/lib/phronomy/{engine → execution}/invalid_async_workflow_action_error.rb +0 -0
  512. /data/lib/phronomy/{engine → execution}/pool_shutdown_error.rb +0 -0
  513. /data/lib/phronomy/{engine → execution}/recursion_limit_error.rb +0 -0
  514. /data/lib/phronomy/{engine → execution}/runtime_shutdown_error.rb +0 -0
  515. /data/lib/phronomy/{engine → execution}/runtime_shutdown_reentrancy_error.rb +0 -0
  516. /data/lib/phronomy/{engine → execution}/scheduler_reentrancy_error.rb +0 -0
  517. /data/lib/phronomy/{engine → execution}/timeout_error.rb +0 -0
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Global configuration access, replacement, and scoped overrides.
4
+ # Explicitly loaded by the application entry point; not a common definition.
5
+ module Phronomy
6
+ class << self
7
+ def configuration
8
+ @configuration ||= Configuration.new
9
+ end
10
+
11
+ def configure
12
+ yield configuration
13
+ end
14
+
15
+ def reset_configuration!
16
+ @configuration = Configuration.new
17
+ end
18
+
19
+ def with_configuration
20
+ original = @configuration&.dup
21
+ yield configuration
22
+ ensure
23
+ @configuration = original
24
+ end
25
+ end
26
+ end
27
+
28
+ # The provider yields only the Engine-facing value object, never Configuration.
29
+ # It is intentionally lazy and follows reset_configuration!/with_configuration.
30
+ Phronomy::RuntimeSettings.install_provider { Phronomy.configuration.__runtime_settings }
31
+
32
+ # Domain accessors are lazy too. Each returns only the owning domain's values
33
+ # or operation, never the application Configuration or a captured snapshot.
34
+ Phronomy::Agent::Settings.install_provider { Phronomy.configuration.__agent_settings }
35
+ Phronomy::WorkflowSettings.install_provider { Phronomy.configuration.__workflow_settings }
36
+ Phronomy::Tool::Settings.install_provider { Phronomy.configuration.__tool_settings }
37
+ Phronomy::Tracing::Settings.install_provider { Phronomy.configuration.__tracing_settings }
38
+ Phronomy::MultiAgent::Store.install_configured_provider { Phronomy.configuration.multi_agent_store }
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Select stores and execution connections at the application composition
4
+ # boundary, without constructing either during library loading.
5
+ Phronomy::MultiAgent::TeamCoordinator.install_persistence_factory(
6
+ -> { Phronomy::PersistenceComposition.in_memory.multi_agent }
7
+ )
8
+
9
+ Phronomy::MultiAgent::ExecutionEnvironment.install_provider(-> { Phronomy::MultiAgent::EngineEnvironment.new })
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Install a lazy provider without creating a Runtime or execution registry.
4
+ Phronomy::WorkflowExecutionEnvironment.install_provider(-> { Phronomy::WorkflowEngineEnvironment.new })
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Storage
5
+ # Asynchronous execution of a synchronous raw storage backend.
6
+ #
7
+ # A transaction opens, runs and closes on one worker. Return values rather
8
+ # than transaction views: a bound view is closed when the block finishes.
9
+ # Timeout includes queue wait. Timeout/cancellation settles TaskResult but
10
+ # does not interrupt running I/O or guarantee rollback of a started write.
11
+ # Construction starts no Runtime; the default pool is resolved per operation.
12
+ # An explicitly injected pool remains owned by its caller.
13
+ #
14
+ # @api public
15
+ class AsyncClient
16
+ # @param backend [Storage::Backend] synchronous raw SPI 2 backend
17
+ # @param pool [Concurrency::OffloadPool, nil] optional execution pool
18
+ # @api public
19
+ def initialize(backend:, pool: nil)
20
+ Backend.validate_capabilities!(backend)
21
+ @backend = backend
22
+ @pool = pool
23
+ end
24
+
25
+ # Executes the entire transaction on one worker without waiting for queue
26
+ # capacity. Explicit nested backend transactions retain savepoint semantics.
27
+ #
28
+ # @param cancellation_token [Concurrency::CancellationToken, nil]
29
+ # @param timeout [Numeric, nil] operation-wide submit timeout
30
+ # @yieldparam view [Storage::View] view valid only within this transaction
31
+ # @return [Phronomy::TaskResult] the pool's original completion handle
32
+ # @api public
33
+ def transaction_async(cancellation_token: nil, timeout: nil, &operation)
34
+ raise ArgumentError, "transaction_async requires a block" unless operation
35
+
36
+ Phronomy::Execution.submit(pool: default_pool, timeout: timeout, cancellation_token: cancellation_token, on_full: :raise) do
37
+ @backend.transaction(&operation)
38
+ end
39
+ end
40
+
41
+ # Submit an existing domain-owned synchronous storage unit unchanged.
42
+ # It may already open transactions or perform reads/reconciliation. Do not
43
+ # add a transaction, timeout or cancellation condition around that unit.
44
+ # The owner supplies its current runtime's pool and applies the result.
45
+ #
46
+ # @api private
47
+ def self.submit(pool:, &operation)
48
+ raise ArgumentError, "storage submit requires a block" unless operation
49
+
50
+ Phronomy::Execution.submit(pool: pool, on_full: :raise, &operation)
51
+ end
52
+
53
+ private
54
+
55
+ def default_pool
56
+ @pool
57
+ end
58
+ end
59
+ end
60
+ end
@@ -30,8 +30,9 @@ module Phronomy
30
30
  def self.validate_capabilities!(backend)
31
31
  capabilities = backend.respond_to?(:capabilities) ? backend.capabilities : {}
32
32
  missing = REQUIRED_CAPABILITIES.reject { |key, value| capabilities.is_a?(Hash) && capabilities[key] == value }
33
- unless missing.empty? && backend.respond_to?(:view) && backend.respond_to?(:transaction)
34
- raise UnsupportedBackendError, "Persistence requires Storage SPI 2: #{missing.keys.join(", ")}"
33
+ missing_methods = %i[view transaction transaction_open?].reject { |name| backend.respond_to?(name) }
34
+ unless missing.empty? && missing_methods.empty?
35
+ raise UnsupportedBackendError, "Persistence requires Storage SPI 2: #{(missing.keys + missing_methods).join(", ")}"
35
36
  end
36
37
  end
37
38
 
@@ -40,6 +41,11 @@ module Phronomy
40
41
  REQUIRED_CAPABILITIES.transform_values { |value| (value == true) ? false : value }.freeze
41
42
  end
42
43
 
44
+ # Whether this synchronous context is already inside a backend transaction.
45
+ # Lets higher-level scopes reject claiming ownership of somebody else's commit.
46
+ # @api public
47
+ def transaction_open? = !current_view.nil?
48
+
43
49
  # All declared resources participate in one atomic transaction domain.
44
50
  # Record and stream revisions provide compare-and-swap conflict detection.
45
51
  # This does not
@@ -7,9 +7,9 @@ module Phronomy
7
7
  class ConditionFailedError < ConflictError
8
8
  attr_reader :condition
9
9
  # @api public
10
- def initialize(condition)
10
+ def initialize(condition, code: nil)
11
11
  @condition = condition
12
- super("storage condition failed: #{condition.class.name}")
12
+ super("storage condition failed: #{condition.class.name}", code: code)
13
13
  end
14
14
  end
15
15
  end
@@ -7,10 +7,10 @@ module Phronomy
7
7
  class UniqueConstraintError < ConflictError
8
8
  attr_reader :resource, :constraint
9
9
  # @api public
10
- def initialize(resource:, constraint:)
10
+ def initialize(resource:, constraint:, code: nil)
11
11
  @resource = resource.id
12
12
  @constraint = constraint
13
- super("unique constraint #{resource.id}.#{constraint} failed")
13
+ super("unique constraint #{resource.id}.#{constraint} failed", code: code)
14
14
  end
15
15
  end
16
16
  end
@@ -19,7 +19,7 @@ module Phronomy
19
19
  end
20
20
 
21
21
  def total_usage
22
- @results.map(&:usage).compact.reduce(Phronomy::TokenUsage.zero, :+)
22
+ @results.map(&:usage).compact.reduce(Phronomy::LLMAdapter::TokenUsage.zero, :+)
23
23
  end
24
24
 
25
25
  def average_latency_ms
@@ -5,6 +5,19 @@ module Phronomy
5
5
  module Eval
6
6
  module Scorer
7
7
  class LlmJudge < Base
8
+ CLIENT_FACTORIES = {}
9
+ private_constant :CLIENT_FACTORIES
10
+
11
+ # One-time boot binding, not an application extension registry. The
12
+ # factory runs for each score, after its Request has been prepared.
13
+ # @api private
14
+ def self.install_client_factory(factory)
15
+ raise ArgumentError, "factory must respond to call" unless factory.respond_to?(:call)
16
+
17
+ CLIENT_FACTORIES.replace(client: factory).freeze
18
+ nil
19
+ end
20
+
8
21
  DEFAULT_PROMPT = <<~PROMPT
9
22
  You are an impartial judge evaluating the quality of an AI assistant response.
10
23
  Rate the response on a scale from 0.0 (completely wrong or unhelpful) to 1.0 (perfect).
@@ -32,9 +45,12 @@ module Phronomy
32
45
  expected: expected.to_s,
33
46
  actual: actual.to_s
34
47
  )
35
- response = Phronomy::Runtime.instance.offload.submit do
36
- RubyLLM.chat(model: @model, provider: @provider, assume_model_exists: @assume_model_exists).ask(prompt)
37
- end.wait_result
48
+ request = Phronomy::LLMAdapter::Request.new(
49
+ model_config: {"model" => @model, "provider" => @provider&.to_s,
50
+ "assume_model_exists" => @assume_model_exists}.compact,
51
+ message: prompt
52
+ )
53
+ response = CLIENT_FACTORIES.fetch(:client).call.complete_async(request).wait_result
38
54
  response.content.to_s.strip.scan(/-?\d+\.?\d*/).first.to_f.clamp(0.0, 1.0)
39
55
  rescue => error
40
56
  raise if @raise_on_error
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  RSpec.shared_examples "a persistence content store" do
4
- let(:content_store) { persistence.contents }
4
+ let(:content_store) { stores.agent.contents }
5
5
 
6
6
  it "returns the same content_id for the same bytes" do
7
7
  first = content_store.put("same".b, canonicalization_version: 1)
@@ -21,7 +21,7 @@ RSpec.shared_examples "a persistence content store" do
21
21
  it "raises NotFoundError for a missing content_id" do
22
22
  expect do
23
23
  content_store.fetch("sha256:#{"0" * 64}")
24
- end.to raise_error(Phronomy::Storage::NotFoundError)
24
+ end.to raise_error(Phronomy::Persistence::NotFoundError)
25
25
  end
26
26
 
27
27
  it "isolates durable bytes from mutation of fetched values" do
@@ -3,7 +3,7 @@
3
3
  require "securerandom"
4
4
 
5
5
  RSpec.shared_examples "a Journal repository" do
6
- let(:journal_repository) { persistence.journals }
6
+ let(:journal_repository) { stores.agent.journals }
7
7
  let(:journal_agent_root) do
8
8
  Phronomy::Agent::AgentRoot.create(
9
9
  agent_id: "journal-agent-#{SecureRandom.uuid}",
@@ -25,7 +25,7 @@ RSpec.shared_examples "a Journal repository" do
25
25
  end
26
26
 
27
27
  before do
28
- persistence.agents.create(journal_agent_root)
28
+ stores.agent.agents.create(journal_agent_root)
29
29
  end
30
30
 
31
31
  it "appends records at the expected position and assigns durable sequences" do
@@ -51,7 +51,7 @@ RSpec.shared_examples "a Journal repository" do
51
51
  expected_position: 1,
52
52
  records: [record]
53
53
  )
54
- end.to raise_error(Phronomy::Storage::ConflictError)
54
+ end.to raise_error(Phronomy::Persistence::ConflictError)
55
55
  end
56
56
 
57
57
  it "rejects a duplicate record_id already present in the Journal" do
@@ -76,7 +76,7 @@ RSpec.shared_examples "a Journal repository" do
76
76
  expected_position: 1,
77
77
  records: [duplicate]
78
78
  )
79
- end.to raise_error(Phronomy::Storage::ConflictError)
79
+ end.to raise_error(Phronomy::Persistence::ConflictError)
80
80
  end
81
81
 
82
82
  it "rejects duplicate record_ids within one append" do
@@ -94,7 +94,7 @@ RSpec.shared_examples "a Journal repository" do
94
94
  expected_position: 0,
95
95
  records: records
96
96
  )
97
- end.to raise_error(Phronomy::Storage::ConflictError)
97
+ end.to raise_error(Phronomy::Persistence::ConflictError)
98
98
  end
99
99
 
100
100
  it "rejects records belonging to another Agent" do
@@ -106,7 +106,7 @@ RSpec.shared_examples "a Journal repository" do
106
106
  expected_position: 0,
107
107
  records: [record]
108
108
  )
109
- end.to raise_error(Phronomy::Storage::SerializationError)
109
+ end.to raise_error(Phronomy::Persistence::SerializationError)
110
110
  end
111
111
 
112
112
  it "reads records in sequence order and supports after/limit" do
@@ -27,26 +27,25 @@ RSpec.shared_examples "a Persistence backend" do
27
27
 
28
28
  it "advertises every required capability" do
29
29
  Phronomy::Storage::Backend::REQUIRED_CAPABILITIES.each do |name, required_value|
30
- expect(persistence.backend.capabilities[name]).to eq(required_value)
30
+ expect(stores.coordinator.backend.capabilities[name]).to eq(required_value)
31
31
  end
32
32
  end
33
33
 
34
- it "yields a transaction view that exposes the complete durable SPI" do
35
- persistence.transaction do |tx|
36
- expect(tx).to respond_to(
37
- :contents,
38
- :agents,
39
- :journals,
40
- :executions,
41
- :workflow_states,
42
- :handoff_states, :teams, :team_executions,
43
- :assert_agent_watermark!
44
- )
34
+ it "binds each domain to its own record protocol in one common scope" do
35
+ stores.coordinator.atomic do |scope|
36
+ stores.agent.participate(scope) do |records|
37
+ expect(records).to respond_to(:contents, :agents, :journals, :executions, :retentions, :cancellations, :assert_agent_watermark!)
38
+ expect(records).not_to respond_to(:teams, :team_executions, :workflow_states)
39
+ end
40
+ stores.multi_agent.participate(scope) do |records|
41
+ expect(records).to respond_to(:contents, :teams, :team_executions, :handoff_states)
42
+ expect(records).not_to respond_to(:agents, :executions, :workflow_states)
43
+ end
45
44
  end
46
45
  end
47
46
 
48
47
  it "returns the transaction block result" do
49
- expect(persistence.transaction { |_tx| :contract_result }).to eq(:contract_result)
48
+ expect(stores.agent.transaction { |_tx| :contract_result }).to eq(:contract_result)
50
49
  end
51
50
 
52
51
  it "commits changes across durable repositories as one transaction" do
@@ -55,7 +54,7 @@ RSpec.shared_examples "a Persistence backend" do
55
54
  workflow_id = "workflow-#{SecureRandom.uuid}"
56
55
  content_id = nil
57
56
 
58
- persistence.transaction do |tx|
57
+ stores.agent.transaction do |tx, scope|
59
58
  content_id = tx.contents.put_text("committed")
60
59
  tx.agents.create(root)
61
60
  appended = tx.journals.append(
@@ -73,11 +72,13 @@ RSpec.shared_examples "a Persistence backend" do
73
72
  ]
74
73
  )
75
74
  tx.executions.create_active(execution)
76
- tx.workflow_states.save(
77
- workflow_id,
78
- expected_revision: nil,
79
- snapshot: {fields: {value: "committed"}, phase: "pause"}
80
- )
75
+ scope.participate(persistence: stores.coordinator, adapter: Phronomy::Workflow::Persistence::StateRepository) do |workflow|
76
+ workflow.save(
77
+ workflow_id,
78
+ expected_revision: nil,
79
+ snapshot: {fields: {value: "committed"}, phase: "pause"}
80
+ )
81
+ end
81
82
 
82
83
  updated_root = root.with(
83
84
  agent_revision: 1,
@@ -87,12 +88,12 @@ RSpec.shared_examples "a Persistence backend" do
87
88
  tx.agents.save(root.agent_id, expected_revision: 0, root: updated_root)
88
89
  end
89
90
 
90
- expect(persistence.contents.exist?(content_id)).to be(true)
91
- expect(persistence.agents.load(root.agent_id).agent_revision).to eq(1)
92
- expect(persistence.journals.head(root.agent_id)).to eq(1)
93
- expect(persistence.executions.load(execution.execution_id).execution_id)
91
+ expect(stores.agent.contents.exist?(content_id)).to be(true)
92
+ expect(stores.agent.agents.load(root.agent_id).agent_revision).to eq(1)
93
+ expect(stores.agent.journals.head(root.agent_id)).to eq(1)
94
+ expect(stores.agent.executions.load(execution.execution_id).execution_id)
94
95
  .to eq(execution.execution_id)
95
- expect(persistence.workflow_states.load(workflow_id)).not_to be_nil
96
+ expect(stores.workflow.load(workflow_id)).not_to be_nil
96
97
  end
97
98
 
98
99
  it "rolls back all durable repositories when the transaction block raises" do
@@ -102,7 +103,7 @@ RSpec.shared_examples "a Persistence backend" do
102
103
  content_id = nil
103
104
 
104
105
  expect do
105
- persistence.transaction do |tx|
106
+ stores.agent.transaction do |tx, scope|
106
107
  content_id = tx.contents.put_text("temporary-#{SecureRandom.uuid}")
107
108
  tx.agents.create(root)
108
109
  tx.journals.append(
@@ -120,32 +121,34 @@ RSpec.shared_examples "a Persistence backend" do
120
121
  ]
121
122
  )
122
123
  tx.executions.create_active(execution)
123
- tx.workflow_states.save(
124
- workflow_id,
125
- expected_revision: nil,
126
- snapshot: {fields: {value: "temporary"}, phase: "pause"}
127
- )
124
+ scope.participate(persistence: stores.coordinator, adapter: Phronomy::Workflow::Persistence::StateRepository) do |workflow|
125
+ workflow.save(
126
+ workflow_id,
127
+ expected_revision: nil,
128
+ snapshot: {fields: {value: "temporary"}, phase: "pause"}
129
+ )
130
+ end
128
131
  raise "rollback-contract"
129
132
  end
130
133
  end.to raise_error("rollback-contract")
131
134
 
132
- expect(persistence.contents.exist?(content_id)).to be(false)
135
+ expect(stores.agent.contents.exist?(content_id)).to be(false)
133
136
  expect do
134
- persistence.agents.load(root.agent_id)
135
- end.to raise_error(Phronomy::Storage::NotFoundError)
136
- expect(persistence.journals.head(root.agent_id)).to eq(0)
137
+ stores.agent.agents.load(root.agent_id)
138
+ end.to raise_error(Phronomy::Persistence::NotFoundError)
139
+ expect(stores.agent.journals.head(root.agent_id)).to eq(0)
137
140
  expect do
138
- persistence.executions.load(execution.execution_id)
139
- end.to raise_error(Phronomy::Storage::NotFoundError)
140
- expect(persistence.workflow_states.load(workflow_id)).to be_nil
141
+ stores.agent.executions.load(execution.execution_id)
142
+ end.to raise_error(Phronomy::Persistence::NotFoundError)
143
+ expect(stores.workflow.load(workflow_id)).to be_nil
141
144
  end
142
145
 
143
146
  it "accepts the current Agent revision and Journal position watermark" do
144
147
  root = backend_agent_root
145
- persistence.agents.create(root)
148
+ stores.agent.agents.create(root)
146
149
 
147
150
  expect(
148
- persistence.assert_agent_watermark!(
151
+ stores.agent.assert_agent_watermark!(
149
152
  agent_id: root.agent_id,
150
153
  agent_revision: root.agent_revision,
151
154
  journal_position: root.journal_position
@@ -155,23 +158,23 @@ RSpec.shared_examples "a Persistence backend" do
155
158
 
156
159
  it "raises ConflictError when the durable Agent revision has advanced" do
157
160
  root = backend_agent_root
158
- persistence.agents.create(root)
161
+ stores.agent.agents.create(root)
159
162
  advanced = root.with(agent_revision: 1)
160
- persistence.agents.save(root.agent_id, expected_revision: 0, root: advanced)
163
+ stores.agent.agents.save(root.agent_id, expected_revision: 0, root: advanced)
161
164
 
162
165
  expect do
163
- persistence.assert_agent_watermark!(
166
+ stores.agent.assert_agent_watermark!(
164
167
  agent_id: root.agent_id,
165
168
  agent_revision: 0,
166
169
  journal_position: 0
167
170
  )
168
- end.to raise_error(Phronomy::Storage::ConflictError)
171
+ end.to raise_error(Phronomy::Persistence::ConflictError)
169
172
  end
170
173
 
171
174
  it "raises ConflictError when the durable Journal position has advanced" do
172
175
  root = backend_agent_root
173
- persistence.agents.create(root)
174
- persistence.journals.append(
176
+ stores.agent.agents.create(root)
177
+ stores.agent.journals.append(
175
178
  root.agent_id,
176
179
  expected_position: 0,
177
180
  records: [
@@ -186,23 +189,23 @@ RSpec.shared_examples "a Persistence backend" do
186
189
  )
187
190
 
188
191
  expect do
189
- persistence.assert_agent_watermark!(
192
+ stores.agent.assert_agent_watermark!(
190
193
  agent_id: root.agent_id,
191
194
  agent_revision: 0,
192
195
  journal_position: 0
193
196
  )
194
- end.to raise_error(Phronomy::Storage::ConflictError)
197
+ end.to raise_error(Phronomy::Persistence::ConflictError)
195
198
  end
196
199
 
197
200
  it "rolls back earlier writes when a watermark precondition fails" do
198
201
  root = backend_agent_root
199
- persistence.agents.create(root)
202
+ stores.agent.agents.create(root)
200
203
  advanced = root.with(agent_revision: 1)
201
- persistence.agents.save(root.agent_id, expected_revision: 0, root: advanced)
204
+ stores.agent.agents.save(root.agent_id, expected_revision: 0, root: advanced)
202
205
  temporary_content_id = nil
203
206
 
204
207
  expect do
205
- persistence.transaction do |tx|
208
+ stores.agent.transaction do |tx, scope|
206
209
  temporary_content_id = tx.contents.put_text(
207
210
  "watermark-rollback-#{SecureRandom.uuid}"
208
211
  )
@@ -212,8 +215,8 @@ RSpec.shared_examples "a Persistence backend" do
212
215
  journal_position: 0
213
216
  )
214
217
  end
215
- end.to raise_error(Phronomy::Storage::ConflictError)
218
+ end.to raise_error(Phronomy::Persistence::ConflictError)
216
219
 
217
- expect(persistence.contents.exist?(temporary_content_id)).to be(false)
220
+ expect(stores.agent.contents.exist?(temporary_content_id)).to be(false)
218
221
  end
219
222
  end
@@ -3,7 +3,7 @@
3
3
  require "securerandom"
4
4
 
5
5
  RSpec.shared_examples "a workflow state repository" do
6
- let(:repository) { persistence.workflow_states }
6
+ let(:repository) { stores.workflow }
7
7
 
8
8
  def workflow_contract_value(hash, key)
9
9
  hash.key?(key) ? hash[key] : hash[key.to_s]
@@ -54,7 +54,7 @@ RSpec.shared_examples "a workflow state repository" do
54
54
  expected_revision: nil,
55
55
  snapshot: {fields: {value: 2}, phase: "__end__"}
56
56
  )
57
- end.to raise_error(Phronomy::Storage::ConflictError)
57
+ end.to raise_error(Phronomy::Persistence::ConflictError)
58
58
  end
59
59
 
60
60
  it "returns a snapshot representation accepted by WorkflowRunner" do
@@ -111,7 +111,7 @@ RSpec.shared_examples "a workflow state repository" do
111
111
 
112
112
  expect do
113
113
  repository.delete(workflow_instance_id, expected_revision: 99)
114
- end.to raise_error(Phronomy::Storage::ConflictError)
114
+ end.to raise_error(Phronomy::Persistence::ConflictError)
115
115
 
116
116
  repository.delete(workflow_instance_id, expected_revision: 1)
117
117
  expect(repository.load(workflow_instance_id)).to be_nil
@@ -3,7 +3,7 @@
3
3
  require "securerandom"
4
4
 
5
5
  RSpec.shared_examples "an Agent repository" do
6
- let(:agent_repository) { persistence.agents }
6
+ let(:agent_repository) { stores.agent.agents }
7
7
  let(:agent_root) do
8
8
  Phronomy::Agent::AgentRoot.create(
9
9
  agent_id: "contract-agent-#{SecureRandom.uuid}",
@@ -22,13 +22,13 @@ RSpec.shared_examples "an Agent repository" do
22
22
 
23
23
  expect do
24
24
  agent_repository.create(agent_root)
25
- end.to raise_error(Phronomy::Storage::ConflictError)
25
+ end.to raise_error(Phronomy::Persistence::ConflictError)
26
26
  end
27
27
 
28
28
  it "raises NotFoundError for a missing Agent" do
29
29
  expect do
30
30
  agent_repository.load("missing-#{SecureRandom.uuid}")
31
- end.to raise_error(Phronomy::Storage::NotFoundError)
31
+ end.to raise_error(Phronomy::Persistence::NotFoundError)
32
32
  end
33
33
 
34
34
  it "saves only at the expected revision" do
@@ -54,7 +54,7 @@ RSpec.shared_examples "an Agent repository" do
54
54
 
55
55
  expect do
56
56
  agent_repository.save(agent_root.agent_id, expected_revision: 0, root: stale)
57
- end.to raise_error(Phronomy::Storage::ConflictError)
57
+ end.to raise_error(Phronomy::Persistence::ConflictError)
58
58
  end
59
59
 
60
60
  it "rejects an Agent identity mismatch" do
@@ -71,7 +71,7 @@ RSpec.shared_examples "an Agent repository" do
71
71
  expected_revision: 0,
72
72
  root: other
73
73
  )
74
- end.to raise_error(Phronomy::Storage::SerializationError)
74
+ end.to raise_error(Phronomy::Persistence::SerializationError)
75
75
  end
76
76
 
77
77
  it "requires revision to advance exactly once" do
@@ -84,7 +84,7 @@ RSpec.shared_examples "an Agent repository" do
84
84
  expected_revision: 0,
85
85
  root: skipped
86
86
  )
87
- end.to raise_error(Phronomy::Storage::ConflictError)
87
+ end.to raise_error(Phronomy::Persistence::ConflictError)
88
88
  end
89
89
 
90
90
  it "deletes idempotently" do
@@ -94,6 +94,6 @@ RSpec.shared_examples "an Agent repository" do
94
94
 
95
95
  expect do
96
96
  agent_repository.load(agent_root.agent_id)
97
- end.to raise_error(Phronomy::Storage::NotFoundError)
97
+ end.to raise_error(Phronomy::Persistence::NotFoundError)
98
98
  end
99
99
  end