phronomy 0.27.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 (475) 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 +9 -3
  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/architecture/agent-chat-and-state-ownership.md +7 -1
  11. data/docs/architecture/agent-transition-ownership.md +13 -89
  12. data/docs/architecture/backend-groups.md +152 -0
  13. data/docs/architecture/before-llm-input.md +3 -3
  14. data/docs/architecture/context-management.md +1 -1
  15. data/docs/architecture/execution-abstraction-review.md +88 -0
  16. data/docs/architecture/execution-boundaries.md +28 -0
  17. data/docs/architecture/execution-metadata-and-values.md +6 -0
  18. data/docs/architecture/multi-agent-handoff.md +6 -5
  19. data/docs/architecture/persistence.md +27 -9
  20. data/docs/architecture/r8-unit1.md +104 -0
  21. data/docs/architecture/r8-unit10.md +72 -0
  22. data/docs/architecture/r8-unit11.md +71 -0
  23. data/docs/architecture/r8-unit12.md +67 -0
  24. data/docs/architecture/r8-unit13.md +61 -0
  25. data/docs/architecture/r8-unit14.md +72 -0
  26. data/docs/architecture/r8-unit15.md +65 -0
  27. data/docs/architecture/r8-unit16.md +54 -0
  28. data/docs/architecture/r8-unit17.md +73 -0
  29. data/docs/architecture/r8-unit18.md +92 -0
  30. data/docs/architecture/r8-unit19.md +61 -0
  31. data/docs/architecture/r8-unit2.md +55 -0
  32. data/docs/architecture/r8-unit20.md +51 -0
  33. data/docs/architecture/r8-unit21.md +79 -0
  34. data/docs/architecture/r8-unit22.md +74 -0
  35. data/docs/architecture/r8-unit23.md +81 -0
  36. data/docs/architecture/r8-unit3.md +130 -0
  37. data/docs/architecture/r8-unit4.md +79 -0
  38. data/docs/architecture/r8-unit5.md +115 -0
  39. data/docs/architecture/r8-unit6.md +71 -0
  40. data/docs/architecture/r8-unit7.md +70 -0
  41. data/docs/architecture/r8-unit8.md +78 -0
  42. data/docs/architecture/r8-unit9.md +73 -0
  43. data/docs/architecture/refactoring-closure.md +10 -1
  44. data/docs/architecture/remaining-refactoring-plan.md +16 -0
  45. data/docs/architecture/storage-execution-boundaries.md +54 -0
  46. data/docs/architecture/tool-schema-recording-gap.md +6 -0
  47. data/docs/architecture/tracing.md +1 -1
  48. data/docs/architecture/workflow-terminal-ownership-design.md +2 -2
  49. data/docs/architecture.md +24 -11
  50. data/docs/decisions/015-tool-public-facade-and-rbs-boundary.md +3 -3
  51. data/docs/decisions/027-llm-adapter-provider-boundary.md +2 -0
  52. data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +2 -0
  53. data/docs/decisions/031-durable-multi-agent-coordination.md +2 -0
  54. data/docs/decisions/032-storage-backend-composition.md +5 -0
  55. data/docs/decisions/034-handoff-runner-coordination-ownership.md +2 -0
  56. data/docs/decisions/035-tool-executor-capability-ownership.md +1 -1
  57. data/docs/decisions/038-responsibility-based-source-layout.md +14 -0
  58. data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +8 -0
  59. data/docs/decisions/040-configuration-default-composition.md +12 -1
  60. data/docs/decisions/045-worker-input-restriction-ownership.md +4 -0
  61. data/docs/decisions/051-execution-outcome-worker-ownership.md +2 -0
  62. data/docs/decisions/056-workflow-terminal-policy-ownership.md +1 -1
  63. data/docs/decisions/058-neutral-storage-primitives.md +5 -0
  64. data/docs/decisions/059-backend-contracts-and-async-clients.md +74 -0
  65. data/docs/decisions/060-runtime-settings-and-application-configuration.md +86 -0
  66. data/docs/decisions/061-persistence-failure-contracts.md +89 -0
  67. data/docs/decisions/062-content-store-service.md +52 -0
  68. data/docs/decisions/063-execution-contracts-and-services.md +88 -0
  69. data/docs/decisions/064-agent-execution-and-multi-agent-responsibility.md +36 -0
  70. data/docs/decisions/065-vector-and-embeddings-common-operations.md +38 -0
  71. data/docs/decisions/066-llm-and-tool-operation-contracts.md +20 -0
  72. data/docs/decisions/067-agent-progress-and-engine-connection.md +29 -0
  73. data/docs/decisions/068-agent-tool-child-progress-and-tool-operation.md +36 -0
  74. data/docs/decisions/README.md +18 -1
  75. data/docs/execution-boundaries-preview.md +45 -0
  76. data/docs/features.md +8 -3
  77. data/docs/getting-started.md +9 -6
  78. data/docs/migrations/0.22.md +2 -2
  79. data/docs/migrations/durable-semantic-coordination-v2.md +2 -0
  80. data/docs/migrations/handoff-runner-multi-agent.md +2 -0
  81. data/docs/migrations/persistence-failure-contracts.md +67 -0
  82. data/docs/migrations/r8-unit10.md +36 -0
  83. data/docs/migrations/r8-unit11.md +33 -0
  84. data/docs/migrations/r8-unit12.md +31 -0
  85. data/docs/migrations/r8-unit13.md +36 -0
  86. data/docs/migrations/r8-unit14.md +31 -0
  87. data/docs/migrations/r8-unit15.md +31 -0
  88. data/docs/migrations/r8-unit16.md +26 -0
  89. data/docs/migrations/r8-unit17.md +28 -0
  90. data/docs/migrations/r8-unit18.md +29 -0
  91. data/docs/migrations/r8-unit19.md +28 -0
  92. data/docs/migrations/r8-unit20.md +24 -0
  93. data/docs/migrations/r8-unit21.md +28 -0
  94. data/docs/migrations/r8-unit22.md +29 -0
  95. data/docs/migrations/r8-unit23.md +20 -0
  96. data/docs/migrations/r8-unit4.md +67 -0
  97. data/docs/migrations/r8-unit5.md +75 -0
  98. data/docs/migrations/r8-unit6.md +109 -0
  99. data/docs/migrations/r8-unit7.md +31 -0
  100. data/docs/migrations/r8-unit8.md +33 -0
  101. data/docs/migrations/r8-unit9.md +28 -0
  102. data/docs/migrations/storage-async-client.md +103 -0
  103. data/docs/migrations/vector-async-clients.md +110 -0
  104. data/docs/persistence-backends.md +38 -21
  105. data/docs/runtime-and-concurrency.md +26 -1
  106. data/lib/phronomy/agent/admission.rb +78 -0
  107. data/lib/phronomy/agent/{lifecycle_contract/agent_already_exists_error.rb → agent_already_exists_error.rb} +1 -1
  108. data/lib/phronomy/agent/{lifecycle_contract/agent_busy_error.rb → agent_busy_error.rb} +1 -1
  109. data/lib/phronomy/agent/{lifecycle_contract/agent_purged_error.rb → agent_purged_error.rb} +1 -1
  110. data/lib/phronomy/{recovery → agent/api}/execution_rehydration_required_error.rb +1 -1
  111. data/lib/phronomy/agent/async_event_api.rb +37 -32
  112. data/lib/phronomy/agent/base.rb +74 -72
  113. data/lib/phronomy/agent/before_llm_input.rb +64 -0
  114. data/lib/phronomy/agent/composition/run_once.rb +1 -1
  115. data/lib/phronomy/agent/{context_assembly/context_candidate_resolver.rb → context_candidate_resolver.rb} +2 -2
  116. data/lib/phronomy/agent/{context_assembly/context_assembler.rb → context_preparation.rb} +35 -221
  117. data/lib/phronomy/agent/control_request.rb +21 -0
  118. data/lib/phronomy/agent/execution/agent_execution.rb +1 -1
  119. data/lib/phronomy/agent/execution/agent_invocation.rb +27 -53
  120. data/lib/phronomy/agent/execution/approval_resume_commit.rb +1 -1
  121. data/lib/phronomy/agent/execution/dispatch_preparation.rb +17 -127
  122. data/lib/phronomy/agent/execution/exact_execution.rb +44 -23
  123. data/lib/phronomy/agent/execution/execution_cancellation.rb +8 -4
  124. data/lib/phronomy/agent/execution/execution_coordinator.rb +214 -395
  125. data/lib/phronomy/agent/execution/execution_metadata.rb +1 -0
  126. data/lib/phronomy/agent/execution/execution_outcome_committer.rb +70 -126
  127. data/lib/phronomy/agent/execution/execution_registry.rb +6 -2
  128. data/lib/phronomy/agent/execution/execution_session_runner.rb +22 -27
  129. data/lib/phronomy/agent/execution/initial_preparation.rb +18 -116
  130. data/lib/phronomy/agent/execution/{agent_invocation_session_builder.rb → invocation_actions.rb} +54 -194
  131. data/lib/phronomy/agent/execution/invocation_transitions.rb +39 -1
  132. data/lib/phronomy/agent/execution/runtime_record_encoder.rb +7 -7
  133. data/lib/phronomy/agent/execution_change.rb +161 -0
  134. data/lib/phronomy/agent/execution_environment.rb +36 -0
  135. data/lib/phronomy/agent/execution_extension_state.rb +21 -0
  136. data/lib/phronomy/agent/execution_observation.rb +35 -0
  137. data/lib/phronomy/agent/filterable.rb +182 -0
  138. data/lib/phronomy/agent/handoff/{handoff_context.rb → transfer_context.rb} +4 -4
  139. data/lib/phronomy/agent/handoff/{handoff_projection.rb → transfer_projection.rb} +14 -33
  140. data/lib/phronomy/{llm_contract/transport_error.rb → agent/handoff_error.rb} +1 -1
  141. data/lib/phronomy/agent/journal/journal_projection.rb +1 -1
  142. data/lib/phronomy/agent/knowledge_item.rb +13 -0
  143. data/lib/phronomy/agent/lifecycle/agent_root.rb +1 -1
  144. data/lib/phronomy/agent/lifecycle/ownership_registry.rb +18 -23
  145. data/lib/phronomy/agent/persistence/agent_repository.rb +29 -21
  146. data/lib/phronomy/agent/persistence/cancellation_repository.rb +51 -0
  147. data/lib/phronomy/agent/persistence/codec.rb +1 -21
  148. data/lib/phronomy/agent/persistence/execution_repository.rb +57 -41
  149. data/lib/phronomy/agent/persistence/journal_repository.rb +27 -18
  150. data/lib/phronomy/agent/persistence/records.rb +33 -0
  151. data/lib/phronomy/agent/persistence/retention_repository.rb +70 -0
  152. data/lib/phronomy/agent/persistence/storage_schema.rb +7 -2
  153. data/lib/phronomy/agent/persistence/watermark.rb +13 -11
  154. data/lib/phronomy/agent/recovery/invocation_restorer.rb +13 -21
  155. data/lib/phronomy/agent/recovery/recovery_coordinator/continuation.rb +10 -9
  156. data/lib/phronomy/agent/recovery/recovery_coordinator/installation.rb +27 -27
  157. data/lib/phronomy/agent/recovery/recovery_coordinator/resolution.rb +19 -21
  158. data/lib/phronomy/agent/recovery/recovery_coordinator.rb +23 -16
  159. data/lib/phronomy/agent/recovery/recovery_rules.rb +119 -0
  160. data/lib/phronomy/agent/recovery/recovery_support.rb +8 -11
  161. data/lib/phronomy/agent/reserved_execution.rb +16 -0
  162. data/lib/phronomy/agent/retention.rb +17 -0
  163. data/lib/phronomy/agent/runtime_binding/engine_environment.rb +60 -0
  164. data/lib/phronomy/agent/runtime_binding/engine_session_builder.rb +34 -0
  165. data/lib/phronomy/agent/runtime_binding/phase_machine_builder.rb +64 -0
  166. data/lib/phronomy/agent/runtime_binding/tool_session_builder.rb +34 -0
  167. data/lib/phronomy/agent/{context_assembly/ruby_llm_materializer.rb → runtime_input.rb} +18 -35
  168. data/lib/phronomy/agent/{context_assembly/saved_context_reader.rb → saved_context_reader.rb} +6 -4
  169. data/lib/phronomy/agent/settings.rb +47 -0
  170. data/lib/phronomy/agent/{context_assembly/state_writer.rb → state_writer.rb} +4 -3
  171. data/lib/phronomy/agent/store.rb +253 -0
  172. data/lib/phronomy/agent/{lifecycle_contract/stream_callback_error.rb → stream_callback_error.rb} +1 -1
  173. data/lib/phronomy/agent/tool_execution/tool_binding.rb +4 -64
  174. data/lib/phronomy/agent/tool_execution/tool_invocation.rb +50 -219
  175. data/lib/phronomy/agent/tool_execution/tool_invocation_actions.rb +107 -0
  176. data/lib/phronomy/agent/tool_execution/tool_invocation_transitions.rb +70 -0
  177. data/lib/phronomy/configuration/runtime_settings.rb +49 -0
  178. data/lib/phronomy/content_store/storage_schema.rb +1 -0
  179. data/lib/phronomy/content_store/stored_contents.rb +2 -1
  180. data/lib/phronomy/context/assembly.rb +202 -0
  181. data/lib/phronomy/{llm_contract/context_budget_exceeded_error.rb → context/budget_exceeded_error.rb} +3 -1
  182. data/lib/phronomy/context/candidate.rb +51 -0
  183. data/lib/phronomy/context/constraint.rb +47 -0
  184. data/lib/phronomy/{agent/context_contract → context}/context_plan.rb +1 -1
  185. data/lib/phronomy/{agent/context_contract → context}/context_plan_validator.rb +3 -3
  186. data/lib/phronomy/{agent/context_contract → context}/context_policy.rb +1 -1
  187. data/lib/phronomy/{agent/context_contract → context}/context_policy_input.rb +1 -1
  188. data/lib/phronomy/{agent/context_assembly → context}/context_policy_input_builder.rb +1 -1
  189. data/lib/phronomy/context/default_policy.rb +140 -0
  190. data/lib/phronomy/context/final_budget_validator.rb +33 -0
  191. data/lib/phronomy/{agent/context_contract → context}/llm_input_build_context.rb +1 -1
  192. data/lib/phronomy/{agent/context_contract → context}/llm_input_manifest.rb +19 -19
  193. data/lib/phronomy/{agent/context_contract → context}/llm_input_patch.rb +1 -1
  194. data/lib/phronomy/context/manifest_error.rb +10 -0
  195. data/lib/phronomy/context/prompt_template.rb +98 -0
  196. data/lib/phronomy/{vector_store → documents}/loader/base.rb +1 -1
  197. data/lib/phronomy/{vector_store → documents}/loader/csv_loader.rb +2 -2
  198. data/lib/phronomy/{vector_store → documents}/loader/markdown_loader.rb +3 -3
  199. data/lib/phronomy/{vector_store → documents}/loader/plain_text_loader.rb +2 -2
  200. data/lib/phronomy/{vector_store → documents}/splitter/base.rb +1 -1
  201. data/lib/phronomy/{vector_store → documents}/splitter/fixed_size_splitter.rb +2 -2
  202. data/lib/phronomy/{vector_store → documents}/splitter/recursive_splitter.rb +2 -2
  203. data/lib/phronomy/embeddings/async/async_client.rb +44 -0
  204. data/lib/phronomy/embeddings/backends/ruby_llm_embeddings.rb +48 -0
  205. data/lib/phronomy/embeddings/base.rb +43 -0
  206. data/lib/phronomy/embeddings/invalid_result_error.rb +11 -0
  207. data/lib/phronomy/embeddings/transport_error.rb +11 -0
  208. data/lib/phronomy/engine/concurrency/offload_pool.rb +36 -21
  209. data/lib/phronomy/engine/concurrency/pool_registry.rb +47 -12
  210. data/lib/phronomy/engine/diagnostics.rb +1 -1
  211. data/lib/phronomy/engine/event_loop.rb +7 -7
  212. data/lib/phronomy/engine/execution_binding.rb +23 -0
  213. data/lib/phronomy/engine/execution_receiver_binding.rb +43 -0
  214. data/lib/phronomy/engine/fsm_protocol.rb +1 -6
  215. data/lib/phronomy/engine/fsm_session.rb +3 -3
  216. data/lib/phronomy/engine/metrics.rb +16 -14
  217. data/lib/phronomy/engine/runtime/timer_queue.rb +1 -1
  218. data/lib/phronomy/engine/runtime.rb +25 -12
  219. data/lib/phronomy/engine/terminal_decision.rb +7 -0
  220. data/lib/phronomy/execution/async_operation.rb +41 -0
  221. data/lib/phronomy/{engine → execution}/blocking.rb +2 -2
  222. data/lib/phronomy/{engine → execution}/concurrency/cancellation_token.rb +1 -1
  223. data/lib/phronomy/{engine → execution}/concurrency/deadline.rb +0 -17
  224. data/lib/phronomy/{engine → execution}/concurrency/physical_completion_task.rb +1 -1
  225. data/lib/phronomy/{engine → execution}/concurrency/result_collector.rb +1 -1
  226. data/lib/phronomy/{engine → execution}/concurrency/result_composition.rb +33 -12
  227. data/lib/phronomy/{engine/concurrency/subscriptions.rb → execution/concurrency/result_subscriptions.rb} +14 -18
  228. data/lib/phronomy/execution/concurrency/subscriptions.rb +25 -0
  229. data/lib/phronomy/{engine → execution}/execution.rb +61 -27
  230. data/lib/phronomy/execution/execution_receiver.rb +78 -0
  231. data/lib/phronomy/execution/execution_scope_state.rb +35 -0
  232. data/lib/phronomy/{engine → execution}/invocation_context.rb +0 -15
  233. data/lib/phronomy/execution/invocation_controls.rb +73 -0
  234. data/lib/phronomy/execution/runnable.rb +24 -0
  235. data/lib/phronomy/{engine → execution}/task_result.rb +3 -3
  236. data/lib/phronomy/execution/wait_policy.rb +24 -0
  237. data/lib/phronomy/filter/{contract/filter_block_error.rb → filter_block_error.rb} +1 -1
  238. data/lib/phronomy/generation/generator_verifier/agent_operation.rb +65 -0
  239. data/lib/phronomy/generation/generator_verifier/default_parser.rb +27 -0
  240. data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +2 -2
  241. data/lib/phronomy/generation/generator_verifier.rb +7 -7
  242. data/lib/phronomy/llm_adapter/async/async_client.rb +47 -0
  243. data/lib/phronomy/{llm_contract → llm_adapter}/authentication_error.rb +3 -1
  244. data/lib/phronomy/llm_adapter/backends/ruby_llm.rb +183 -0
  245. data/lib/phronomy/llm_adapter/base.rb +49 -68
  246. data/lib/phronomy/{llm_contract → llm_adapter}/context_length_error.rb +3 -1
  247. data/lib/phronomy/llm_adapter/invalid_result_error.rb +10 -0
  248. data/lib/phronomy/llm_adapter/message.rb +62 -0
  249. data/lib/phronomy/{llm_contract → llm_adapter}/rate_limit_error.rb +3 -1
  250. data/lib/phronomy/llm_adapter/request.rb +79 -0
  251. data/lib/phronomy/llm_adapter/response.rb +36 -0
  252. data/lib/phronomy/llm_adapter/stream_chunk.rb +18 -0
  253. data/lib/phronomy/llm_adapter/token_usage.rb +64 -0
  254. data/lib/phronomy/llm_adapter/transport_error.rb +9 -0
  255. data/lib/phronomy/llm_adapter.rb +3 -16
  256. data/lib/phronomy/multi_agent/admission_registry.rb +1 -8
  257. data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +108 -84
  258. data/lib/phronomy/multi_agent/execution_environment.rb +29 -0
  259. data/lib/phronomy/{agent/handoff → multi_agent}/handoff.rb +2 -2
  260. data/lib/phronomy/{agent/handoff → multi_agent}/handoff_capability_factory.rb +21 -2
  261. data/lib/phronomy/multi_agent/handoff_participant.rb +79 -0
  262. data/lib/phronomy/{agent/handoff → multi_agent}/handoff_policy.rb +1 -1
  263. data/lib/phronomy/multi_agent/handoff_runner.rb +77 -100
  264. data/lib/phronomy/{agent/handoff → multi_agent}/handoff_state.rb +1 -1
  265. data/lib/phronomy/multi_agent/orchestrator.rb +61 -75
  266. data/lib/phronomy/multi_agent/persistence/codec.rb +20 -0
  267. data/lib/phronomy/multi_agent/persistence/handoff_state_repository.rb +77 -0
  268. data/lib/phronomy/multi_agent/persistence/records.rb +27 -0
  269. data/lib/phronomy/multi_agent/persistence/storage_schema.rb +21 -0
  270. data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +58 -42
  271. data/lib/phronomy/multi_agent/persistence/team_repository.rb +29 -21
  272. data/lib/phronomy/multi_agent/reserved_child_admission.rb +88 -0
  273. data/lib/phronomy/multi_agent/runtime_binding/engine_environment.rb +35 -0
  274. data/lib/phronomy/multi_agent/shared_state.rb +2 -2
  275. data/lib/phronomy/multi_agent/store.rb +156 -0
  276. data/lib/phronomy/multi_agent/team_coordinator.rb +83 -74
  277. data/lib/phronomy/multi_agent/team_ownership_registry.rb +2 -12
  278. data/lib/phronomy/output_parser/parse_error.rb +7 -0
  279. data/lib/phronomy/persistence/content_repository.rb +26 -0
  280. data/lib/phronomy/persistence/errors.rb +16 -0
  281. data/lib/phronomy/persistence/migration/initial_format_migration.rb +2 -2
  282. data/lib/phronomy/persistence/persistence.rb +23 -0
  283. data/lib/phronomy/persistence/save_outcome.rb +24 -0
  284. data/lib/phronomy/persistence/snapshot_comparison.rb +81 -0
  285. data/lib/phronomy/persistence/storage_boundary.rb +32 -0
  286. data/lib/phronomy/persistence/transaction.rb +86 -0
  287. data/lib/phronomy/persistence_composition/storage_schema.rb +9 -4
  288. data/lib/phronomy/persistence_composition/stores.rb +34 -0
  289. data/lib/phronomy/persistence_composition/unit4_migration.rb +276 -0
  290. data/lib/phronomy/runtime_composition/agent_defaults.rb +5 -1
  291. data/lib/phronomy/runtime_composition/configuration.rb +146 -0
  292. data/lib/phronomy/runtime_composition/evaluation_defaults.rb +7 -0
  293. data/lib/phronomy/runtime_composition/execution_defaults.rb +5 -0
  294. data/lib/phronomy/runtime_composition/generation_defaults.rb +6 -0
  295. data/lib/phronomy/runtime_composition/global_configuration.rb +38 -0
  296. data/lib/phronomy/runtime_composition/multi_agent_defaults.rb +9 -0
  297. data/lib/phronomy/runtime_composition/workflow_defaults.rb +4 -0
  298. data/lib/phronomy/storage/async/async_client.rb +60 -0
  299. data/lib/phronomy/storage/backend.rb +8 -2
  300. data/lib/phronomy/testing/eval/metrics.rb +1 -1
  301. data/lib/phronomy/testing/eval/scorer/llm_judge.rb +19 -3
  302. data/lib/phronomy/testing/persistence_contract/a_content_store.rb +2 -2
  303. data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +6 -6
  304. data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +55 -52
  305. data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +3 -3
  306. data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +7 -7
  307. data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +11 -11
  308. data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +57 -51
  309. data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +1 -1
  310. data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +23 -22
  311. data/lib/phronomy/tool/authorization.rb +77 -0
  312. data/lib/phronomy/tool/base.rb +549 -9
  313. data/lib/phronomy/tool/call_request.rb +40 -0
  314. data/lib/phronomy/{agent/tool_execution/tool_definition_set.rb → tool/definition_set.rb} +7 -8
  315. data/lib/phronomy/tool/operation.rb +88 -0
  316. data/lib/phronomy/tool/schema.rb +214 -0
  317. data/lib/phronomy/tool/settings.rb +31 -0
  318. data/lib/phronomy/tool/tool_error.rb +7 -0
  319. data/lib/phronomy/tool/tool_executor.rb +58 -0
  320. data/lib/phronomy/tools/agent.rb +22 -90
  321. data/lib/phronomy/tools/mcp.rb +21 -14
  322. data/lib/phronomy/tools/vector_search.rb +4 -4
  323. data/lib/phronomy/tracing/automatic.rb +11 -6
  324. data/lib/phronomy/tracing/base.rb +1 -1
  325. data/lib/phronomy/tracing/observation.rb +37 -0
  326. data/lib/phronomy/tracing/settings.rb +36 -0
  327. data/lib/phronomy/vector_store/{async_backend.rb → async/async_client.rb} +35 -29
  328. data/lib/phronomy/vector_store/{in_memory.rb → backends/in_memory.rb} +12 -10
  329. data/lib/phronomy/vector_store/{pgvector.rb → backends/pgvector.rb} +44 -16
  330. data/lib/phronomy/vector_store/{redis_search.rb → backends/redis_search.rb} +74 -37
  331. data/lib/phronomy/vector_store/base.rb +118 -49
  332. data/lib/phronomy/vector_store/invalid_result_error.rb +12 -0
  333. data/lib/phronomy/vector_store.rb +2 -17
  334. data/lib/phronomy/version.rb +1 -1
  335. data/lib/phronomy/workflow/completion.rb +11 -0
  336. data/lib/phronomy/workflow/execution/workflow.rb +7 -7
  337. data/lib/phronomy/workflow/execution/workflow_action_rules.rb +67 -0
  338. data/lib/phronomy/workflow/execution/workflow_context.rb +2 -2
  339. data/lib/phronomy/workflow/execution/workflow_execution_environment.rb +27 -0
  340. data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +3 -5
  341. data/lib/phronomy/workflow/execution/workflow_runner.rb +58 -79
  342. data/lib/phronomy/workflow/execution/workflow_settings.rb +28 -0
  343. data/lib/phronomy/workflow/persistence/state_repository.rb +28 -22
  344. data/lib/phronomy/workflow/persistence/storage_schema.rb +13 -0
  345. data/lib/phronomy/workflow/runtime_binding/workflow_engine_environment.rb +38 -0
  346. data/lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb +156 -0
  347. data/lib/phronomy/workflow/{execution → runtime_binding}/workflow_terminal_policy.rb +4 -4
  348. data/lib/phronomy.rb +57 -16
  349. data/scripts/api_snapshot.rb +35 -15
  350. data/sig/_private/phronomy/agent/settings.rbs +19 -0
  351. data/sig/_private/phronomy/configuration/runtime_settings.rbs +20 -0
  352. data/sig/_private/phronomy/llm_adapter/async/async_client.rbs +10 -0
  353. data/sig/_private/phronomy/multi_agent/store.rbs +8 -0
  354. data/sig/_private/phronomy/persistence/storage_boundary.rbs +7 -0
  355. data/sig/_private/phronomy/tool/settings.rbs +13 -0
  356. data/sig/_private/phronomy/tracing/settings.rbs +15 -0
  357. data/sig/_private/phronomy/workflow/execution/workflow_settings.rbs +13 -0
  358. data/sig/phronomy/agent/admission.rbs +19 -0
  359. data/sig/phronomy/agent/execution_contract.rbs +132 -0
  360. data/sig/phronomy/agent/execution_environment.rbs +20 -0
  361. data/sig/phronomy/agent/store.rbs +73 -0
  362. data/sig/phronomy/agent.rbs +14 -138
  363. data/sig/phronomy/common.rbs +7 -0
  364. data/sig/phronomy/content_store/contracts.rbs +13 -0
  365. data/sig/phronomy/context.rbs +152 -0
  366. data/sig/phronomy/embeddings/async/async_client.rbs +9 -0
  367. data/sig/phronomy/embeddings/base.rbs +17 -0
  368. data/sig/phronomy/engine/concurrency/timer_bindings.rbs +22 -0
  369. data/sig/phronomy/engine/control_bindings.rbs +11 -0
  370. data/sig/phronomy/engine/control_protocols.rbs +23 -0
  371. data/sig/phronomy/engine/execution_binding.rbs +6 -0
  372. data/sig/phronomy/engine/execution_receiver.rbs +15 -0
  373. data/sig/phronomy/engine/observability.rbs +13 -0
  374. data/sig/phronomy/execution/async_operation.rbs +7 -0
  375. data/sig/phronomy/execution/blocking.rbs +5 -0
  376. data/sig/phronomy/execution/concurrency/cancellation_token.rbs +13 -0
  377. data/sig/phronomy/execution/concurrency/deadline.rbs +10 -0
  378. data/sig/phronomy/execution/concurrency/result_composition.rbs +7 -0
  379. data/sig/phronomy/execution/control_protocols.rbs +18 -0
  380. data/sig/phronomy/execution/errors.rbs +7 -0
  381. data/sig/phronomy/execution/execution.rbs +41 -0
  382. data/sig/phronomy/execution/execution_receiver.rbs +40 -0
  383. data/sig/phronomy/execution/execution_scope_state.rbs +9 -0
  384. data/sig/phronomy/execution/invocation_context.rbs +18 -0
  385. data/sig/phronomy/execution/invocation_controls.rbs +13 -0
  386. data/sig/phronomy/execution/task_result.rbs +29 -0
  387. data/sig/phronomy/extensions.rbs +0 -21
  388. data/sig/phronomy/handoff.rbs +10 -4
  389. data/sig/phronomy/llm_adapter/backends/ruby_llm.rbs +6 -0
  390. data/sig/phronomy/llm_adapter/base.rbs +14 -0
  391. data/sig/phronomy/llm_adapter/usage.rbs +14 -0
  392. data/sig/phronomy/llm_adapter/values.rbs +43 -0
  393. data/sig/phronomy/llm_context_window.rbs +10 -0
  394. data/sig/phronomy/multi_agent/reserved_child_admission.rbs +8 -0
  395. data/sig/phronomy/multi_agent/store.rbs +54 -0
  396. data/sig/phronomy/multi_agent.rbs +6 -3
  397. data/sig/phronomy/persistence/errors.rbs +18 -0
  398. data/sig/phronomy/persistence/migration.rbs +13 -0
  399. data/sig/phronomy/persistence/transaction.rbs +19 -0
  400. data/sig/phronomy/persistence.rbs +1 -99
  401. data/sig/phronomy/persistence_composition.rbs +19 -0
  402. data/sig/phronomy/runtime_composition/configuration.rbs +36 -0
  403. data/sig/phronomy/storage/async/async_client.rbs +8 -0
  404. data/sig/phronomy/storage/backends/in_memory.rbs +9 -0
  405. data/sig/phronomy/{storage.rbs → storage/contracts.rbs} +2 -16
  406. data/sig/phronomy/tool.rbs +48 -4
  407. data/sig/phronomy/tracing/observation.rbs +7 -0
  408. data/sig/phronomy/vector_store/async/async_client.rbs +13 -0
  409. data/sig/phronomy/vector_store/base.rbs +23 -0
  410. data/sig/phronomy/workflow/execution/workflow_execution_environment.rbs +14 -0
  411. data/sig/phronomy/workflow/runtime_binding/workflow_terminal_policy.rbs +7 -0
  412. data/sig/phronomy/workflow/store.rbs +8 -0
  413. data/sig/phronomy/workflow.rbs +1 -1
  414. data/sig/phronomy.rbs +0 -49
  415. metadata +318 -117
  416. data/lib/phronomy/agent/concerns/before_llm_input.rb +0 -66
  417. data/lib/phronomy/agent/concerns/error_translation.rb +0 -45
  418. data/lib/phronomy/agent/concerns/filterable.rb +0 -184
  419. data/lib/phronomy/agent/context/capability/base.rb +0 -548
  420. data/lib/phronomy/agent/context/capability/tool_executor.rb +0 -62
  421. data/lib/phronomy/agent/context/instruction/prompt_template.rb +0 -102
  422. data/lib/phronomy/agent/context_assembly/runtime_chat_builder.rb +0 -36
  423. data/lib/phronomy/agent/context_assembly/token_budget_resolver.rb +0 -22
  424. data/lib/phronomy/agent/context_parts/validators/final_budget_validator.rb +0 -37
  425. data/lib/phronomy/agent/context_policies/default.rb +0 -142
  426. data/lib/phronomy/agent/execution/phase_machine_builder.rb +0 -101
  427. data/lib/phronomy/agent/execution/provider_call_outcome.rb +0 -106
  428. data/lib/phronomy/agent/handoff/handoff_execution_coordinator.rb +0 -15
  429. data/lib/phronomy/agent/handoff/handoff_outcome_committer.rb +0 -131
  430. data/lib/phronomy/agent/handoff/handoff_request.rb +0 -45
  431. data/lib/phronomy/agent/lifecycle_contract/handoff_error.rb +0 -7
  432. data/lib/phronomy/agent/persistence/handoff_state_repository.rb +0 -58
  433. data/lib/phronomy/agent/persistence/queries.rb +0 -61
  434. data/lib/phronomy/agent/selection/candidate.rb +0 -53
  435. data/lib/phronomy/agent/selection/constraint.rb +0 -49
  436. data/lib/phronomy/agent/tool_execution/tool_call_intercepted.rb +0 -29
  437. data/lib/phronomy/agent/tool_execution/tool_invocation_session_builder.rb +0 -276
  438. data/lib/phronomy/configuration/configuration.rb +0 -68
  439. data/lib/phronomy/configuration/global_configuration.rb +0 -26
  440. data/lib/phronomy/engine/concurrency/cancellation_scope.rb +0 -124
  441. data/lib/phronomy/engine/concurrency/operation_binding.rb +0 -43
  442. data/lib/phronomy/engine/execution_receiver.rb +0 -65
  443. data/lib/phronomy/engine/runnable.rb +0 -54
  444. data/lib/phronomy/llm_adapter/ruby_llm.rb +0 -47
  445. data/lib/phronomy/llm_contract/token_usage.rb +0 -85
  446. data/lib/phronomy/multi_agent/persistence/queries.rb +0 -30
  447. data/lib/phronomy/multi_agent/storage_contract/team_storage_schema.rb +0 -15
  448. data/lib/phronomy/output_parser/contract/parse_error.rb +0 -7
  449. data/lib/phronomy/persistence/api/persistence.rb +0 -140
  450. data/lib/phronomy/persistence_composition/repositories.rb +0 -77
  451. data/lib/phronomy/recovery/recovery.rb +0 -186
  452. data/lib/phronomy/tool/contract/tool_error.rb +0 -7
  453. data/lib/phronomy/vector_store/embeddings/base.rb +0 -44
  454. data/lib/phronomy/vector_store/embeddings/ruby_llm_embeddings.rb +0 -47
  455. data/lib/phronomy/workflow/phase_machine_builder.rb +0 -205
  456. data/lib/phronomy/workflow/storage_contract/workflow_storage_schema.rb +0 -9
  457. data/sig/phronomy/execution_receiver.rbs +0 -34
  458. data/sig/phronomy/llm_adapter.rbs +0 -11
  459. data/sig/phronomy/runtime.rbs +0 -76
  460. /data/lib/phronomy/agent/{context_assembly/context_importer.rb → context_importer.rb} +0 -0
  461. /data/lib/phronomy/{engine → execution}/backpressure_error.rb +0 -0
  462. /data/lib/phronomy/{engine → execution}/cancellation_error.rb +0 -0
  463. /data/lib/phronomy/{engine → execution}/concurrency/worker_input_restricted.rb +0 -0
  464. /data/lib/phronomy/{engine → execution}/event_loop_reentrancy_error.rb +0 -0
  465. /data/lib/phronomy/{engine → execution}/execution_cancellation_error.rb +0 -0
  466. /data/lib/phronomy/{engine → execution}/execution_timeout_error.rb +0 -0
  467. /data/lib/phronomy/{engine → execution}/invalid_async_entry_action_error.rb +0 -0
  468. /data/lib/phronomy/{engine → execution}/invalid_async_transition_action_error.rb +0 -0
  469. /data/lib/phronomy/{engine → execution}/invalid_async_workflow_action_error.rb +0 -0
  470. /data/lib/phronomy/{engine → execution}/pool_shutdown_error.rb +0 -0
  471. /data/lib/phronomy/{engine → execution}/recursion_limit_error.rb +0 -0
  472. /data/lib/phronomy/{engine → execution}/runtime_shutdown_error.rb +0 -0
  473. /data/lib/phronomy/{engine → execution}/runtime_shutdown_reentrancy_error.rb +0 -0
  474. /data/lib/phronomy/{engine → execution}/scheduler_reentrancy_error.rb +0 -0
  475. /data/lib/phronomy/{engine → execution}/timeout_error.rb +0 -0
