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
@@ -3,7 +3,7 @@
3
3
  require "securerandom"
4
4
 
5
5
  RSpec.shared_examples "an Execution repository" do
6
- let(:execution_repository) { persistence.executions }
6
+ let(:execution_repository) { stores.agent.executions }
7
7
  let(:execution_agent_root) do
8
8
  Phronomy::Agent::AgentRoot.create(
9
9
  agent_id: "execution-agent-#{SecureRandom.uuid}",
@@ -27,7 +27,7 @@ RSpec.shared_examples "an Execution repository" do
27
27
  end
28
28
 
29
29
  before do
30
- persistence.agents.create(execution_agent_root)
30
+ stores.agent.agents.create(execution_agent_root)
31
31
  end
32
32
 
33
33
  it "creates and loads an active execution" do
@@ -40,7 +40,7 @@ RSpec.shared_examples "an Execution repository" do
40
40
  it "raises NotFoundError for a missing execution" do
41
41
  expect do
42
42
  execution_repository.load("missing-#{SecureRandom.uuid}")
43
- end.to raise_error(Phronomy::Storage::NotFoundError)
43
+ end.to raise_error(Phronomy::Persistence::NotFoundError)
44
44
  end
45
45
 
46
46
  it "rejects a duplicate execution_id" do
@@ -49,7 +49,7 @@ RSpec.shared_examples "an Execution repository" do
49
49
 
50
50
  expect do
51
51
  execution_repository.create_active(execution)
52
- end.to raise_error(Phronomy::Storage::ConflictError)
52
+ end.to raise_error(Phronomy::Persistence::ConflictError)
53
53
  end
54
54
 
55
55
  it "admits at most one active or suspended execution for one Agent" do
@@ -68,7 +68,7 @@ RSpec.shared_examples "an Execution repository" do
68
68
  agent_definition_id: "contract-agent",
69
69
  agent_definition_version: 1
70
70
  )
71
- persistence.agents.create(other_root)
71
+ stores.agent.agents.create(other_root)
72
72
 
73
73
  expect do
74
74
  execution_repository.create_active(build_contract_execution(execution_agent_root))
@@ -107,7 +107,7 @@ RSpec.shared_examples "an Execution repository" do
107
107
  expected_revision: 0,
108
108
  execution: updated
109
109
  )
110
- end.to raise_error(Phronomy::Storage::ConflictError)
110
+ end.to raise_error(Phronomy::Persistence::ConflictError)
111
111
  end
112
112
 
113
113
  it "rejects an execution identity mismatch" do
@@ -118,7 +118,7 @@ RSpec.shared_examples "an Execution repository" do
118
118
  agent_definition_id: "contract-agent",
119
119
  agent_definition_version: 1
120
120
  )
121
- persistence.agents.create(other_root)
121
+ stores.agent.agents.create(other_root)
122
122
  other = build_contract_execution(other_root).with(
123
123
  execution_revision: 1,
124
124
  status: :active,
@@ -131,7 +131,7 @@ RSpec.shared_examples "an Execution repository" do
131
131
  expected_revision: 0,
132
132
  execution: other
133
133
  )
134
- end.to raise_error(Phronomy::Storage::SerializationError)
134
+ end.to raise_error(Phronomy::Persistence::SerializationError)
135
135
  end
136
136
 
137
137
  it "requires execution revision to advance exactly once" do
@@ -149,7 +149,7 @@ RSpec.shared_examples "an Execution repository" do
149
149
  expected_revision: 0,
150
150
  execution: skipped
151
151
  )
152
- end.to raise_error(Phronomy::Storage::ConflictError)
152
+ end.to raise_error(Phronomy::Persistence::ConflictError)
153
153
  end
154
154
 
155
155
  it "lists active executions for one Agent" do
@@ -179,7 +179,7 @@ RSpec.shared_examples "an Execution repository" do
179
179
 
180
180
  expect do
181
181
  execution_repository.load(execution.execution_id)
182
- end.to raise_error(Phronomy::Storage::NotFoundError)
182
+ end.to raise_error(Phronomy::Persistence::NotFoundError)
183
183
  end
184
184
 
185
185
  it "deletes all executions for one Agent without deleting other Agents' executions" do
@@ -188,7 +188,7 @@ RSpec.shared_examples "an Execution repository" do
188
188
  agent_definition_id: "contract-agent",
