phronomy 0.26.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (557) hide show
  1. checksums.yaml +4 -4
  2. data/.mutant.yml +9 -9
  3. data/CHANGELOG.md +92 -0
  4. data/CONTRIBUTING.md +8 -8
  5. data/README.md +13 -3
  6. data/VERIFY.sh +27 -27
  7. data/benchmark/bench_agent_invoke.rb +26 -47
  8. data/benchmark/bench_context_assembler.rb +17 -15
  9. data/benchmark/bench_regression.rb +1 -1
  10. data/benchmark/bench_tool_schema.rb +1 -1
  11. data/docs/architecture/agent-chat-and-state-ownership.md +153 -0
  12. data/docs/architecture/agent-configuration-and-tool-binding.md +139 -0
  13. data/docs/architecture/agent-context.md +4 -2
  14. data/docs/architecture/agent-transition-ownership.md +13 -0
  15. data/docs/architecture/backend-groups.md +152 -0
  16. data/docs/architecture/before-llm-input.md +9 -3
  17. data/docs/architecture/context-management.md +31 -1
  18. data/docs/architecture/context-preparation-steps.md +90 -0
  19. data/docs/architecture/entry-action-and-team-wording.md +72 -0
  20. data/docs/architecture/execution-abstraction-review.md +88 -0
  21. data/docs/architecture/execution-boundaries.md +28 -0
  22. data/docs/architecture/execution-metadata-and-values.md +86 -0
  23. data/docs/architecture/generator-verifier-ownership.md +111 -0
  24. data/docs/architecture/multi-agent-handoff.md +11 -4
  25. data/docs/architecture/persistence-refactoring-plan.md +50 -0
  26. data/docs/architecture/persistence.md +45 -4
  27. data/docs/architecture/r8-unit1.md +104 -0
  28. data/docs/architecture/r8-unit10.md +72 -0
  29. data/docs/architecture/r8-unit11.md +71 -0
  30. data/docs/architecture/r8-unit12.md +67 -0
  31. data/docs/architecture/r8-unit13.md +61 -0
  32. data/docs/architecture/r8-unit14.md +72 -0
  33. data/docs/architecture/r8-unit15.md +65 -0
  34. data/docs/architecture/r8-unit16.md +54 -0
  35. data/docs/architecture/r8-unit17.md +73 -0
  36. data/docs/architecture/r8-unit18.md +92 -0
  37. data/docs/architecture/r8-unit19.md +61 -0
  38. data/docs/architecture/r8-unit2.md +55 -0
  39. data/docs/architecture/r8-unit20.md +51 -0
  40. data/docs/architecture/r8-unit21.md +79 -0
  41. data/docs/architecture/r8-unit22.md +74 -0
  42. data/docs/architecture/r8-unit23.md +81 -0
  43. data/docs/architecture/r8-unit3.md +130 -0
  44. data/docs/architecture/r8-unit4.md +79 -0
  45. data/docs/architecture/r8-unit5.md +115 -0
  46. data/docs/architecture/r8-unit6.md +71 -0
  47. data/docs/architecture/r8-unit7.md +70 -0
  48. data/docs/architecture/r8-unit8.md +78 -0
  49. data/docs/architecture/r8-unit9.md +73 -0
  50. data/docs/architecture/refactoring-closure.md +343 -0
  51. data/docs/architecture/remaining-refactoring-plan.md +390 -0
  52. data/docs/architecture/rubyllm-2-token-ownership.md +82 -0
  53. data/docs/architecture/storage-execution-boundaries.md +54 -0
  54. data/docs/architecture/tool-schema-recording-gap.md +56 -0
  55. data/docs/architecture/tracing.md +2 -2
  56. data/docs/architecture/workflow-terminal-ownership-design.md +104 -0
  57. data/docs/architecture.md +222 -0
  58. data/docs/decisions/014-unified-persistence-durable-state.md +11 -0
  59. data/docs/decisions/015-tool-public-facade-and-rbs-boundary.md +3 -3
  60. data/docs/decisions/024-event-loop-single-writer-agent-runtime.md +5 -1
  61. data/docs/decisions/025-process-local-agent-ownership-and-runtime-admission.md +13 -3
  62. data/docs/decisions/026-workflow-runtime-admission-and-durable-terminal-barrier.md +5 -1
  63. data/docs/decisions/027-llm-adapter-provider-boundary.md +2 -0
  64. data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +7 -0
  65. data/docs/decisions/031-durable-multi-agent-coordination.md +7 -0
  66. data/docs/decisions/032-storage-backend-composition.md +85 -0
  67. data/docs/decisions/033-domain-persistence-ownership.md +79 -0
  68. data/docs/decisions/034-handoff-runner-coordination-ownership.md +78 -0
  69. data/docs/decisions/035-tool-executor-capability-ownership.md +68 -0
  70. data/docs/decisions/036-context-contract-ownership.md +86 -0
  71. data/docs/decisions/037-common-definition-ownership.md +62 -0
  72. data/docs/decisions/038-responsibility-based-source-layout.md +145 -0
  73. data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +82 -0
  74. data/docs/decisions/040-configuration-default-composition.md +105 -0
  75. data/docs/decisions/041-feature-owned-identity-registries.md +100 -0
  76. data/docs/decisions/042-feature-owned-execution-state.md +106 -0
  77. data/docs/decisions/043-storage-execution-constraint-notifications.md +75 -0
  78. data/docs/decisions/044-agent-default-and-one-shot-composition.md +106 -0
  79. data/docs/decisions/045-worker-input-restriction-ownership.md +104 -0
  80. data/docs/decisions/046-agent-responsibility-layout-and-shared-records.md +110 -0
  81. data/docs/decisions/047-recovered-execution-continuation-contract.md +116 -0
  82. data/docs/decisions/048-dispatch-preparation-worker-ownership.md +120 -0
  83. data/docs/decisions/049-initial-preparation-worker-ownership.md +111 -0
  84. data/docs/decisions/050-approval-resume-snapshot-and-commit-ownership.md +98 -0
  85. data/docs/decisions/051-execution-outcome-worker-ownership.md +153 -0
  86. data/docs/decisions/052-tool-invocation-restoration-ownership.md +92 -0
  87. data/docs/decisions/053-shared-state-coordination-ownership.md +71 -0
  88. data/docs/decisions/054-workflow-terminal-save-single-owner.md +76 -0
  89. data/docs/decisions/055-terminal-observer-failure-settlement.md +60 -0
  90. data/docs/decisions/056-workflow-terminal-policy-ownership.md +87 -0
  91. data/docs/decisions/057-storage-transaction-boundaries.md +73 -0
  92. data/docs/decisions/058-neutral-storage-primitives.md +83 -0
  93. data/docs/decisions/059-backend-contracts-and-async-clients.md +74 -0
  94. data/docs/decisions/060-runtime-settings-and-application-configuration.md +86 -0
  95. data/docs/decisions/061-persistence-failure-contracts.md +89 -0
  96. data/docs/decisions/062-content-store-service.md +52 -0
  97. data/docs/decisions/063-execution-contracts-and-services.md +88 -0
  98. data/docs/decisions/064-agent-execution-and-multi-agent-responsibility.md +36 -0
  99. data/docs/decisions/065-vector-and-embeddings-common-operations.md +38 -0
  100. data/docs/decisions/066-llm-and-tool-operation-contracts.md +20 -0
  101. data/docs/decisions/067-agent-progress-and-engine-connection.md +29 -0
  102. data/docs/decisions/068-agent-tool-child-progress-and-tool-operation.md +36 -0
  103. data/docs/decisions/README.md +52 -6
  104. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_DESIGN_V2.md +6 -0
  105. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md +6 -0
  106. data/docs/execution-boundaries-preview.md +45 -0
  107. data/docs/features.md +16 -5
  108. data/docs/getting-started.md +11 -8
  109. data/docs/migrations/0.15.md +5 -0
  110. data/docs/migrations/0.22.md +2 -2
  111. data/docs/migrations/durable-semantic-coordination-v2.md +8 -0
  112. data/docs/migrations/handoff-runner-multi-agent.md +37 -0
  113. data/docs/migrations/neutral-storage-spi.md +43 -0
  114. data/docs/migrations/parallel-tool-chat-removal.md +49 -0
  115. data/docs/migrations/persistence-failure-contracts.md +67 -0
  116. data/docs/migrations/r8-unit10.md +36 -0
  117. data/docs/migrations/r8-unit11.md +33 -0
  118. data/docs/migrations/r8-unit12.md +31 -0
  119. data/docs/migrations/r8-unit13.md +36 -0
  120. data/docs/migrations/r8-unit14.md +31 -0
  121. data/docs/migrations/r8-unit15.md +31 -0
  122. data/docs/migrations/r8-unit16.md +26 -0
  123. data/docs/migrations/r8-unit17.md +28 -0
  124. data/docs/migrations/r8-unit18.md +29 -0
  125. data/docs/migrations/r8-unit19.md +28 -0
  126. data/docs/migrations/r8-unit20.md +24 -0
  127. data/docs/migrations/r8-unit21.md +28 -0
  128. data/docs/migrations/r8-unit22.md +29 -0
  129. data/docs/migrations/r8-unit23.md +20 -0
  130. data/docs/migrations/r8-unit4.md +67 -0
  131. data/docs/migrations/r8-unit5.md +75 -0
  132. data/docs/migrations/r8-unit6.md +109 -0
  133. data/docs/migrations/r8-unit7.md +31 -0
  134. data/docs/migrations/r8-unit8.md +33 -0
  135. data/docs/migrations/r8-unit9.md +28 -0
  136. data/docs/migrations/shared-state-multi-agent.md +44 -0
  137. data/docs/migrations/storage-async-client.md +103 -0
  138. data/docs/migrations/storage-backend-composition.md +117 -0
  139. data/docs/migrations/storage-transaction-boundaries.md +74 -0
  140. data/docs/migrations/vector-async-clients.md +110 -0
  141. data/docs/persistence-backends.md +190 -215
  142. data/docs/runtime-and-concurrency.md +142 -32
  143. data/lib/phronomy/agent/admission.rb +78 -0
  144. data/lib/phronomy/{agent_already_exists_error.rb → agent/agent_already_exists_error.rb} +2 -0
  145. data/lib/phronomy/{agent_busy_error.rb → agent/agent_busy_error.rb} +2 -0
  146. data/lib/phronomy/{agent_purged_error.rb → agent/agent_purged_error.rb} +2 -0
  147. data/lib/phronomy/agent/api/agent.rb +17 -0
  148. data/lib/phronomy/{execution_rehydration_required_error.rb → agent/api/execution_rehydration_required_error.rb} +2 -0
  149. data/lib/phronomy/agent/async_event_api.rb +38 -33
  150. data/lib/phronomy/agent/base.rb +106 -294
  151. data/lib/phronomy/agent/before_llm_input.rb +64 -0
  152. data/lib/phronomy/{agent.rb → agent/composition/run_once.rb} +4 -13
  153. data/lib/phronomy/agent/context_candidate_resolver.rb +2 -2
  154. data/lib/phronomy/agent/context_importer.rb +2 -2
  155. data/lib/phronomy/agent/{context_assembler.rb → context_preparation.rb} +157 -288
  156. data/lib/phronomy/agent/control_request.rb +21 -0
  157. data/lib/phronomy/agent/{agent_execution.rb → execution/agent_execution.rb} +8 -4
  158. data/lib/phronomy/agent/{agent_invocation.rb → execution/agent_invocation.rb} +32 -63
  159. data/lib/phronomy/agent/execution/approval_resume_commit.rb +108 -0
  160. data/lib/phronomy/agent/execution/dispatch_preparation.rb +195 -0
  161. data/lib/phronomy/agent/{exact_execution.rb → execution/exact_execution.rb} +43 -23
  162. data/lib/phronomy/agent/{execution_cancellation.rb → execution/execution_cancellation.rb} +8 -5
  163. data/lib/phronomy/agent/execution/execution_coordinator.rb +1744 -0
  164. data/lib/phronomy/agent/execution/execution_failure.rb +30 -0
  165. data/lib/phronomy/agent/execution/execution_metadata.rb +54 -0
  166. data/lib/phronomy/agent/execution/execution_outcome_committer.rb +288 -0
  167. data/lib/phronomy/agent/execution/execution_registry.rb +463 -0
  168. data/lib/phronomy/agent/execution/execution_session_runner.rb +113 -0
  169. data/lib/phronomy/agent/execution/initial_preparation.rb +323 -0
  170. data/lib/phronomy/agent/execution/invocation_actions.rb +368 -0
  171. data/lib/phronomy/agent/execution/invocation_transitions.rb +124 -0
  172. data/lib/phronomy/agent/execution/runtime_record_encoder.rb +210 -0
  173. data/lib/phronomy/agent/execution_change.rb +161 -0
  174. data/lib/phronomy/agent/execution_environment.rb +36 -0
  175. data/lib/phronomy/agent/execution_extension_state.rb +21 -0
  176. data/lib/phronomy/agent/execution_observation.rb +35 -0
  177. data/lib/phronomy/agent/filterable.rb +182 -0
  178. data/lib/phronomy/agent/{handoff_context.rb → handoff/transfer_context.rb} +6 -6
  179. data/lib/phronomy/agent/{handoff_projection.rb → handoff/transfer_projection.rb} +14 -33
  180. data/lib/phronomy/agent/handoff_error.rb +7 -0
  181. data/lib/phronomy/agent/{journal_projection.rb → journal/journal_projection.rb} +4 -0
  182. data/lib/phronomy/agent/{journal_record.rb → journal/journal_record.rb} +3 -3
  183. data/lib/phronomy/agent/{llm_call_record.rb → journal/llm_call_record.rb} +2 -2
  184. data/lib/phronomy/agent/knowledge_item.rb +13 -0
  185. data/lib/phronomy/agent/{agent_root.rb → lifecycle/agent_root.rb} +6 -2
  186. data/lib/phronomy/agent/lifecycle/default_persistence.rb +29 -0
  187. data/lib/phronomy/{engine/runtime/agent_ownership_registry.rb → agent/lifecycle/ownership_registry.rb} +24 -15
  188. data/lib/phronomy/agent/persistence/agent_repository.rb +69 -0
  189. data/lib/phronomy/agent/persistence/cancellation_repository.rb +51 -0
  190. data/lib/phronomy/agent/persistence/codec.rb +338 -0
  191. data/lib/phronomy/agent/persistence/execution_repository.rb +124 -0
  192. data/lib/phronomy/agent/persistence/journal_repository.rb +63 -0
  193. data/lib/phronomy/agent/persistence/records.rb +33 -0
  194. data/lib/phronomy/agent/persistence/retention_repository.rb +70 -0
  195. data/lib/phronomy/agent/persistence/storage_schema.rb +29 -0
  196. data/lib/phronomy/agent/persistence/watermark.rb +29 -0
  197. data/lib/phronomy/agent/recovery/invocation_restorer.rb +124 -0
  198. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/continuation.rb +28 -51
  199. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/installation.rb +50 -55
  200. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/resolution.rb +38 -43
  201. data/lib/phronomy/agent/{recovery_coordinator.rb → recovery/recovery_coordinator.rb} +26 -23
  202. data/lib/phronomy/agent/recovery/recovery_rules.rb +119 -0
  203. data/lib/phronomy/agent/recovery/recovery_support.rb +224 -0
  204. data/lib/phronomy/agent/reserved_execution.rb +16 -0
  205. data/lib/phronomy/agent/retention.rb +17 -0
  206. data/lib/phronomy/agent/runtime_binding/engine_environment.rb +60 -0
  207. data/lib/phronomy/agent/runtime_binding/engine_session_builder.rb +34 -0
  208. data/lib/phronomy/agent/runtime_binding/phase_machine_builder.rb +64 -0
  209. data/lib/phronomy/agent/runtime_binding/tool_session_builder.rb +34 -0
  210. data/lib/phronomy/agent/{ruby_llm_materializer.rb → runtime_input.rb} +19 -39
  211. data/lib/phronomy/agent/saved_context_reader.rb +55 -0
  212. data/lib/phronomy/agent/settings.rb +47 -0
  213. data/lib/phronomy/agent/state_writer.rb +166 -0
  214. data/lib/phronomy/agent/store.rb +253 -0
  215. data/lib/phronomy/{stream_callback_error.rb → agent/stream_callback_error.rb} +2 -0
  216. data/lib/phronomy/agent/{approval_evaluation_request.rb → tool_execution/approval_evaluation_request.rb} +1 -12
  217. data/lib/phronomy/agent/{tool_approval_request.rb → tool_execution/tool_approval_request.rb} +1 -10
  218. data/lib/phronomy/agent/tool_execution/tool_binding.rb +30 -0
  219. data/lib/phronomy/agent/{tool_invocation.rb → tool_execution/tool_invocation.rb} +114 -248
  220. data/lib/phronomy/agent/tool_execution/tool_invocation_actions.rb +107 -0
  221. data/lib/phronomy/agent/tool_execution/tool_invocation_transitions.rb +70 -0
  222. data/lib/phronomy/common/configuration_error.rb +7 -0
  223. data/lib/phronomy/common/error.rb +5 -0
  224. data/lib/phronomy/{agent → common/values}/immutable.rb +9 -1
  225. data/lib/phronomy/common/values/serializable.rb +32 -0
  226. data/lib/phronomy/configuration/runtime_settings.rb +49 -0
  227. data/lib/phronomy/content_store/storage_schema.rb +12 -0
  228. data/lib/phronomy/content_store/stored_contents.rb +44 -0
  229. data/lib/phronomy/context/assembly.rb +202 -0
  230. data/lib/phronomy/{context_budget_exceeded_error.rb → context/budget_exceeded_error.rb} +5 -1
  231. data/lib/phronomy/context/candidate.rb +51 -0
  232. data/lib/phronomy/context/constraint.rb +47 -0
  233. data/lib/phronomy/{agent → context}/context_plan.rb +2 -2
  234. data/lib/phronomy/{agent → context}/context_plan_validator.rb +3 -3
  235. data/lib/phronomy/{agent → context}/context_policy.rb +1 -1
  236. data/lib/phronomy/{agent → context}/context_policy_input.rb +7 -7
  237. data/lib/phronomy/{agent → context}/context_policy_input_builder.rb +1 -1
  238. data/lib/phronomy/context/default_policy.rb +140 -0
  239. data/lib/phronomy/context/final_budget_validator.rb +33 -0
  240. data/lib/phronomy/{agent → context}/llm_input_build_context.rb +2 -2
  241. data/lib/phronomy/{agent → context}/llm_input_manifest.rb +21 -21
  242. data/lib/phronomy/{agent → context}/llm_input_patch.rb +3 -3
  243. data/lib/phronomy/context/manifest_error.rb +10 -0
  244. data/lib/phronomy/context/prompt_template.rb +98 -0
  245. data/lib/phronomy/{vector_store → documents}/loader/base.rb +1 -1
  246. data/lib/phronomy/{vector_store → documents}/loader/csv_loader.rb +2 -2
  247. data/lib/phronomy/{vector_store → documents}/loader/markdown_loader.rb +3 -3
  248. data/lib/phronomy/{vector_store → documents}/loader/plain_text_loader.rb +2 -2
  249. data/lib/phronomy/{vector_store → documents}/splitter/base.rb +1 -1
  250. data/lib/phronomy/{vector_store → documents}/splitter/fixed_size_splitter.rb +2 -2
  251. data/lib/phronomy/{vector_store → documents}/splitter/recursive_splitter.rb +2 -2
  252. data/lib/phronomy/embeddings/async/async_client.rb +44 -0
  253. data/lib/phronomy/embeddings/backends/ruby_llm_embeddings.rb +48 -0
  254. data/lib/phronomy/embeddings/base.rb +43 -0
  255. data/lib/phronomy/embeddings/invalid_result_error.rb +11 -0
  256. data/lib/phronomy/embeddings/transport_error.rb +11 -0
  257. data/lib/phronomy/engine/concurrency/offload_pool.rb +39 -20
  258. data/lib/phronomy/engine/concurrency/pool_registry.rb +47 -12
  259. data/lib/phronomy/{diagnostics.rb → engine/diagnostics.rb} +1 -1
  260. data/lib/phronomy/engine/event_loop.rb +164 -630
  261. data/lib/phronomy/engine/execution_binding.rb +23 -0
  262. data/lib/phronomy/engine/execution_receiver_binding.rb +43 -0
  263. data/lib/phronomy/engine/fsm_protocol.rb +9 -0
  264. data/lib/phronomy/engine/fsm_session.rb +37 -30
  265. data/lib/phronomy/engine/metrics.rb +28 -0
  266. data/lib/phronomy/engine/runtime/timer_queue.rb +1 -1
  267. data/lib/phronomy/engine/runtime.rb +104 -95
  268. data/lib/phronomy/engine/terminal_decision.rb +7 -0
  269. data/lib/phronomy/execution/async_operation.rb +41 -0
  270. data/lib/phronomy/execution/backpressure_error.rb +7 -0
  271. data/lib/phronomy/{blocking.rb → execution/blocking.rb} +2 -2
  272. data/lib/phronomy/execution/cancellation_error.rb +7 -0
  273. data/lib/phronomy/{engine → execution}/concurrency/cancellation_token.rb +5 -1
  274. data/lib/phronomy/{engine → execution}/concurrency/deadline.rb +0 -17
  275. data/lib/phronomy/{engine → execution}/concurrency/physical_completion_task.rb +1 -1
  276. data/lib/phronomy/{engine → execution}/concurrency/result_collector.rb +1 -1
  277. data/lib/phronomy/{engine → execution}/concurrency/result_composition.rb +33 -12
  278. data/lib/phronomy/{engine/concurrency/subscriptions.rb → execution/concurrency/result_subscriptions.rb} +14 -18
  279. data/lib/phronomy/execution/concurrency/subscriptions.rb +25 -0
  280. data/lib/phronomy/execution/concurrency/worker_input_restricted.rb +14 -0
  281. data/lib/phronomy/execution/event_loop_reentrancy_error.rb +8 -0
  282. data/lib/phronomy/{execution.rb → execution/execution.rb} +57 -30
  283. data/lib/phronomy/{execution_cancellation_error.rb → execution/execution_cancellation_error.rb} +2 -0
  284. data/lib/phronomy/execution/execution_receiver.rb +78 -0
  285. data/lib/phronomy/execution/execution_scope_state.rb +35 -0
  286. data/lib/phronomy/{execution_timeout_error.rb → execution/execution_timeout_error.rb} +2 -0
  287. data/lib/phronomy/{invalid_async_entry_action_error.rb → execution/invalid_async_entry_action_error.rb} +2 -0
  288. data/lib/phronomy/{invalid_async_transition_action_error.rb → execution/invalid_async_transition_action_error.rb} +2 -0
  289. data/lib/phronomy/{invalid_async_workflow_action_error.rb → execution/invalid_async_workflow_action_error.rb} +2 -0
  290. data/lib/phronomy/{invocation_context.rb → execution/invocation_context.rb} +0 -15
  291. data/lib/phronomy/execution/invocation_controls.rb +73 -0
  292. data/lib/phronomy/execution/pool_shutdown_error.rb +7 -0
  293. data/lib/phronomy/execution/recursion_limit_error.rb +7 -0
  294. data/lib/phronomy/execution/runnable.rb +24 -0
  295. data/lib/phronomy/execution/runtime_shutdown_error.rb +7 -0
  296. data/lib/phronomy/execution/runtime_shutdown_reentrancy_error.rb +7 -0
  297. data/lib/phronomy/execution/scheduler_reentrancy_error.rb +9 -0
  298. data/lib/phronomy/{engine → execution}/task_result.rb +7 -3
  299. data/lib/phronomy/execution/timeout_error.rb +7 -0
  300. data/lib/phronomy/execution/wait_policy.rb +24 -0
  301. data/lib/phronomy/filter/filter_block_error.rb +14 -0
  302. data/lib/phronomy/generation/generator_verifier/agent_operation.rb +65 -0
  303. data/lib/phronomy/generation/generator_verifier/agent_result_receiver.rb +89 -0
  304. data/lib/phronomy/generation/generator_verifier/default_parser.rb +27 -0
  305. data/lib/phronomy/generation/generator_verifier/pipeline_state.rb +57 -0
  306. data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +112 -0
  307. data/lib/phronomy/generation/generator_verifier.rb +118 -0
  308. data/lib/phronomy/generation/low_confidence_error.rb +14 -0
  309. data/lib/phronomy/llm_adapter/async/async_client.rb +47 -0
  310. data/lib/phronomy/llm_adapter/authentication_error.rb +9 -0
  311. data/lib/phronomy/llm_adapter/backends/ruby_llm.rb +183 -0
  312. data/lib/phronomy/llm_adapter/base.rb +49 -68
  313. data/lib/phronomy/llm_adapter/context_length_error.rb +9 -0
  314. data/lib/phronomy/llm_adapter/invalid_result_error.rb +10 -0
  315. data/lib/phronomy/llm_adapter/message.rb +62 -0
  316. data/lib/phronomy/llm_adapter/rate_limit_error.rb +9 -0
  317. data/lib/phronomy/llm_adapter/request.rb +79 -0
  318. data/lib/phronomy/llm_adapter/response.rb +36 -0
  319. data/lib/phronomy/llm_adapter/stream_chunk.rb +18 -0
  320. data/lib/phronomy/llm_adapter/token_usage.rb +64 -0
  321. data/lib/phronomy/llm_adapter/transport_error.rb +9 -0
  322. data/lib/phronomy/llm_adapter.rb +3 -16
  323. data/lib/phronomy/llm_context_window/token_budget.rb +6 -7
  324. data/lib/phronomy/multi_agent/admission_registry.rb +15 -2
  325. data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +108 -84
  326. data/lib/phronomy/multi_agent/execution_environment.rb +29 -0
  327. data/lib/phronomy/{agent → multi_agent}/handoff.rb +2 -2
  328. data/lib/phronomy/{agent → multi_agent}/handoff_capability_factory.rb +21 -2
  329. data/lib/phronomy/multi_agent/handoff_participant.rb +79 -0
  330. data/lib/phronomy/{agent → multi_agent}/handoff_policy.rb +1 -1
  331. data/lib/phronomy/multi_agent/handoff_runner.rb +156 -0
  332. data/lib/phronomy/{agent → multi_agent}/handoff_state.rb +2 -2
  333. data/lib/phronomy/multi_agent/orchestrator.rb +61 -75
  334. data/lib/phronomy/multi_agent/persistence/codec.rb +75 -0
  335. data/lib/phronomy/multi_agent/persistence/handoff_state_repository.rb +77 -0
  336. data/lib/phronomy/multi_agent/persistence/records.rb +27 -0
  337. data/lib/phronomy/multi_agent/persistence/storage_schema.rb +21 -0
  338. data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +124 -0
  339. data/lib/phronomy/multi_agent/persistence/team_repository.rb +69 -0
  340. data/lib/phronomy/multi_agent/reserved_child_admission.rb +88 -0
  341. data/lib/phronomy/multi_agent/runtime_binding/engine_environment.rb +35 -0
  342. data/lib/phronomy/{agent → multi_agent}/shared_state.rb +56 -39
  343. data/lib/phronomy/multi_agent/store.rb +156 -0
  344. data/lib/phronomy/multi_agent/team_coordinator.rb +87 -75
  345. data/lib/phronomy/multi_agent/team_execution.rb +1 -1
  346. data/lib/phronomy/{engine/runtime → multi_agent}/team_ownership_registry.rb +5 -5
  347. data/lib/phronomy/multi_agent/team_root.rb +1 -1
  348. data/lib/phronomy/output_parser/parse_error.rb +7 -0
  349. data/lib/phronomy/persistence/content_repository.rb +26 -0
  350. data/lib/phronomy/persistence/errors.rb +16 -0
  351. data/lib/phronomy/persistence/migration/initial_format_migration.rb +21 -21
  352. data/lib/phronomy/persistence/persistence.rb +23 -0
  353. data/lib/phronomy/persistence/save_outcome.rb +24 -0
  354. data/lib/phronomy/persistence/snapshot_comparison.rb +81 -0
  355. data/lib/phronomy/persistence/storage_boundary.rb +32 -0
  356. data/lib/phronomy/persistence/transaction.rb +86 -0
  357. data/lib/phronomy/persistence_composition/storage_schema.rb +29 -0
  358. data/lib/phronomy/persistence_composition/stores.rb +34 -0
  359. data/lib/phronomy/persistence_composition/unit4_migration.rb +276 -0
  360. data/lib/phronomy/runtime_composition/agent_defaults.rb +11 -0
  361. data/lib/phronomy/runtime_composition/configuration.rb +146 -0
  362. data/lib/phronomy/runtime_composition/configuration_defaults.rb +9 -0
  363. data/lib/phronomy/runtime_composition/evaluation_defaults.rb +7 -0
  364. data/lib/phronomy/runtime_composition/execution_defaults.rb +5 -0
  365. data/lib/phronomy/runtime_composition/generation_defaults.rb +6 -0
  366. data/lib/phronomy/runtime_composition/global_configuration.rb +38 -0
  367. data/lib/phronomy/runtime_composition/global_runtime.rb +19 -0
  368. data/lib/phronomy/runtime_composition/multi_agent_defaults.rb +9 -0
  369. data/lib/phronomy/runtime_composition/workflow_defaults.rb +4 -0
  370. data/lib/phronomy/storage/async/async_client.rb +60 -0
  371. data/lib/phronomy/storage/backend.rb +107 -0
  372. data/lib/phronomy/storage/backends/in_memory.rb +157 -0
  373. data/lib/phronomy/storage/blob_conflict_error.rb +10 -0
  374. data/lib/phronomy/storage/blobs.rb +31 -0
  375. data/lib/phronomy/storage/condition.rb +25 -0
  376. data/lib/phronomy/storage/condition_failed_error.rb +16 -0
  377. data/lib/phronomy/storage/conflict_error.rb +9 -0
  378. data/lib/phronomy/{persistence → storage}/durable_record.rb +12 -12
  379. data/lib/phronomy/storage/entry.rb +33 -0
  380. data/lib/phronomy/storage/guard_ref.rb +13 -0
  381. data/lib/phronomy/storage/not_found_error.rb +9 -0
  382. data/lib/phronomy/storage/record_codec.rb +177 -0
  383. data/lib/phronomy/storage/records.rb +61 -0
  384. data/lib/phronomy/storage/resource.rb +126 -0
  385. data/lib/phronomy/storage/scope.rb +25 -0
  386. data/lib/phronomy/storage/serialization_error.rb +9 -0
  387. data/lib/phronomy/storage/streams.rb +44 -0
  388. data/lib/phronomy/storage/transaction_error.rb +10 -0
  389. data/lib/phronomy/storage/unique_constraint_error.rb +17 -0
  390. data/lib/phronomy/storage/unsupported_backend_error.rb +9 -0
  391. data/lib/phronomy/storage/validation.rb +53 -0
  392. data/lib/phronomy/storage/view.rb +131 -0
  393. data/lib/phronomy/testing/eval/metrics.rb +1 -1
  394. data/lib/phronomy/testing/eval/scorer/llm_judge.rb +19 -3
  395. data/lib/phronomy/testing/persistence_contract/a_content_store.rb +1 -1
  396. data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +2 -2
  397. data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +54 -48
  398. data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +1 -1
  399. data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +1 -1
  400. data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +5 -5
  401. data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +52 -46
  402. data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +263 -0
  403. data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +124 -0
  404. data/lib/phronomy/testing/persistence_contract.rb +4 -0
  405. data/lib/phronomy/tool/authorization.rb +77 -0
  406. data/lib/phronomy/tool/base.rb +549 -9
  407. data/lib/phronomy/tool/call_request.rb +40 -0
  408. data/lib/phronomy/{agent/tool_definition_set.rb → tool/definition_set.rb} +16 -12
  409. data/lib/phronomy/tool/operation.rb +88 -0
  410. data/lib/phronomy/tool/schema.rb +214 -0
  411. data/lib/phronomy/tool/settings.rb +31 -0
  412. data/lib/phronomy/tool/tool_error.rb +7 -0
  413. data/lib/phronomy/{agent → tool}/tool_executor.rb +10 -7
  414. data/lib/phronomy/tools/agent.rb +22 -90
  415. data/lib/phronomy/tools/mcp.rb +21 -14
  416. data/lib/phronomy/tools/vector_search.rb +4 -4
  417. data/lib/phronomy/tracing/automatic.rb +11 -6
  418. data/lib/phronomy/tracing/base.rb +1 -1
  419. data/lib/phronomy/tracing/observation.rb +37 -0
  420. data/lib/phronomy/tracing/settings.rb +36 -0
  421. data/lib/phronomy/vector_store/{async_backend.rb → async/async_client.rb} +35 -29
  422. data/lib/phronomy/vector_store/{in_memory.rb → backends/in_memory.rb} +12 -10
  423. data/lib/phronomy/vector_store/{pgvector.rb → backends/pgvector.rb} +44 -16
  424. data/lib/phronomy/vector_store/{redis_search.rb → backends/redis_search.rb} +74 -37
  425. data/lib/phronomy/vector_store/base.rb +118 -49
  426. data/lib/phronomy/vector_store/invalid_result_error.rb +12 -0
  427. data/lib/phronomy/vector_store.rb +2 -17
  428. data/lib/phronomy/version.rb +1 -1
  429. data/lib/phronomy/workflow/completion.rb +11 -0
  430. data/lib/phronomy/{workflow.rb → workflow/execution/workflow.rb} +10 -8
  431. data/lib/phronomy/workflow/execution/workflow_action_rules.rb +67 -0
  432. data/lib/phronomy/{workflow_context.rb → workflow/execution/workflow_context.rb} +5 -1
  433. data/lib/phronomy/workflow/execution/workflow_context_ownership_error.rb +7 -0
  434. data/lib/phronomy/workflow/execution/workflow_execution_environment.rb +27 -0
  435. data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +186 -0
  436. data/lib/phronomy/{workflow_runner.rb → workflow/execution/workflow_runner.rb} +145 -115
  437. data/lib/phronomy/workflow/execution/workflow_settings.rb +28 -0
  438. data/lib/phronomy/workflow/persistence/codec.rb +153 -0
  439. data/lib/phronomy/workflow/persistence/state_repository.rb +63 -0
  440. data/lib/phronomy/workflow/persistence/storage_schema.rb +13 -0
  441. data/lib/phronomy/workflow/runtime_binding/workflow_engine_environment.rb +38 -0
  442. data/lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb +156 -0
  443. data/lib/phronomy/workflow/runtime_binding/workflow_terminal_policy.rb +40 -0
  444. data/lib/phronomy.rb +97 -98
  445. data/scripts/api_snapshot.rb +36 -16
  446. data/scripts/storage_spi_snapshot.rb +36 -0
  447. data/sig/_private/phronomy/agent/settings.rbs +19 -0
  448. data/sig/_private/phronomy/configuration/runtime_settings.rbs +20 -0
  449. data/sig/_private/phronomy/llm_adapter/async/async_client.rbs +10 -0
  450. data/sig/_private/phronomy/multi_agent/store.rbs +8 -0
  451. data/sig/_private/phronomy/persistence/storage_boundary.rbs +7 -0
  452. data/sig/_private/phronomy/tool/settings.rbs +13 -0
  453. data/sig/_private/phronomy/tracing/settings.rbs +15 -0
  454. data/sig/_private/phronomy/workflow/execution/workflow_settings.rbs +13 -0
  455. data/sig/phronomy/agent/admission.rbs +19 -0
  456. data/sig/phronomy/agent/execution_contract.rbs +132 -0
  457. data/sig/phronomy/agent/execution_environment.rbs +20 -0
  458. data/sig/phronomy/agent/store.rbs +73 -0
  459. data/sig/phronomy/agent.rbs +14 -139
  460. data/sig/phronomy/common.rbs +7 -0
  461. data/sig/phronomy/content_store/contracts.rbs +13 -0
  462. data/sig/phronomy/context.rbs +152 -0
  463. data/sig/phronomy/embeddings/async/async_client.rbs +9 -0
  464. data/sig/phronomy/embeddings/base.rbs +17 -0
  465. data/sig/phronomy/engine/concurrency/timer_bindings.rbs +22 -0
  466. data/sig/phronomy/engine/control_bindings.rbs +11 -0
  467. data/sig/phronomy/engine/control_protocols.rbs +23 -0
  468. data/sig/phronomy/engine/execution_binding.rbs +6 -0
  469. data/sig/phronomy/engine/execution_receiver.rbs +15 -0
  470. data/sig/phronomy/engine/observability.rbs +13 -0
  471. data/sig/phronomy/execution/async_operation.rbs +7 -0
  472. data/sig/phronomy/execution/blocking.rbs +5 -0
  473. data/sig/phronomy/execution/concurrency/cancellation_token.rbs +13 -0
  474. data/sig/phronomy/execution/concurrency/deadline.rbs +10 -0
  475. data/sig/phronomy/execution/concurrency/result_composition.rbs +7 -0
  476. data/sig/phronomy/execution/control_protocols.rbs +18 -0
  477. data/sig/phronomy/execution/errors.rbs +7 -0
  478. data/sig/phronomy/execution/execution.rbs +41 -0
  479. data/sig/phronomy/execution/execution_receiver.rbs +40 -0
  480. data/sig/phronomy/execution/execution_scope_state.rbs +9 -0
  481. data/sig/phronomy/execution/invocation_context.rbs +18 -0
  482. data/sig/phronomy/execution/invocation_controls.rbs +13 -0
  483. data/sig/phronomy/execution/task_result.rbs +29 -0
  484. data/sig/phronomy/extensions.rbs +0 -21
  485. data/sig/phronomy/handoff.rbs +12 -4
  486. data/sig/phronomy/llm_adapter/backends/ruby_llm.rbs +6 -0
  487. data/sig/phronomy/llm_adapter/base.rbs +14 -0
  488. data/sig/phronomy/llm_adapter/usage.rbs +14 -0
  489. data/sig/phronomy/llm_adapter/values.rbs +43 -0
  490. data/sig/phronomy/llm_context_window.rbs +10 -0
  491. data/sig/phronomy/multi_agent/reserved_child_admission.rbs +8 -0
  492. data/sig/phronomy/multi_agent/store.rbs +54 -0
  493. data/sig/phronomy/multi_agent.rbs +24 -3
  494. data/sig/phronomy/persistence/errors.rbs +18 -0
  495. data/sig/phronomy/persistence/migration.rbs +13 -0
  496. data/sig/phronomy/persistence/transaction.rbs +19 -0
  497. data/sig/phronomy/persistence.rbs +3 -185
  498. data/sig/phronomy/persistence_composition.rbs +19 -0
  499. data/sig/phronomy/runtime_composition/configuration.rbs +36 -0
  500. data/sig/phronomy/storage/async/async_client.rbs +8 -0
  501. data/sig/phronomy/storage/backends/in_memory.rbs +9 -0
  502. data/sig/phronomy/storage/contracts.rbs +160 -0
  503. data/sig/phronomy/tool.rbs +58 -5
  504. data/sig/phronomy/tracing/observation.rbs +7 -0
  505. data/sig/phronomy/vector_store/async/async_client.rbs +13 -0
  506. data/sig/phronomy/vector_store/base.rbs +23 -0
  507. data/sig/phronomy/workflow/execution/workflow_execution_environment.rbs +14 -0
  508. data/sig/phronomy/workflow/runtime_binding/workflow_terminal_policy.rbs +7 -0
  509. data/sig/phronomy/workflow/store.rbs +8 -0
  510. data/sig/phronomy/workflow.rbs +1 -1
  511. data/sig/phronomy.rbs +0 -49
  512. metadata +462 -130
  513. data/lib/phronomy/agent/agent_invocation_session_builder.rb +0 -584
  514. data/lib/phronomy/agent/concerns/before_llm_input.rb +0 -66
  515. data/lib/phronomy/agent/concerns/error_translation.rb +0 -45
  516. data/lib/phronomy/agent/concerns/filterable.rb +0 -184
  517. data/lib/phronomy/agent/context/capability/base.rb +0 -507
  518. data/lib/phronomy/agent/context/instruction/prompt_template.rb +0 -102
  519. data/lib/phronomy/agent/context_parts/validators/final_budget_validator.rb +0 -37
  520. data/lib/phronomy/agent/context_policies/default.rb +0 -142
  521. data/lib/phronomy/agent/execution_coordinator.rb +0 -3151
  522. data/lib/phronomy/agent/handoff_execution_coordinator.rb +0 -143
  523. data/lib/phronomy/agent/handoff_request.rb +0 -45
  524. data/lib/phronomy/agent/handoff_runner.rb +0 -178
  525. data/lib/phronomy/agent/phase_machine_builder.rb +0 -152
  526. data/lib/phronomy/agent/provider_call_outcome.rb +0 -106
  527. data/lib/phronomy/agent/recovery_support.rb +0 -504
  528. data/lib/phronomy/agent/selection/candidate.rb +0 -53
  529. data/lib/phronomy/agent/selection/constraint.rb +0 -49
  530. data/lib/phronomy/agent/token_budget_resolver.rb +0 -70
  531. data/lib/phronomy/agent/tool_call_intercepted.rb +0 -29
  532. data/lib/phronomy/agent/tool_invocation_session_builder.rb +0 -276
  533. data/lib/phronomy/configuration.rb +0 -59
  534. data/lib/phronomy/engine/concurrency/cancellation_scope.rb +0 -124
  535. data/lib/phronomy/engine/concurrency/operation_binding.rb +0 -43
  536. data/lib/phronomy/generator_verifier.rb +0 -369
  537. data/lib/phronomy/invalid_context_budget_configuration_error.rb +0 -8
  538. data/lib/phronomy/llm_adapter/ruby_llm.rb +0 -47
  539. data/lib/phronomy/metrics.rb +0 -26
  540. data/lib/phronomy/multi_agent/parallel_tool_chat.rb +0 -116
  541. data/lib/phronomy/persistence/durable_codec.rb +0 -706
  542. data/lib/phronomy/persistence/in_memory.rb +0 -690
  543. data/lib/phronomy/persistence/repository_facades.rb +0 -535
  544. data/lib/phronomy/persistence.rb +0 -276
  545. data/lib/phronomy/recovery.rb +0 -186
  546. data/lib/phronomy/ruby_llm_patches.rb +0 -24
  547. data/lib/phronomy/runnable.rb +0 -54
  548. data/lib/phronomy/token_usage.rb +0 -85
  549. data/lib/phronomy/vector_store/embeddings/base.rb +0 -44
  550. data/lib/phronomy/vector_store/embeddings/ruby_llm_embeddings.rb +0 -47
  551. data/lib/phronomy/workflow/phase_machine_builder.rb +0 -205
  552. data/lib/phronomy/workflow_recovery.rb +0 -123
  553. data/sig/phronomy/llm_adapter.rbs +0 -11
  554. data/sig/phronomy/runtime.rbs +0 -76
  555. /data/lib/phronomy/agent/{llm_operation_result.rb → execution/llm_operation_result.rb} +0 -0
  556. /data/lib/phronomy/{canonical_json.rb → common/canonical_json.rb} +0 -0
  557. /data/lib/phronomy/{event.rb → engine/event.rb} +0 -0