@@ -1,58 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phronomy
4
- module Agent
5
- module Persistence
6
- # Owns Handoff state encoding and the initial revision.
7
- # @api private
8
- class HandoffStateRepository
9
- def initialize(view) = @view = view
10
-
11
- def load(main_agent_id)
12
- @view.atomic do |bound|
13
- entry = bound.records(StorageSchema::HANDOFF_STATES).read(main_agent_id.to_s)
14
- entry && decode(entry, main_agent_id)
15
- end
16
- end
17
-
18
- def save(main_agent_id, expected_revision:, state:)
19
- expected = expected_revision.nil? ? nil : Integer(expected_revision)
20
- revision = expected.nil? ? 1 : expected + 1
21
- unless state.main_agent_id == main_agent_id.to_s && state.handoff_revision == revision
22
- raise Phronomy::Storage::SerializationError, "Handoff identity/revision mismatch"
23
- end
24
- record = Codec.encode_handoff_state(state)
25
- @view.atomic do |bound|
26
- records = bound.records(StorageSchema::HANDOFF_STATES)
27
- values = {key: main_agent_id.to_s, attributes: {active_agent_id: state.active_agent_id}, record: record}
28
- entry = if expected.nil?
29
- records.insert(**values, revision: revision)
30
- else
31
- records.replace(**values, expected_revision: expected, next_revision: revision)
32
- end
33
- decode(entry, main_agent_id, revision: revision)
34
- end
35
- rescue Phronomy::Storage::NotFoundError => error
36
- raise Phronomy::Storage::ConflictError, error.message
37
- end
38
-
39
- def delete(main_agent_id, expected_revision:)
40
- @view.records(StorageSchema::HANDOFF_STATES).delete(key: main_agent_id.to_s, expected_revision: Integer(expected_revision))
41
- end
42
-
43
- private
44
-
45
- def decode(entry, main_agent_id, revision: nil)
46
- state = Codec.decode_handoff_state(entry.record)
47
- valid = entry.key == main_agent_id.to_s && state.main_agent_id == entry.key &&
48
- state.handoff_revision == entry.revision && (!revision || revision == entry.revision) &&
49
- entry.attributes == {active_agent_id: state.active_agent_id}
50
- unless valid
51
- raise Phronomy::Storage::SerializationError, "backend returned another Handoff identity/revision"
52
- end
53
- state
54
- end
55
- end
56
- end
57
- end
58
- end
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phronomy
4
- module Agent
5
- module Persistence
6
- # Read and assemble domain results; the public entry point owns the execution-thread guard.
7
- # @api private
8
- class Queries
9
- def initialize(repositories)
10
- @repositories = repositories
11
- end
12
-
13
- def execution_result(execution_id)
14
- execution = @repositories.executions.load(execution_id)
15
- {
16
- execution_id: execution.execution_id, agent_id: execution.agent_id,
17
- status: execution.status, phase: execution.phase,
18
- result_ref: execution.result_ref, error_ref: execution.error_ref,
19
- result: execution.result_ref && @repositories.contents.fetch_text(execution.result_ref),
20
- error: execution.error_ref && @repositories.contents.fetch_json(execution.error_ref)
21
- }.then { |value| Phronomy::Values::Immutable.copy(value) }
22
- end
23
-
24
- def handoff_result(execution_id, main_agent_id: nil)
25
- anchor = main_agent_id&.to_s
26
- seen = {}
27
- current = execution_id.to_s
28
- reserved_agent_id = nil
29
- loop do
30
- raise Phronomy::Storage::SerializationError, "Cyclic durable Handoff chain" if seen[current]
31
- seen[current] = true
32
- begin
33
- execution = @repositories.executions.load(current)
34
- rescue Phronomy::Storage::NotFoundError
35
- routing = @repositories.handoff_states.load(anchor)
36
- if routing && Array(routing.metadata["cancelled_execution_ids"]).include?(current)
37
- return {execution_id: current, agent_id: reserved_agent_id, status: :cancelled, result: nil, error: nil}.freeze
38
- end
39
- if reserved_agent_id && routing && routing.phase != "stable" && routing.pending_target_execution_id == current
40
- return {execution_id: current, agent_id: reserved_agent_id, status: :active,
41
- phase: :target_pending, reserved: true, result: nil, error: nil}.freeze
42
- end
43
- raise
44
- end
45
- anchor ||= execution.metadata.dig("coordination", "main_agent_id")
46
- unless anchor && execution.metadata.dig("coordination", "main_agent_id") == anchor
47
- raise Phronomy::Storage::ConflictError, "Execution does not belong to this Handoff anchor"
48
- end
49
- return @repositories.execution_result(current) unless execution.status == :handed_off
50
- reserved_agent_id = execution.metadata.fetch("handoff_target_agent_id")
51
- current = execution.metadata.fetch("handoff_target_execution_id")
52
- end
53
- end
54
-
55
- def list_executions(agent_id, after: nil, limit: 100)
56
- @repositories.executions.list(agent_id, after: after, limit: limit)
57
- end
58
- end
59
- end
60
- end
61
- end
@@ -1,53 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phronomy
4
- module Agent
5
- module Selection
6
- Candidate = Data.define(
7
- :candidate_id,
8
- :source_kind,
9
- :category,
10
- :role,
11
- :content_ref,
12
- :record_id,
13
- :agent_id,
14
- :execution_id,
15
- :llm_call_id,
16
- :tool_call_id,
17
- :sequence,
18
- :constraint,
19
- :priority,
20
- :metadata
21
- ) do
22
- def initialize(**values)
23
- effective_constraint = values[:constraint] ||
24
- Constraint.selectable(origin: :context_policy)
25
- unless effective_constraint.is_a?(Constraint)
26
- raise ArgumentError,
27
- "Selection::Candidate constraint must be Selection::Constraint"
28
- end
29
-
30
- normalized = values.merge(
31
- candidate_id: values.fetch(:candidate_id).to_s.freeze,
32
- source_kind: values.fetch(:source_kind).to_sym,
33
- category: values.fetch(:category).to_sym,
34
- role: values[:role]&.to_sym,
35
- content_ref: values.fetch(:content_ref).to_s.freeze,
36
- record_id: values[:record_id]&.to_s&.freeze,
37
- agent_id: values[:agent_id]&.to_s&.freeze,
38
- execution_id: values[:execution_id]&.to_s&.freeze,
39
- llm_call_id: values[:llm_call_id]&.to_s&.freeze,
40
- tool_call_id: values[:tool_call_id]&.to_s&.freeze,
41
- sequence: values[:sequence] && Integer(values[:sequence]),
42
- constraint: effective_constraint,
43
- priority: Integer(values[:priority] || 0),
44
- metadata: Phronomy::Values::Immutable.copy(values[:metadata] || {})
45
- )
46
-
47
- super(**normalized)
48
- freeze
49
- end
50
- end
51
- end
52
- end
53
- end
@@ -1,49 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phronomy
4
- module Agent
5
- module Selection
6
- CONSTRAINT_MODES = %i[required selectable forbidden].freeze
7
-
8
- Constraint = Data.define(:mode, :origin, :reason) do
9
- def initialize(mode:, origin:, reason: nil)
10
- normalized_mode = mode.to_sym
11
- unless CONSTRAINT_MODES.include?(normalized_mode)
12
- raise ArgumentError, "unknown Selection constraint mode: #{mode.inspect}"
13
- end
14
-
15
- super(
16
- mode: normalized_mode,
17
- origin: origin.to_sym,
18
- reason: reason&.to_s&.freeze
19
- )
20
- freeze
21
- end
22
-
23
- def required?
24
- mode == :required
25
- end
26
-
27
- def selectable?
28
- mode == :selectable
29
- end
30
-
31
- def forbidden?
32
- mode == :forbidden
33
- end
34
-
35
- def self.required(origin:, reason: nil)
36
- new(mode: :required, origin: origin, reason: reason)
37
- end
38
-
39
- def self.selectable(origin:, reason: nil)
40
- new(mode: :selectable, origin: origin, reason: reason)
41
- end
42
-
43
- def self.forbidden(origin:, reason: nil)
44
- new(mode: :forbidden, origin: origin, reason: reason)
45
- end
46
- end
47
- end
48
- end
49
- end
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phronomy
4
- module Agent
5
- # Normal control-transfer signal raised before RubyLLM executes a Tool Call.
6
- #
7
- # RubyLLM 2.0 guarantees that the complete assistant message has already
8
- # been added to Chat#messages before after_message runs. Keeping that
9
- # message here allows Phronomy to persist the complete Provider outcome even
10
- # though the RubyLLM call itself unwinds through this exception.
11
- # @api private
12
- class ToolCallIntercepted < StandardError
13
- attr_reader :tool_calls, :assistant_message, :assistant_outcome, :llm_call_id
14
-
15
- def initialize(tool_calls, assistant_message: nil, assistant_outcome: nil, llm_call_id: nil)
16
- @tool_calls = Array(tool_calls).freeze
17
- @assistant_message = assistant_message
18
- @assistant_outcome = assistant_outcome || ProviderCallOutcome.capture(assistant_message)
19
- @llm_call_id = llm_call_id&.to_s&.freeze
20
- names = @tool_calls.map(&:name).join(", ")
21
- super("Tool call intercepted: #{names}")
22
- end
23
-
24
- def tool_call
25
- @tool_calls.first
26
- end
27
- end
28
- end
29
- end
@@ -1,276 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "state_machines"
4
-
5
- module Phronomy
6
- module Agent
7
- # Builds an FSMSession for one ToolInvocation.
8
- # Async completion is represented only by explicit FSM events.
9
- class ToolInvocationSessionBuilder
10
- AUTO_STATE_SET = {idle: true, validating: true, queued: true}.freeze
11
-
12
- DECLARED_STATES = %i[
13
- idle validating authorizing awaiting_approval authorized queued running
14
- completed failed rejected cancelled
15
- ].freeze
16
-
17
- WAIT_STATES = %i[awaiting_approval authorized].freeze
18
-
19
- EXTERNAL_EVENTS = {
20
- authorization_completed: [
21
- {from: :authorizing, to: :cancelled, guard: ->(ctx) { ctx.cancelled? }},
22
- {from: :authorizing, to: :failed, guard: ->(ctx) { ctx.failed? }},
23
- {from: :authorizing, to: :rejected, guard: ->(ctx) { ctx.rejected? }},
24
- {from: :authorizing, to: :awaiting_approval, guard: ->(ctx) { ctx.awaiting_approval? }},
25
- {from: :authorizing, to: :authorized, guard: ->(ctx) { ctx.authorized? }}
26
- ],
27
- execution_completed: [
28
- {from: :running, to: :cancelled, guard: ->(ctx) { ctx.cancelled? }},
29
- {from: :running, to: :failed, guard: ->(ctx) { ctx.failed? }},
30
- {from: :running, to: :completed, guard: ->(ctx) { ctx.execution_completed? }}
31
- ],
32
- approve: [{from: :awaiting_approval, to: :authorized, guard: nil}],
33
- reject: [{from: :awaiting_approval, to: :rejected, guard: nil}],
34
- dispatch: [{from: :authorized, to: :queued, guard: nil}],
35
- cancel: [
36
- {from: :awaiting_approval, to: :cancelled, guard: nil},
37
- {from: :authorized, to: :cancelled, guard: nil},
38
- {from: :queued, to: :cancelled, guard: nil},
39
- {from: :running, to: :cancelled, guard: nil}
40
- ]
41
- }.freeze
42
-
43
- def self.build(
44
- tool_invocation:,
45
- parent_event_sink:,
46
- runtime: Phronomy::Runtime.instance
47
- )
48
- build_session(
49
- tool_invocation: tool_invocation,
50
- parent_event_sink: parent_event_sink,
51
- runtime: runtime
52
- )
53
- end
54
-
55
- def self.build_for_resume(
56
- tool_invocation:,
57
- parent_event_sink:,
58
- resume_event:,
59
- resume_phase:,
60
- runtime: Phronomy::Runtime.instance
61
- )
62
- build_session(
63
- tool_invocation: tool_invocation,
64
- parent_event_sink: parent_event_sink,
65
- runtime: runtime,
66
- resume_event: resume_event,
67
- resume_phase: resume_phase
68
- )
69
- end
70
-
71
- def self.build_session(
72
- tool_invocation:,
73
- parent_event_sink:,
74
- runtime:,
75
- resume_event: nil,
76
- resume_phase: nil
77
- )
78
- event_sink = Phronomy::FSMSession::EventSink.new(event_loop: runtime.event_loop)
79
- actions = build_entry_actions(runtime, event_sink, parent_event_sink)
80
- phase_machine = build_phase_machine(actions)
81
-
82
- Phronomy::FSMSession.new(
83
- context: tool_invocation,
84
- event_sink: event_sink,
85
- entry_point: :idle,
86
- phase_machine_class: phase_machine,
87
- entry_actions: {},
88
- auto_state_set: AUTO_STATE_SET,
89
- declared_states: DECLARED_STATES,
90
- wait_state_names: WAIT_STATES,
91
- external_events: EXTERNAL_EVENTS,
92
- recursion_limit: 20,
93
- event_loop: runtime.event_loop,
94
- resume_event: resume_event,
95
- resume_phase: resume_phase
96
- )
97
- end
98
- private_class_method :build_session
99
-
100
- def self.build_entry_actions(runtime, event_sink, parent_event_sink)
101
- {
102
- validating: [method(:validating_action)],
103
- authorizing: [method(:authorizing_action).curry.call(runtime, event_sink)],
104
- awaiting_approval: [method(:awaiting_approval_action).curry.call(parent_event_sink)],
105
- authorized: [method(:authorized_action).curry.call(parent_event_sink)],
106
- queued: [method(:queued_action)],
107
- running: [method(:running_action).curry.call(runtime, event_sink)],
108
- completed: [method(:completed_action).curry.call(parent_event_sink)],
109
- failed: [method(:failed_action).curry.call(parent_event_sink)],
110
- rejected: [method(:rejected_action).curry.call(parent_event_sink)],
111
- cancelled: [method(:cancelled_action).curry.call(parent_event_sink)]
112
- }
113
- end
114
- private_class_method :build_entry_actions
115
-
116
- def self.build_phase_machine(entry_actions)
117
- callbacks = entry_actions
118
- callback_builder = method(:build_entry_callback)
119
-
120
- Class.new do
121
- attr_accessor :context, :current_event
122
-
123
- state_machine :phase, initial: :idle do
124
- state :idle
125
- state :validating
126
- state :authorizing
127
- state :awaiting_approval
128
- state :authorized
129
- state :queued
130
- state :running
131
- state :completed
132
- state :failed
133
- state :rejected
134
- state :cancelled
135
-
136
- event :state_completed do
137
- transition idle: :validating
138
- transition validating: :failed, if: ->(m) { m.context&.failed? }
139
- transition validating: :completed, if: ->(m) { m.context&.validation_completed? }
140
- transition validating: :authorizing, if: ->(m) { m.context&.validation_passed? }
141
- transition queued: :running
142
- end
143
-
144
- event :authorization_completed do
145
- transition authorizing: :cancelled, if: ->(m) { m.context&.cancelled? }
146
- transition authorizing: :failed, if: ->(m) { m.context&.failed? }
147
- transition authorizing: :rejected, if: ->(m) { m.context&.rejected? }
148
- transition authorizing: :awaiting_approval, if: ->(m) { m.context&.awaiting_approval? }
149
- transition authorizing: :authorized, if: ->(m) { m.context&.authorized? }
150
- end
151
-
152
- event :execution_completed do
153
- transition running: :cancelled, if: ->(m) { m.context&.cancelled? }
154
- transition running: :failed, if: ->(m) { m.context&.failed? }
155
- transition running: :completed, if: ->(m) { m.context&.execution_completed? }
156
- end
157
-
158
- event(:approve) { transition awaiting_approval: :authorized }
159
- event(:reject) { transition awaiting_approval: :rejected }
160
- event(:dispatch) { transition authorized: :queued }
161
- event :cancel do
162
- transition awaiting_approval: :cancelled
163
- transition authorized: :cancelled
164
- transition queued: :cancelled
165
- transition running: :cancelled
166
- end
167
-
168
- callbacks.each do |state_name, callables|
169
- callables.each do |callable|
170
- after_transition(
171
- to: state_name,
172
- do: callback_builder.call(callable, state_name)
173
- )
174
- end
175
- end
176
- end
177
- end
178
- end
179
- private_class_method :build_phase_machine
180
-
181
- def self.build_entry_callback(callable, state_name)
182
- ->(machine) {
183
- result = callable.call(machine.context)
184
- if result.is_a?(Phronomy::TaskResult)
185
- raise Phronomy::InvalidAsyncEntryActionError,
186
- "Tool entry action for #{state_name.inspect} returned Phronomy::TaskResult"
187
- end
188
- machine.context = result if result.respond_to?(:set_graph_metadata)
189
- }
190
- end
191
- private_class_method :build_entry_callback
192
-
193
- def self.validating_action(invocation)
194
- invocation.validate!
195
- end
196
- private_class_method :validating_action
197
-
198
- def self.authorizing_action(runtime, event_sink, invocation)
199
- invocation.start_authorization(runtime: runtime) do |outcome|
200
- post_to_session(event_sink, :authorization_completed, outcome)
201
- end
202
- invocation
203
- end
204
- private_class_method :authorizing_action
205
-
206
- def self.awaiting_approval_action(parent_event_sink, invocation)
207
- invocation.mark_awaiting_approval!
208
- notify_parent(parent_event_sink, invocation, :tool_approval_required)
209
- invocation
210
- end
211
- private_class_method :awaiting_approval_action
212
-
213
- def self.authorized_action(parent_event_sink, invocation)
214
- invocation.mark_authorized!
215
- notify_parent(parent_event_sink, invocation, :tool_authorized)
216
- invocation
217
- end
218
- private_class_method :authorized_action
219
-
220
- def self.queued_action(invocation)
221
- invocation.mark_queued!
222
- end
223
- private_class_method :queued_action
224
-
225
- def self.running_action(runtime, event_sink, invocation)
226
- invocation.start_execution(runtime: runtime) do |outcome|
227
- post_to_session(event_sink, :execution_completed, outcome)
228
- end
229
- invocation.mark_running!
230
- invocation
231
- end
232
- private_class_method :running_action
233
-
234
- def self.completed_action(parent_event_sink, invocation)
235
- notify_parent(parent_event_sink, invocation, :tool_completed)
236
- invocation
237
- end
238
- private_class_method :completed_action
239
-
240
- def self.failed_action(parent_event_sink, invocation)
241
- notify_parent(parent_event_sink, invocation, :tool_failed)
242
- invocation
243
- end
244
- private_class_method :failed_action
245
-
246
- def self.rejected_action(parent_event_sink, invocation)
247
- invocation.mark_rejected!
248
- notify_parent(parent_event_sink, invocation, :tool_rejected)
249
- invocation
250
- end
251
- private_class_method :rejected_action
252
-
253
- def self.cancelled_action(parent_event_sink, invocation)
254
- invocation.mark_cancelled!
255
- notify_parent(parent_event_sink, invocation, :tool_cancelled)
256
- invocation
257
- end
258
- private_class_method :cancelled_action
259
-
260
- def self.post_to_session(event_sink, event_type, payload)
261
- return if event_sink.post(event_type, payload)
262
-
263
- Phronomy.configuration.logger&.warn(
264
- "[Phronomy] Dropped #{event_type.inspect} for " \
265
- "FSMSession #{event_sink.fsm_session_id}"
266
- )
267
- end
268
- private_class_method :post_to_session
269
-
270
- def self.notify_parent(parent_event_sink, invocation, event_type)
271
- parent_event_sink.post(event_type, {tool_invocation_id: invocation.id})
272
- end
273
- private_class_method :notify_parent
274
- end
275
- end
276
- end
@@ -1,68 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phronomy
4
- # Holds global configuration for the entire framework.
5
- class Configuration
6
- STREAM_CALLBACK_ERROR_POLICIES = %i[report fail_task].freeze
7
- private_constant :STREAM_CALLBACK_ERROR_POLICIES
8
-
9
- DEFAULT_FACTORIES = {}
10
- private_constant :DEFAULT_FACTORIES
11
-
12
- # Bind fresh-instance factories once during application loading. Concrete
13
- # component selection belongs to runtime_composition, not configuration.
14
- # Applications replace instances through the existing configuration writers.
15
- # @api private
16
- def self.install_default_factories(tracer:, llm_adapter:)
17
- DEFAULT_FACTORIES.replace(tracer: tracer, llm_adapter: llm_adapter).freeze
18
- nil
19
- end
20
-
21
- attr_accessor :default_model
22
- attr_accessor :default_embedding_model
23
- attr_accessor :tracer
24
- attr_accessor :before_llm_input
25
- attr_accessor :recursion_limit
26
- attr_accessor :trace_pii
27
- attr_accessor :logger
28
- attr_accessor :event_loop_stop_grace_seconds
29
- attr_accessor :persistence
30
- attr_accessor :tool_result_max_size
31
- attr_accessor :llm_adapter
32
- attr_accessor :event_loop_starvation_threshold_seconds
33
- attr_accessor :event_loop_dispatch_threshold_seconds
34
- attr_reader :stream_callback_error_policy
35
- attr_accessor :offload_pool_size
36
- attr_accessor :offload_queue_size
37
- attr_accessor :authorization_pool_size
38
- attr_accessor :authorization_queue_size
39
- attr_accessor :authorization_timeout
40
-
41
- def stream_callback_error_policy=(value)
42
- unless STREAM_CALLBACK_ERROR_POLICIES.include?(value)
43
- allowed = STREAM_CALLBACK_ERROR_POLICIES.map(&:inspect).join(", ")
44
- raise Phronomy::ConfigurationError,
45
- "stream_callback_error_policy must be one of: #{allowed}"
46
- end
47
-
48
- @stream_callback_error_policy = value
49
- end
50
-
51
- def initialize
52
- @recursion_limit = 25
53
- @tracer = DEFAULT_FACTORIES.fetch(:tracer).call
54
- @trace_pii = false
55
- @event_loop_stop_grace_seconds = 5
56
- @llm_adapter = DEFAULT_FACTORIES.fetch(:llm_adapter).call
57
- @event_loop_starvation_threshold_seconds = nil
58
- @event_loop_dispatch_threshold_seconds = nil
59
- @stream_callback_error_policy = :report
60
- @offload_pool_size = 10
61
- @offload_queue_size = 100
62
- @authorization_pool_size = 4
63
- @authorization_queue_size = 100
64
- @authorization_timeout = 5
65
- @persistence = nil
66
- end
67
- end
68
- end
@@ -1,26 +0,0 @@
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