189
189
  agent_definition_version: 1
190
190
  )
191
- persistence.agents.create(other_root)
191
+ stores.agent.agents.create(other_root)
192
192
  own = build_contract_execution(execution_agent_root)
193
193
  other = build_contract_execution(other_root)
194
194
  execution_repository.create_active(own)
@@ -19,7 +19,7 @@ RSpec.shared_context "coordination repository values" do
19
19
  end
20
20
  let(:coordination_handoff) do
21
21
  now = Time.now.utc.iso8601(6)
22
- Phronomy::Agent::HandoffState.new(main_agent_id: SecureRandom.uuid, handoff_revision: 1,
22
+ Phronomy::MultiAgent::HandoffState.new(main_agent_id: SecureRandom.uuid, handoff_revision: 1,
23
23
  active_agent_id: "contract-target", active_handoff_context_ref: nil, phase: "target_pending",
24
24
  pending_source_execution_id: "source-execution", pending_target_execution_id: "target-execution",
25
25
  created_at: now, updated_at: now, metadata: {})
@@ -30,27 +30,27 @@ RSpec.shared_examples "a Handoff state repository" do
30
30
  include_context "coordination repository values"
31
31
 
32
32
  it "returns nil only for an authoritative absent Handoff anchor" do
33
- expect(persistence.handoff_states.load(SecureRandom.uuid)).to be_nil
33
+ expect(stores.multi_agent.handoff_states.load(SecureRandom.uuid)).to be_nil
34
34
  end
35
35
 
36
36
  it "round-trips the immutable Handoff snapshot and rejects stale routing CAS" do
37
37
  value = coordination_handoff
38
- persistence.handoff_states.save(value.main_agent_id, expected_revision: nil, state: value)
39
- loaded = persistence.handoff_states.load(value.main_agent_id)
38
+ stores.multi_agent.handoff_states.save(value.main_agent_id, expected_revision: nil, state: value)
39
+ loaded = stores.multi_agent.handoff_states.load(value.main_agent_id)
40
40
  expect(loaded.to_h).to eq(value.to_h)
41
41
  expect(loaded).to be_frozen
42
42
  updated = value.with(phase: "stable")
43
- persistence.handoff_states.save(value.main_agent_id, expected_revision: 1, state: updated)
43
+ stores.multi_agent.handoff_states.save(value.main_agent_id, expected_revision: 1, state: updated)
44
44
  expect do
45
- persistence.handoff_states.save(value.main_agent_id, expected_revision: 1, state: updated)
46
- end.to raise_error(Phronomy::Storage::ConflictError)
47
- expect(persistence.handoff_states.load(value.main_agent_id).handoff_revision).to eq(2)
45
+ stores.multi_agent.handoff_states.save(value.main_agent_id, expected_revision: 1, state: updated)
46
+ end.to raise_error(Phronomy::Persistence::ConflictError)
47
+ expect(stores.multi_agent.handoff_states.load(value.main_agent_id).handoff_revision).to eq(2)
48
48
  end
49
49
 
50
50
  it "rejects a snapshot for a different routing anchor" do
51
51
  expect do
52
- persistence.handoff_states.save("other", expected_revision: nil, state: coordination_handoff)
53
- end.to raise_error(Phronomy::Storage::SerializationError)
52
+ stores.multi_agent.handoff_states.save("other", expected_revision: nil, state: coordination_handoff)
53
+ end.to raise_error(Phronomy::Persistence::SerializationError)
54
54
  end
55
55
  end
56
56
 
@@ -58,53 +58,53 @@ RSpec.shared_examples "a Team repository" do
58
58
  include_context "coordination repository values"
59
59
 
60
60
  it "round-trips a Team lineage and rejects duplicate creation" do
61
- persistence.teams.create(coordination_team)
62
- expect(persistence.teams.load(coordination_team.team_id).to_h).to eq(coordination_team.to_h)
63
- expect { persistence.teams.create(coordination_team) }.to raise_error(Phronomy::Storage::ConflictError)
61
+ stores.multi_agent.teams.create(coordination_team)
62
+ expect(stores.multi_agent.teams.load(coordination_team.team_id).to_h).to eq(coordination_team.to_h)
63
+ expect { stores.multi_agent.teams.create(coordination_team) }.to raise_error(Phronomy::Persistence::ConflictError)
64
64
  end
65
65
 
66
66
  it "rejects a stale Team revision" do
67
- persistence.teams.create(coordination_team)
67
+ stores.multi_agent.teams.create(coordination_team)
68
68
  updated = coordination_team.with(lifecycle_status: "active")
69
- persistence.teams.save(coordination_team.team_id, expected_revision: 0, root: updated)
69
+ stores.multi_agent.teams.save(coordination_team.team_id, expected_revision: 0, root: updated)
70
70
  expect do
71
- persistence.teams.save(coordination_team.team_id, expected_revision: 0, root: updated)
72
- end.to raise_error(Phronomy::Storage::ConflictError)
71
+ stores.multi_agent.teams.save(coordination_team.team_id, expected_revision: 0, root: updated)
72
+ end.to raise_error(Phronomy::Persistence::ConflictError)
73
73
  end
74
74
 
75
75
  it "raises NotFoundError for an absent Team" do
76
- expect { persistence.teams.load(SecureRandom.uuid) }.to raise_error(Phronomy::Storage::NotFoundError)
76
+ expect { stores.multi_agent.teams.load(SecureRandom.uuid) }.to raise_error(Phronomy::Persistence::NotFoundError)
77
77
  end
78
78
  end
79
79
 
80
80
  RSpec.shared_examples "a Team execution repository" do
81
81
  include_context "coordination repository values"
82
- before { persistence.teams.create(coordination_team) }
82
+ before { stores.multi_agent.teams.create(coordination_team) }
83
83
 
84
84
  it "atomically admits at most one active run per Team" do
85
- persistence.team_executions.create_active(coordination_run)
85
+ stores.multi_agent.team_executions.create_active(coordination_run)
86
86
  other = coordination_run.with(team_execution_id: SecureRandom.uuid, execution_revision: 0)
87
- expect { persistence.team_executions.create_active(other) }.to raise_error(Phronomy::AgentBusyError)
88
- expect(persistence.team_executions.list_active(coordination_team.team_id).map(&:team_execution_id)).to eq([coordination_run.team_execution_id])
87
+ expect { stores.multi_agent.team_executions.create_active(other) }.to raise_error(Phronomy::MultiAgent::TeamBusyError)
88
+ expect(stores.multi_agent.team_executions.list_active(coordination_team.team_id).map(&:team_execution_id)).to eq([coordination_run.team_execution_id])
89
89
  end
90
90
 
91
91
  it "releases admission after terminal CAS while retaining discoverable outcomes" do
92
- persistence.team_executions.create_active(coordination_run)
92
+ stores.multi_agent.team_executions.create_active(coordination_run)
93
93
  completed = coordination_run.with(status: "completed", phase: "completed")
94
- persistence.team_executions.save(coordination_run.team_execution_id, expected_revision: 0, execution: completed)
94
+ stores.multi_agent.team_executions.save(coordination_run.team_execution_id, expected_revision: 0, execution: completed)
95
95
  other = coordination_run.with(team_execution_id: SecureRandom.uuid, execution_revision: 0)
96
- persistence.team_executions.create_active(other)
96
+ stores.multi_agent.team_executions.create_active(other)
97
97
  ids = [completed.team_execution_id, other.team_execution_id].sort
98
- expect(persistence.team_executions.list(coordination_team.team_id, limit: 1).map(&:team_execution_id)).to eq(ids.first(1))
99
- expect(persistence.team_executions.list(coordination_team.team_id, after: ids.first).map(&:team_execution_id)).to eq(ids.last(1))
100
- expect(persistence.team_executions.list("other-team")).to be_empty
98
+ expect(stores.multi_agent.team_executions.list(coordination_team.team_id, limit: 1).map(&:team_execution_id)).to eq(ids.first(1))
99
+ expect(stores.multi_agent.team_executions.list(coordination_team.team_id, after: ids.first).map(&:team_execution_id)).to eq(ids.last(1))
100
+ expect(stores.multi_agent.team_executions.list("other-team")).to be_empty
101
101
  expect do
102
- persistence.team_executions.save(completed.team_execution_id, expected_revision: 0, execution: completed)
103
- end.to raise_error(Phronomy::Storage::ConflictError)
102
+ stores.multi_agent.team_executions.save(completed.team_execution_id, expected_revision: 0, execution: completed)
103
+ end.to raise_error(Phronomy::Persistence::ConflictError)
104
104
  expect do
105
- persistence.team_executions.save(completed.team_execution_id, expected_revision: 1,
105
+ stores.multi_agent.team_executions.save(completed.team_execution_id, expected_revision: 1,
106
106
  execution: completed.with(status: "active", phase: "coordinator"))
107
- end.to raise_error(Phronomy::Storage::ConflictError)
107
+ end.to raise_error(Phronomy::Persistence::ConflictError)
108
108
  end
109
109
 
110
110
  it "rolls the new three repositories back with the existing transaction domain" do
@@ -112,26 +112,32 @@ RSpec.shared_examples "a Team execution repository" do
112
112
  root_id = "coordination-rollback-agent-#{SecureRandom.uuid}"
113
113
  content_ref = nil
114
114
  expect do
115
- persistence.transaction do |tx|
116
- tx.team_executions.create_active(coordination_run)
117
- tx.teams.save(coordination_team.team_id, expected_revision: 0, root: coordination_team.with(lifecycle_status: "active"))
118
- tx.handoff_states.save(handoff.main_agent_id, expected_revision: nil, state: handoff)
119
- content_ref = tx.contents.put_text("coordination-rollback-#{SecureRandom.uuid}")
120
- root = Phronomy::Agent::AgentRoot.create(agent_id: root_id, agent_definition_id: "rollback", agent_definition_version: 1)
121
- tx.agents.create(root)
122
- tx.journals.append(root_id, expected_position: 0, records: [])
123
- record = Phronomy::Agent::JournalRecord.new(agent_id: root_id, kind: :input_received, channel: :external)
124
- tx.executions.create_active(Phronomy::Agent::AgentExecution.start(agent_root: root, input_record: record))
125
- tx.workflow_states.save(root_id, expected_revision: nil, snapshot: {fields: {value: "committed"}, phase: "pause"})
126
- raise "rollback all eight"
115
+ stores.coordinator.atomic do |scope|
116
+ stores.multi_agent.participate(scope) do |team|
117
+ stores.agent.participate(scope) do |agent|
118
+ scope.participate(persistence: stores.coordinator, adapter: Phronomy::Workflow::Persistence::StateRepository) do |workflow|
119
+ team.team_executions.create_active(coordination_run)
120
+ team.teams.save(coordination_team.team_id, expected_revision: 0, root: coordination_team.with(lifecycle_status: "active"))
121
+ team.handoff_states.save(handoff.main_agent_id, expected_revision: nil, state: handoff)
122
+ content_ref = agent.contents.put_text("coordination-rollback-#{SecureRandom.uuid}")
123
+ root = Phronomy::Agent::AgentRoot.create(agent_id: root_id, agent_definition_id: "rollback", agent_definition_version: 1)
124
+ agent.agents.create(root)
125
+ agent.journals.append(root_id, expected_position: 0, records: [])
126
+ record = Phronomy::Agent::JournalRecord.new(agent_id: root_id, kind: :input_received, channel: :external)
127
+ agent.executions.create_active(Phronomy::Agent::AgentExecution.start(agent_root: root, input_record: record))
128
+ workflow.save(root_id, expected_revision: nil, snapshot: {fields: {value: "committed"}, phase: "pause"})
129
+ raise "rollback all eight"
130
+ end
131
+ end
132
+ end
127
133
  end
128
134
  end.to raise_error(RuntimeError, "rollback all eight")
129
- expect(persistence.teams.load(coordination_team.team_id).team_revision).to eq(0)
130
- expect(persistence.handoff_states.load(handoff.main_agent_id)).to be_nil
131
- expect(persistence.team_executions.list(coordination_team.team_id)).to be_empty
132
- expect { persistence.agents.load(root_id) }.to raise_error(Phronomy::Storage::NotFoundError)
133
- expect(persistence.executions.list(root_id)).to be_empty
134
- expect(persistence.workflow_states.load(root_id)).to be_nil
135
- expect(persistence.contents.exist?(content_ref)).to be(false)
135
+ expect(stores.multi_agent.teams.load(coordination_team.team_id).team_revision).to eq(0)
136
+ expect(stores.multi_agent.handoff_states.load(handoff.main_agent_id)).to be_nil
137
+ expect(stores.multi_agent.team_executions.list(coordination_team.team_id)).to be_empty
138
+ expect { stores.agent.agents.load(root_id) }.to raise_error(Phronomy::Persistence::NotFoundError)
139
+ expect(stores.agent.executions.list(root_id)).to be_empty
140
+ expect(stores.workflow.load(root_id)).to be_nil
141
+ expect(stores.agent.contents.exist?(content_ref)).to be(false)
136
142
  end
137
143
  end
@@ -4,7 +4,7 @@ require "securerandom"
4
4
 
5
5
  # This same opaque-record suite runs against InMemory, SQLite and PostgreSQL.
6
6
  RSpec.shared_examples "neutral storage primitives" do
7
- let(:raw_backend) { persistence.backend }
7
+ let(:raw_backend) { stores.coordinator.backend }
8
8
  let(:raw_view) { raw_backend.view }
9
9
  let(:raw_schema) { Phronomy::Agent::Persistence::StorageSchema }
10
10
  let(:raw_record) { Phronomy::Storage::DurableRecord.new(record_type: "unrelated.envelope", format_version: "0.1", payload: {"status" => "ignored"}) }
@@ -9,7 +9,7 @@ RSpec.shared_examples "storage transaction boundaries" do
9
9
  view.is_a?(Phronomy::Storage::View) ? Phronomy::ContentStore::StoredContents.new(view) : view.contents
10
10
  end
11
11
 
12
- let(:transaction_service) { (entry_point == :backend) ? persistence.backend : persistence }
12
+ let(:transaction_service) { (entry_point == :backend) ? stores.coordinator.backend : stores.agent }
13
13
 
14
14
  it "rolls back a failed inner scope and re-raises the same error before the outer scope continues" do
15
15
  outer_id = inner_id = later_id = nil
@@ -19,39 +19,40 @@ RSpec.shared_examples "storage transaction boundaries" do
19
19
  result = transaction_service.transaction do |outer|
20
20
  outer_id = transaction_contents(outer).put_text("outer-#{workflow_id}")
21
21
  expect do
22
- transaction_service.transaction do |inner|
22
+ transaction_service.transaction do |inner, scope|
23
23
  inner_id = transaction_contents(inner).put_text("inner-#{workflow_id}")
24
24
  if entry_point == :backend
25
- inner.records(Phronomy::WorkflowStorageSchema::STATES).insert(key: workflow_id,
25
+ inner.records(Phronomy::Workflow::Persistence::StorageSchema::STATES).insert(key: workflow_id,
26
26
  revision: 1, attributes: {}, record: Phronomy::Storage::DurableRecord.new(
27
27
  record_type: "opaque.transaction", format_version: "0.1", payload: {"value" => "inner"}
28
28
  ))
29
29
  else
30
- inner.workflow_states.save(workflow_id,
31
- expected_revision: nil, snapshot: {fields: {value: "inner"}, phase: "pause"})
30
+ scope.participate(persistence: stores.coordinator, adapter: Phronomy::Workflow::Persistence::StateRepository) do |workflow|
31
+ workflow.save(workflow_id, expected_revision: nil, snapshot: {fields: {value: "inner"}, phase: "pause"})
32
+ end
32
33
  end
33
34
  raise failure
34
35
  end
35
36
  end.to raise_error { |error| expect(error).to equal(failure) }
36
37
  expect(transaction_contents(outer).exist?(outer_id)).to be(true)
37
38
  expect(transaction_contents(outer).exist?(inner_id)).to be(false)
38
- expect(persistence.backend.view.records(Phronomy::WorkflowStorageSchema::STATES).read(workflow_id)).to be_nil
39
+ expect(stores.coordinator.backend.view.records(Phronomy::Workflow::Persistence::StorageSchema::STATES).read(workflow_id)).to be_nil
39
40
  later_id = transaction_contents(outer).put_text("later-#{workflow_id}")
40
41
  :outer_result
41
42
  end
42
43
 
43
44
  expect(result).to eq(:outer_result)
44
- expect(persistence.contents.exist?(outer_id)).to be(true)
45
- expect(persistence.contents.exist?(later_id)).to be(true)
46
- expect(persistence.contents.exist?(inner_id)).to be(false)
47
- expect(persistence.backend.view.records(Phronomy::WorkflowStorageSchema::STATES).read(workflow_id)).to be_nil
45
+ expect(stores.agent.contents.exist?(outer_id)).to be(true)
46
+ expect(stores.agent.contents.exist?(later_id)).to be(true)
47
+ expect(stores.agent.contents.exist?(inner_id)).to be(false)
48
+ expect(stores.coordinator.backend.view.records(Phronomy::Workflow::Persistence::StorageSchema::STATES).read(workflow_id)).to be_nil
48
49
  end
49
50
 
50
51
  it "commits both scopes on normal completion and returns their block values" do
51
52
  outer_id = inner_id = nil
52
53
  result = transaction_service.transaction do |outer|
53
54
  outer_id = transaction_contents(outer).put_text("outer-#{SecureRandom.uuid}")
54
- inner_result = transaction_service.transaction do |inner|
55
+ inner_result = transaction_service.transaction do |inner, scope|
55
56
  inner_id = transaction_contents(inner).put_text("inner-#{SecureRandom.uuid}")
56
57
  :inner_result
57
58
  end
@@ -59,8 +60,8 @@ RSpec.shared_examples "storage transaction boundaries" do
59
60
  :outer_result
60
61
  end
61
62
  expect(result).to eq(:outer_result)
62
- expect(persistence.contents.exist?(outer_id)).to be(true)
63
- expect(persistence.contents.exist?(inner_id)).to be(true)
63
+ expect(stores.agent.contents.exist?(outer_id)).to be(true)
64
+ expect(stores.agent.contents.exist?(inner_id)).to be(true)
64
65
  end
65
66
 
66
67
  it "rolls back successful inner writes when the outer scope fails" do
@@ -68,7 +69,7 @@ RSpec.shared_examples "storage transaction boundaries" do
68
69
  expect do
69
70
  transaction_service.transaction do |outer|
70
71
  outer_id = transaction_contents(outer).put_text("outer-#{SecureRandom.uuid}")
71
- result = transaction_service.transaction do |inner|
72
+ result = transaction_service.transaction do |inner, scope|
72
73
  inner_id = transaction_contents(inner).put_text("inner-#{SecureRandom.uuid}")
73
74
  :inner_result
74
75
  end
@@ -77,8 +78,8 @@ RSpec.shared_examples "storage transaction boundaries" do
77
78
  raise "outer failure"
78
79
  end
79
80
  end.to raise_error("outer failure")
80
- expect(persistence.contents.exist?(outer_id)).to be(false)
81
- expect(persistence.contents.exist?(inner_id)).to be(false)
81
+ expect(stores.agent.contents.exist?(outer_id)).to be(false)
82
+ expect(stores.agent.contents.exist?(inner_id)).to be(false)
82
83
  end
83
84
 
84
85
  it "rolls back both scopes when an inner error escapes the outer scope" do
@@ -86,20 +87,20 @@ RSpec.shared_examples "storage transaction boundaries" do
86
87
  expect do
87
88
  transaction_service.transaction do |outer|
88
89
  outer_id = transaction_contents(outer).put_text("outer-#{SecureRandom.uuid}")
89
- transaction_service.transaction do |inner|
90
+ transaction_service.transaction do |inner, scope|
90
91
  inner_id = transaction_contents(inner).put_text("inner-#{SecureRandom.uuid}")
91
92
  raise "uncaught inner failure"
92
93
  end
93
94
  end
94
95
  end.to raise_error("uncaught inner failure")
95
- expect(persistence.contents.exist?(outer_id)).to be(false)
96
- expect(persistence.contents.exist?(inner_id)).to be(false)
96
+ expect(stores.agent.contents.exist?(outer_id)).to be(false)
97
+ expect(stores.agent.contents.exist?(inner_id)).to be(false)
97
98
  end
98
99
  end
99
100
  end
100
101
 
101
102
  it "validates every stream entry before writing any part of a batch" do
102
- backend = persistence.backend
103
+ backend = stores.coordinator.backend
103
104
  schema = Phronomy::Agent::Persistence::StorageSchema
104
105
  owner_id = "batch-#{SecureRandom.uuid}"
105
106
  record = Phronomy::Storage::DurableRecord.new(record_type: "opaque.transaction", format_version: "0.1", payload: {})
@@ -107,14 +108,14 @@ RSpec.shared_examples "storage transaction boundaries" do
107
108
  valid = Phronomy::Storage::Entry::Append.new(id: "first", record: record)
108
109
  content_id = nil
109
110
  backend.transaction do |view|
110
- content_id = persistence.contents.put_text("retained-#{owner_id}")
111
+ content_id = stores.agent.contents.put_text("retained-#{owner_id}")
111
112
  stream = view.streams(schema::JOURNAL)
112
113
  expect { stream.append(stream: owner_id, expected_head: 0, entries: [valid, Object.new]) }
113
114
  .to raise_error(Phronomy::Storage::SerializationError)
114
115
  expect(stream.head(stream: owner_id)).to eq(0)
115
116
  expect(stream.read(stream: owner_id)).to be_empty
116
117
  end
117
- expect(persistence.contents.exist?(content_id)).to be(true)
118
+ expect(stores.agent.contents.exist?(content_id)).to be(true)
118
119
  entries = backend.view.streams(schema::JOURNAL).append(stream: owner_id, expected_head: 0,
119
120
  entries: [valid, Phronomy::Storage::Entry::Append.new(id: "second", record: record)])
120
121
  expect(entries.map(&:position)).to eq([1, 2])
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Tool
5
+ # Common authorization framework. Requests implement the Tool request protocol;
6
+ # callers attach their own immutable identity context, never a live owner.
7
+ module Authorization
8
+ VALID_DECISIONS = %i[allow require_approval reject].freeze
9
+ Decision = Data.define(:decision, :facts, :reason)
10
+
11
+ def self.call(request:, arguments:, context:, facts: nil, requirement: false, policy: nil)
12
+ value = facts&.call(arguments, context)
13
+ unless value.nil? || value.is_a?(Hash)
14
+ raise Phronomy::ConfigurationError, "approval_facts must return a Hash or nil (got #{value.class})"
15
+ end
16
+ value = snapshot(value || {})
17
+ request = request.with(facts: value)
18
+ required = requirement.respond_to?(:call) ? requirement.call(request) : requirement
19
+ default = case required
20
+ when true then :require_approval
21
+ when false, nil then :allow
22
+ else
23
+ raise Phronomy::ConfigurationError,
24
+ "requires_approval callable must return true or false (got #{required.inspect})"
25
+ end
26
+ request = request.with(default_decision: default)
27
+ decision = policy ? policy.call(request) : default
28
+ decision = decision.to_sym if decision.respond_to?(:to_sym)
29
+ unless VALID_DECISIONS.include?(decision)
30
+ raise Phronomy::ConfigurationError,
31
+ "tool_approval_policy must return :allow, :require_approval, or :reject (got #{decision.inspect})"
32
+ end
33
+ reason = if decision == :require_approval
34
+ (request.origin == :mcp) ? "MCP Tool execution requires approval" : "Tool execution requires approval"
35
+ end
36
+ Decision.new(decision: decision, facts: value, reason: reason)
37
+ end
38
+
39
+ def self.snapshot(value)
40
+ if phronomy_managed_live_domain_object?(value)
41
+ raise Phronomy::ConfigurationError,
42
+ "authorization worker snapshot cannot contain Phronomy-managed live " \
43
+ "domain object #{value.class}"
44
+ end
45
+
46
+ case value
47
+ when Hash
48
+ value.each_with_object({}) do |(key, item), result|
49
+ result[snapshot(key)] = snapshot(item)
50
+ end.freeze
51
+ when Array
52
+ value.map { |item| snapshot(item) }.freeze
53
+ when String
54
+ value.dup.freeze
55
+ else
56
+ # Application-defined opaque objects are permitted by ACS-11 and remain
57
+ # Application-owned. A stricter general value-type protocol is deferred.
58
+ value
59
+ end
60
+ end
61
+
62
+ def self.phronomy_managed_live_domain_object?(value)
63
+ value.is_a?(Phronomy::Concurrency::WorkerInputRestricted)
64
+ end
65
+ private_class_method :phronomy_managed_live_domain_object?
66
+
67
+ def self.behavior(value, name)
68
+ return value if value.nil? || value == true || value == false
69
+ if phronomy_managed_live_domain_object?(value)
70
+ raise Phronomy::ConfigurationError,
71
+ "#{name} must not be a Phronomy-managed live domain object (got #{value.class})"
72
+ end
73
+ value
74
+ end
75
+ end
76
+ end
77
+ end