@@ -0,0 +1,146 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "forwardable"
4
+ require_relative "../configuration/runtime_settings"
5
+ require_relative "../agent/settings"
6
+ require_relative "../workflow/execution/workflow_settings"
7
+ require_relative "../tool/settings"
8
+ require_relative "../tracing/settings"
9
+
10
+ module Phronomy
11
+ # Application-facing configuration composes domain options and neutral runtime
12
+ # settings. Public accessors and constructor behavior stay unchanged.
13
+ class Configuration
14
+ extend Forwardable
15
+
16
+ DEFAULT_FACTORIES = {}
17
+ private_constant :DEFAULT_FACTORIES
18
+
19
+ # Bind fresh-instance factories once during application loading. Concrete
20
+ # component selection belongs to runtime_composition, not configuration.
21
+ # Applications replace instances through the existing configuration writers.
22
+ # @api private
23
+ def self.install_default_factories(tracer:, llm_adapter:)
24
+ DEFAULT_FACTORIES.replace(tracer: tracer, llm_adapter: llm_adapter).freeze
25
+ nil
26
+ end
27
+
28
+ attr_accessor :default_embedding_model, :multi_agent_store
29
+
30
+ # Preserve the public settings facade while each domain owns its values.
31
+ def_delegators :@__agent_settings, :default_model, :default_model=
32
+ def_delegators :@__agent_settings, :before_llm_input, :before_llm_input=
33
+ def_delegators :@__agent_settings, :agent_store, :agent_store=
34
+ def_delegators :@__agent_settings, :llm_adapter, :llm_adapter=
35
+ def_delegators :@__agent_settings, :stream_callback_error_policy, :stream_callback_error_policy=
36
+ def_delegators :@__agent_settings, :authorization_timeout, :authorization_timeout=
37
+ def_delegators :@__workflow_settings, :recursion_limit, :recursion_limit=
38
+ def_delegators :@__workflow_settings, :workflow_store, :workflow_store=
39
+ def_delegators :@__runtime_settings, :authorization_pool_size, :authorization_pool_size=
40
+ def_delegators :@__runtime_settings, :authorization_queue_size, :authorization_queue_size=
41
+
42
+ # @api private
43
+ attr_reader :__agent_settings, :__workflow_settings, :__tool_settings, :__tracing_settings
44
+
45
+ # Engine access is bound by the composition root, not by Engine itself.
46
+ # @api private
47
+ attr_reader :__runtime_settings
48
+
49
+ def tracer
50
+ @__tracing_settings.tracer
51
+ end
52
+
53
+ def tracer=(value)
54
+ @__tracing_settings.tracer = value
55
+ end
56
+
57
+ def trace_pii
58
+ @__tracing_settings.trace_pii
59
+ end
60
+
61
+ def trace_pii=(value)
62
+ @__tracing_settings.trace_pii = value
63
+ end
64
+
65
+ def logger
66
+ @__runtime_settings.logger
67
+ end
68
+
69
+ def logger=(value)
70
+ @__runtime_settings.logger = value
71
+ end
72
+
73
+ def tool_result_max_size
74
+ @__tool_settings.max_result_size
75
+ end
76
+
77
+ def tool_result_max_size=(value)
78
+ @__tool_settings.max_result_size = value
79
+ end
80
+
81
+ def event_loop_stop_grace_seconds
82
+ @__runtime_settings.event_loop_stop_grace_seconds
83
+ end
84
+
85
+ def event_loop_stop_grace_seconds=(value)
86
+ @__runtime_settings.event_loop_stop_grace_seconds = value
87
+ end
88
+
89
+ def event_loop_starvation_threshold_seconds
90
+ @__runtime_settings.event_loop_starvation_threshold_seconds
91
+ end
92
+
93
+ def event_loop_starvation_threshold_seconds=(value)
94
+ @__runtime_settings.event_loop_starvation_threshold_seconds = value
95
+ end
96
+
97
+ def event_loop_dispatch_threshold_seconds
98
+ @__runtime_settings.event_loop_dispatch_threshold_seconds
99
+ end
100
+
101
+ def event_loop_dispatch_threshold_seconds=(value)
102
+ @__runtime_settings.event_loop_dispatch_threshold_seconds = value
103
+ end
104
+
105
+ def offload_pool_size
106
+ @__runtime_settings.offload_pool_size
107
+ end
108
+
109
+ def offload_pool_size=(value)
110
+ @__runtime_settings.offload_pool_size = value
111
+ end
112
+
113
+ def offload_queue_size
114
+ @__runtime_settings.offload_queue_size
115
+ end
116
+
117
+ def offload_queue_size=(value)
118
+ @__runtime_settings.offload_queue_size = value
119
+ end
120
+
121
+ def initialize
122
+ @__workflow_settings = WorkflowSettings.new(recursion_limit: 25)
123
+ @__runtime_settings = RuntimeSettings.new
124
+ @__tool_settings = Tool::Settings.new
125
+ @__tracing_settings = Tracing::Settings.new(tracer: DEFAULT_FACTORIES.fetch(:tracer).call)
126
+ @__agent_settings = Agent::Settings.new(
127
+ llm_adapter: DEFAULT_FACTORIES.fetch(:llm_adapter).call,
128
+ stream_callback_error_policy: :report, authorization_timeout: 5
129
+ )
130
+ @multi_agent_store = nil
131
+ end
132
+
133
+ private
134
+
135
+ # A scoped configuration copy owns its scalar settings while retaining the
136
+ # same injected tracer/logger identities, just like the previous flat object.
137
+ def initialize_copy(original)
138
+ super
139
+ @__runtime_settings = original.__runtime_settings.dup
140
+ @__agent_settings = original.__agent_settings.dup
141
+ @__workflow_settings = original.__workflow_settings.dup
142
+ @__tool_settings = original.__tool_settings.dup
143
+ @__tracing_settings = original.__tracing_settings.dup
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Select concrete defaults at the application composition boundary. Each factory
4
+ # runs during Configuration.new, not during binding; no Runtime is started here.
5
+ # Explicitly loaded after Zeitwerk setup and before global configuration access.
6
+ Phronomy::Configuration.install_default_factories(
7
+ tracer: -> { Phronomy::Tracing::NullTracer.new },
8
+ llm_adapter: -> { Phronomy::LLMAdapter::RubyLLM.new }
9
+ )
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Binding creates no client, Configuration or Runtime. Resolve the configured
4
+ # adapter for every score at the existing read point; do not cache the client.
5
+ Phronomy::Testing::Eval::Scorer::LlmJudge.install_client_factory(
6
+ -> { Phronomy::LLMAdapter::AsyncClient.new(adapter: Phronomy.configuration.llm_adapter) }
7
+ )
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ Phronomy::Execution.backend_provider = -> { Phronomy::ExecutionBinding }
4
+
5
+ Phronomy::ExecutionReceiver.install_binding(-> { Phronomy::ExecutionReceiverBinding })
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Select the existing default without constructing a parser during boot.
4
+ Phronomy::GeneratorVerifier::DefaultParser.install_factory(
5
+ -> { Phronomy::OutputParser::JsonParser.new }
6
+ )
@@ -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,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Application lifecycle coordination across Runtime and global configuration.
4
+ # Explicitly loaded by the application entry point; not an Engine primitive.
5
+ module Phronomy
6
+ class << self
7
+ def reset_runtime!(timeout: configuration.event_loop_stop_grace_seconds)
8
+ previous_grace = @configuration&.event_loop_stop_grace_seconds
9
+ result = Runtime.reset_default!(timeout: timeout)
10
+
11
+ new_configuration = Configuration.new
12
+ if previous_grace
13
+ new_configuration.event_loop_stop_grace_seconds = previous_grace
14
+ end
15
+ @configuration = new_configuration
16
+ result
17
+ end
18
+ end
19
+ end
@@ -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
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Storage
5
+ # Neutral storage SPI. Domain resource declarations are supplied by composition.
6
+ # @api public
7
+ class Backend
8
+ REQUIRED_CAPABILITIES = {spi_version: 2, atomic_resources: true,
9
+ record_cas: true, stream_cas: true, conditional_unique: true,
10
+ guarded_checks: true, nested_savepoints: true}.freeze
11
+ attr_reader :resources, :view
12
+
13
+ # @api public
14
+ def initialize(resources:)
15
+ unless resources.is_a?(Array) && resources.all? { |resource| resource.is_a?(Resource) }
16
+ raise UnsupportedBackendError, "SPI 2 requires Resource declarations"
17
+ end
18
+ @resources = resources.to_h { |resource| [resource.id, resource] }.freeze
19
+ raise UnsupportedBackendError, "duplicate resource identity" unless @resources.length == resources.length
20
+ @resources.each_value do |resource|
21
+ next unless resource.guard
22
+ anchor = @resources[resource.guard.fetch(:resource)]
23
+ raise UnsupportedBackendError, "guard must name a registered records resource" unless anchor && anchor.kind == :records
24
+ end
25
+ @view = View.new(self)
26
+ end
27
+
28
+ # Reject the previous eight-repository SPI before constructing domain wrappers.
29
+ # @api public
30
+ def self.validate_capabilities!(backend)
31
+ capabilities = backend.respond_to?(:capabilities) ? backend.capabilities : {}
32
+ missing = REQUIRED_CAPABILITIES.reject { |key, value| capabilities.is_a?(Hash) && capabilities[key] == value }
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(", ")}"
36
+ end
37
+ end
38
+
39
+ # @api public
40
+ def capabilities
41
+ REQUIRED_CAPABILITIES.transform_values { |value| (value == true) ? false : value }.freeze
42
+ end
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
+
49
+ # All declared resources participate in one atomic transaction domain.
50
+ # Record and stream revisions provide compare-and-swap conflict detection.
51
+ # This does not
52
+ # mean cross-process Workflow admission or distributed locking.
53
+ # Storage failures whose commit outcome is fundamentally unknown remain
54
+ # database failures; Phronomy does not claim exactly-once semantics.
55
+ # Explicit nesting uses savepoints. Non-local block exits raise and roll back.
56
+ # @api public
57
+ def transaction
58
+ current_view&.validate!
59
+ storage_transaction do |context|
60
+ scope = Scope.new
61
+ bound = View.new(self, scope: scope, context: context)
62
+ views = stack
63
+ views.push(bound)
64
+ completed = false
65
+ begin
66
+ result = yield bound
67
+ scope.check!
68
+ completed = true
69
+ result
70
+ ensure
71
+ scope.close!
72
+ views.pop
73
+ Thread.current.thread_variable_get(:phronomy_storage_scopes)&.delete(self) if views.empty?
74
+ if !completed && $!.nil?
75
+ raise TransactionError, "transaction block must complete normally; non-local exit rolled back"
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ # @api private
82
+ def current_view
83
+ Thread.current.thread_variable_get(:phronomy_storage_scopes)&.dig(self)&.last.tap { |bound| bound&.validate! }
84
+ end
85
+
86
+ # @api private
87
+ def execute(context, operation, resource, **arguments)
88
+ send(operation, context, resource, **arguments)
89
+ end
90
+
91
+ private
92
+
93
+ def stack
94
+ scopes = Thread.current.thread_variable_get(:phronomy_storage_scopes)
95
+ unless scopes
96
+ scopes = {}
97
+ Thread.current.thread_variable_set(:phronomy_storage_scopes, scopes)
98
+ end
99
+ scopes[self] ||= []
100
+ end
101
+
102
+ def storage_transaction
103
+ raise UnsupportedBackendError, "backend must implement an atomic storage transaction"
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,157 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "monitor"
4
+
5
+ module Phronomy
6
+ module Storage
7
+ module Backends
8
+ # One Monitor and snapshot cover every declared resource and nested scope.
9
+ # @api public
10
+ class InMemory < Backend
11
+ # @api public
12
+ def initialize(resources: [])
13
+ super
14
+ @monitor = Monitor.new
15
+ @state = resources.to_h { |resource| [resource.id, {}] }
16
+ end
17
+
18
+ # @api public
19
+ def capabilities = REQUIRED_CAPABILITIES
20
+
21
+ private
22
+
23
+ def storage_transaction
24
+ @monitor.synchronize do
25
+ snapshot = Marshal.load(Marshal.dump(@state))
26
+ completed = false
27
+ begin
28
+ result = yield @state
29
+ completed = true
30
+ result
31
+ ensure
32
+ @state.replace(snapshot) unless completed
33
+ end
34
+ end
35
+ end
36
+
37
+ def bucket(state, resource) = state.fetch(resource.id)
38
+
39
+ def read_record(state, resource, key:)
40
+ value = bucket(state, resource)[key]
41
+ value && Entry::Record.new(**value.to_h)
42
+ end
43
+
44
+ def lock_guard(state, resource, key:)
45
+ raise NotFoundError, "guard record not found: #{resource.id}/#{key}" unless bucket(state, resource).key?(key)
46
+ true
47
+ end
48
+
49
+ def guard_record(state, resource, key:, attributes: {}, creating: false)
50
+ guard = resource.guard
51
+ return unless guard
52
+ anchor = resources.fetch(guard.fetch(:resource))
53
+ return if creating && anchor.equal?(resource) && guard[:via] == :key
54
+ guard_key = [:key, :stream].include?(guard[:via]) ? key : attributes.fetch(guard[:via])
55
+ lock_guard(state, anchor, key: guard_key)
56
+ end
57
+
58
+ def check_unique(state, resource, entry)
59
+ resource.unique.each do |constraint|
60
+ next if constraint[:fields].any? { |key| entry.attributes[key].nil? }
61
+ next unless constraint.fetch(:where).all? { |key, value| entry.attributes[key] == value }
62
+ conflict = bucket(state, resource).values.any? do |other|
63
+ other.key != entry.key && constraint[:where].all? { |k, val| other.attributes[k] == val } && constraint[:fields].all? { |k| other.attributes[k] == entry.attributes[k] }
64
+ end
65
+ raise UniqueConstraintError.new(resource: resource, constraint: constraint[:name]) if conflict
66
+ end
67
+ end
68
+
69
+ def insert_record(state, resource, entry:)
70
+ raise ConflictError, "duplicate record identity" if bucket(state, resource).key?(entry.key)
71
+ guard_record(state, resource, key: entry.key, attributes: entry.attributes, creating: true)
72
+ check_unique(state, resource, entry)
73
+ bucket(state, resource)[entry.key] = entry
74
+ Entry::Record.new(**entry.to_h)
75
+ end
76
+
77
+ def replace_record(state, resource, entry:, expected_revision:, expected_attributes:)
78
+ current = bucket(state, resource)[entry.key] || raise(NotFoundError, "record not found: #{entry.key}")
79
+ guard_record(state, resource, key: entry.key, attributes: current.attributes)
80
+ raise ConflictError, "record revision conflict" unless current.revision == expected_revision
81
+ unless expected_attributes.all? { |key, value| current.attributes[key] == value } && resource.immutable_attributes.all? { |key| current.attributes[key] == entry.attributes[key] }
82
+ raise ConflictError, "record attribute precondition failed"
83
+ end
84
+ check_unique(state, resource, entry)
85
+ bucket(state, resource)[entry.key] = entry
86
+ Entry::Record.new(**entry.to_h)
87
+ end
88
+
89
+ def delete_record(state, resource, key:, expected_revision:)
90
+ current = bucket(state, resource)[key]
91
+ if !expected_revision.equal?(Records::UNCHECKED) && (!current || current.revision != expected_revision)
92
+ raise ConflictError, "record deletion revision conflict"
93
+ end
94
+ return nil unless current
95
+ guard_record(state, resource, key: key, attributes: current.attributes)
96
+ bucket(state, resource).delete(key)
97
+ nil
98
+ end
99
+
100
+ def scan_records(state, resource, equals:, after:, limit:)
101
+ entries = bucket(state, resource).values.select { |entry| (!after || entry.key.b > after.b) && equals.all? { |key, value| entry.attributes[key] == value } }.sort_by { |entry| entry.key.b }
102
+ entries = entries.first(limit) if limit
103
+ entries.map { |entry| Entry::Record.new(**entry.to_h) }.freeze
104
+ end
105
+
106
+ def delete_matching_records(state, resource, equals:)
107
+ if resource.guard && ![:key, :stream].include?(resource.guard[:via])
108
+ raise ArgumentError, "deletion must constrain its guard attribute" unless equals.key?(resource.guard[:via])
109
+ guard_record(state, resource, key: "unused", attributes: equals)
110
+ end
111
+ scan_records(state, resource, equals: equals, after: nil, limit: nil).each do |entry|
112
+ delete_record(state, resource, key: entry.key, expected_revision: Records::UNCHECKED)
113
+ end
114
+ nil
115
+ end
116
+
117
+ def append_stream(state, resource, stream:, expected_head:, entries:)
118
+ guard_record(state, resource, key: stream)
119
+ stored = bucket(state, resource).fetch(stream, [])
120
+ raise ConflictError, "stream head conflict" unless stored.length == expected_head
121
+ raise ConflictError, "duplicate stream entry identity" unless (stored.map(&:id) & entries.map(&:id)).empty?
122
+ appended = entries.each_with_index.map { |entry, index| Entry::Stream.new(position: expected_head + index + 1, id: entry.id, record: entry.record) }
123
+ bucket(state, resource)[stream] = stored + appended unless appended.empty?
124
+ appended.map { |entry| Entry::Stream.new(**entry.to_h) }.freeze
125
+ end
126
+
127
+ def read_stream(state, resource, stream:, after:, limit:)
128
+ selected = bucket(state, resource).fetch(stream, []).select { |entry| entry.position > after }
129
+ selected = selected.first(limit) if limit
130
+ selected.map { |entry| Entry::Stream.new(**entry.to_h) }.freeze
131
+ end
132
+
133
+ def stream_head(state, resource, stream:) = bucket(state, resource).fetch(stream, []).length
134
+
135
+ def delete_stream(state, resource, stream:)
136
+ guard_record(state, resource, key: stream)
137
+ bucket(state, resource).delete(stream)
138
+ nil
139
+ end
140
+
141
+ def put_blob(state, resource, blob:)
142
+ stored = bucket(state, resource)[blob.key]
143
+ raise BlobConflictError, "blob bytes are immutable" if stored && stored.bytes != blob.bytes
144
+ stored ||= bucket(state, resource)[blob.key] = blob
145
+ Entry::Blob.new(**stored.to_h)
146
+ end
147
+
148
+ def fetch_blob(state, resource, key:)
149
+ stored = bucket(state, resource)[key] || raise(NotFoundError, "blob not found: #{key}")
150
+ Entry::Blob.new(**stored.to_h)
151
+ end
152
+
153
+ def blob_exists(state, resource, key:) = bucket(state, resource).key?(key)
154
+ end
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Storage
5
+ # A neutral storage contract failure.
6
+ # @api public
7
+ class BlobConflictError < ConflictError
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Storage
5
+ # Immutable byte values sharing the same transaction domain as other resources.
6
+ # @api public
7
+ class Blobs
8
+ attr_reader :resource
9
+ def initialize(view, resource) = (@view, @resource = view, resource)
10
+
11
+ # @api public
12
+ def put_if_absent(key:, bytes:, attributes:)
13
+ @view.validate!
14
+ blob = Entry::Blob.new(key: key, bytes: bytes, attributes: resource.validate_attributes(attributes))
15
+ @view.perform(:put_blob, resource, blob: blob)
16
+ end
17
+
18
+ # @api public
19
+ def fetch(key)
20
+ @view.validate!
21
+ @view.perform(:fetch_blob, resource, key: Validation.key(key))
22
+ end
23
+
24
+ # @api public
25
+ def exist?(key)
26
+ @view.validate!
27
+ @view.perform(:blob_exists, resource, key: Validation.key(key))
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Storage
5
+ # A deliberately closed set of conditions; no payload predicates or callbacks.
6
+ # @api public
7
+ module Condition
8
+ RevisionIs = Data.define(:resource, :key, :expected) do
9
+ def initialize(resource:, key:, expected:)
10
+ super(resource: Resource.normalize_reference(resource), key: Validation.key(key), expected: Validation.revision(expected))
11
+ end
12
+ end
13
+ StreamHeadIs = Data.define(:resource, :stream, :expected) do
14
+ def initialize(resource:, stream:, expected:)
15
+ super(resource: Resource.normalize_reference(resource), stream: Validation.key(stream), expected: Validation.revision(expected))
16
+ end
17
+ end
18
+ NoRows = Data.define(:resource, :index, :equals) do
19
+ def initialize(resource:, index:, equals:)
20
+ super(resource: Resource.normalize_reference(resource), index: index, equals: Validation.immutable(equals))
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Storage
5
+ # A neutral storage contract failure.
6
+ # @api public
7
+ class ConditionFailedError < ConflictError
8
+ attr_reader :condition
9
+ # @api public
10
+ def initialize(condition)
11
+ @condition = condition
12
+ super("storage condition failed: #{condition.class.name}")
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Storage
5
+ # A storage identity, revision, or position precondition failed.
6
+ # @api public
7
+ class ConflictError < Phronomy::Error; end
8
+ end
9
+ end