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
@@ -27,6 +27,7 @@ module Phronomy
27
27
  "arguments" => serializable_value(child.raw_arguments || {}),
28
28
  "status" => child.status.to_s
29
29
  }
30
+ entry["validated_arguments"] = serializable_value(child.arguments) if child.arguments
30
31
  if child.execution_completed?
31
32
  entry["result"] = serializable_value(child.result)
32
33
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../../engine/concurrency/worker_input_restricted"
3
+ require_relative "../../execution/concurrency/worker_input_restricted"
4
4
 
5
5
  module Phronomy
6
6
  module Agent
@@ -10,10 +10,6 @@ module Phronomy
10
10
  class ExecutionOutcomeCommitter
11
11
  include Phronomy::Concurrency::WorkerInputRestricted
12
12
 
13
- HandoffTerminalView = Data.define(
14
- :target_agent_id, :responsibility, :selection_intent,
15
- :llm_call_id, :tool_call_id, :policy
16
- )
17
13
  TerminalView = Data.define(
18
14
  :phase, :output, :usage, :approval_request, :rejected,
19
15
  :input_blocked, :output_blocked, :block_error,
@@ -22,7 +18,7 @@ module Phronomy
22
18
  Command = Data.define(
23
19
  :execution_id, :fsm_session_id, :expected_execution_revision,
24
20
  :root, :journal_records, :execution, :runtime_snapshot,
25
- :terminal_view, :state_required
21
+ :terminal_view, :state_required, :wiring
26
22
  )
27
23
  Outcome = Data.define(
28
24
  :type, :execution, :root, :appended_records,
@@ -40,121 +36,88 @@ module Phronomy
40
36
  view = operation.terminal_view
41
37
  error = view.callback_failure&.to_stream_callback_error || view.source_error ||
42
38
  view.block_error || view.invocation_error
43
- if operation.execution.metadata["multi_agent_coordination_ref"] &&
44
- (error.is_a?(Phronomy::ExecutionRehydrationRequiredError) || view.cancel_requested)
45
- waiting = commit_coordination_wait(operation, error)
46
- return waiting if waiting
39
+ change = ExecutionChange.new(agent: @agent, current: operation.execution,
40
+ root: operation.root, kind: :terminal, terminal: view, config: operation.wiring) do |tx, state, receipt, pending|
41
+ if pending
42
+ waiting = operation.execution.with(metadata: extension_metadata(operation.execution, state).merge(
43
+ "cancellation_requested" => change.cancel_requested || operation.execution.metadata["cancellation_requested"] == true
44
+ ))
45
+ save_execution(tx, operation.execution, waiting)
46
+ Outcome.new(type: :coordination_wait, execution: waiting, root: operation.root,
47
+ appended_records: [].freeze, result: nil,
48
+ error: error || Phronomy::ExecutionRehydrationRequiredError.new("Execution retains unfinished participant work"), approval_request: nil)
49
+ else
50
+ raise error if error.is_a?(Phronomy::ExecutionRehydrationRequiredError)
51
+ commit_terminal(tx, operation, state, receipt, error)
52
+ end
47
53
  end
48
- raise error if error.is_a?(Phronomy::ExecutionRehydrationRequiredError)
49
- return commit_failed_outcome(operation, error) if error
50
- return commit_suspended(operation) if view.phase == :suspended
51
- commit_completed(operation)
52
- rescue => caught
53
- reconcile_terminal_error(operation, caught)
54
+ change.perform
54
55
  end
55
56
 
56
57
  private
57
58
 
58
- # Reuse the Agent barrier to retain unfinished owned children. This is an
59
- # active AgentExecution snapshot, not a second scheduler or terminal state.
60
- def commit_coordination_wait(operation, error)
61
- current = operation.execution
62
- waiting = nil
63
- @persistence.transaction do |tx|
64
- refreshed = @agent.__prepare_coordination_record(current, tx: tx)
65
- snapshot = tx.contents.fetch_json(refreshed.metadata.fetch("multi_agent_coordination_ref"))
66
- unresolved = snapshot.fetch("children").any? do |child|
67
- !%w[completed failed cancelled rejected blocked handed_off].include?(child.fetch("state")) &&
68
- !(operation.terminal_view.cancel_requested && child.fetch("state") == "reserved")
69
- end
70
- next unless unresolved
71
- waiting = current.with(metadata: refreshed.metadata.merge(
72
- "coordination_cancel_requested" => operation.terminal_view.cancel_requested || current.metadata["coordination_cancel_requested"] == true
73
- ))
74
- tx.executions.save(current.execution_id, expected_revision: current.execution_revision, execution: waiting)
59
+ def commit_terminal(tx, operation, state, receipt, error)
60
+ view = operation.terminal_view
61
+ if !error && view.phase == :suspended
62
+ suspended = encode_suspension(tx, operation)
63
+ suspended = with_extension(suspended, state)
64
+ save_execution(tx, operation.execution, suspended)
65
+ root = save_suspended_root(tx, operation.root)
66
+ return Outcome.new(type: :suspended, execution: suspended, root: root,
67
+ appended_records: [].freeze,
68
+ result: result_base(suspended, root).merge(suspended: true, approval_request: view.approval_request).freeze,
69
+ error: nil, approval_request: view.approval_request)
75
70
  end
76
- return unless waiting
77
- coordination_wait_outcome(operation, waiting, error)
78
- rescue => failure
79
- confirmed = @persistence.executions.load(current.execution_id)
80
- raise failure unless waiting && confirmed.to_h == waiting.to_h
81
- coordination_wait_outcome(operation, confirmed, error)
82
- end
83
-
84
- def coordination_wait_outcome(operation, execution, error)
85
- Outcome.new(type: :coordination_wait, execution: execution, root: operation.root,
86
- appended_records: [].freeze, result: nil,
87
- error: error.is_a?(Phronomy::ExecutionRehydrationRequiredError) ? error :
88
- Phronomy::ExecutionRehydrationRequiredError.new("Execution #{execution.execution_id} retains unfinished children for cancellation/recovery"),
89
- approval_request: nil)
90
- end
91
-
92
- # F1: terminal atomicity alone does not establish commit outcome certainty.
93
- # Reuse an exact committed outcome; an absent/active/read-failed result is
94
- # never permission to write a second terminal transition.
95
- def reconcile_terminal_error(operation, error)
96
- confirmed = @persistence.executions.load(operation.execution_id)
97
- raise error unless confirmed.terminal? &&
98
- confirmed.agent_id == @agent.agent_id &&
99
- confirmed.execution_revision == operation.expected_execution_revision + 1
100
- root = @persistence.agents.load(@agent.agent_id)
101
- records = @persistence.journals.read(@agent.agent_id,
102
- after: operation.root.journal_position, limit: root.journal_position - operation.root.journal_position)
103
- result = @persistence.execution_result(confirmed.execution_id)
104
- failure = result[:error] && RecoverySupport.error_from_failure(result[:error])
105
- type = if failure
106
- :failed
71
+ records, execution = if error
72
+ encode_failure(tx, operation, error)
73
+ elsif view.handoff
74
+ raise Phronomy::ConfigurationError, "Control transfer needs a transaction participant" unless receipt
75
+ encode_transfer(tx, operation, receipt)
107
76
  else
108
- ((confirmed.status == :handed_off) ? :handed_off : :completed)
77
+ encode_completion(tx, operation)
109
78
  end
110
- Outcome.new(type: type, execution: confirmed, root: root,
111
- appended_records: records.freeze,
112
- result: failure ? nil : result_base(confirmed, root).merge(output: result[:result]).freeze,
113
- error: failure, approval_request: nil)
114
- end
115
-
116
- def commit_suspended(operation)
117
- suspended = next_root = nil
118
- @persistence.transaction do |tx|
119
- suspended = encode_suspension(tx, operation)
120
- save_execution(tx, operation.execution, suspended)
121
- next_root = save_suspended_root(tx, operation.root)
79
+ execution = with_extension(execution, state)
80
+ appended = append_journal(tx, operation.root, records)
81
+ save_execution(tx, operation.execution, execution)
82
+ root = save_terminal_root(tx, operation.root, appended)
83
+ if error
84
+ Outcome.new(type: :failed, execution: execution, root: root, appended_records: appended.freeze,
85
+ result: nil, error: error, approval_request: nil)
86
+ elsif view.handoff
87
+ Outcome.new(type: :handed_off, execution: execution, root: root, appended_records: appended.freeze,
88
+ result: result_base(execution, root).freeze, error: nil, approval_request: nil)
89
+ else
90
+ messages = transcript_messages(root, operation.journal_records + appended, persistence: tx)
91
+ completed_outcome(view, execution, root, appended, messages)
122
92
  end
123
- request = operation.terminal_view.approval_request
124
- Outcome.new(type: :suspended, execution: suspended, root: next_root,
125
- appended_records: [].freeze,
126
- result: result_base(suspended, next_root).merge(suspended: true, approval_request: request).freeze,
127
- error: nil, approval_request: request)
128
93
  end
129
94
 
130
- def commit_completed(operation)
131
- completed = next_root = appended = messages = nil
132
- @persistence.transaction do |tx|
133
- records, completed = encode_completion(tx, operation)
134
- appended = append_journal(tx, operation.root, records)
135
- completed = save_coordinated_execution(tx, operation.execution, completed)
136
- next_root = save_terminal_root(tx, operation.root, appended)
95
+ def extension_metadata(execution, state)
96
+ execution.metadata.merge("execution_extension" => state&.to_h).compact
97
+ end
137
98
 
138
- # Materialization stays inside the transaction. A failure rolls back
139
- # the terminal transition instead of writing again from a stale base.
140
- messages = transcript_messages(next_root,
141
- operation.journal_records + Array(appended), persistence: tx)
142
- end
143
- completed_outcome(operation.terminal_view, completed, next_root, appended, messages)
99
+ def with_extension(execution, state)
100
+ execution.with(execution_revision: execution.execution_revision, metadata: extension_metadata(execution, state))
144
101
  end
145
102
 
146
- def commit_failed_outcome(operation, error)
147
- translated_error = @agent.send(:_translated_error, error)
148
- failed = next_root = appended = nil
149
- @persistence.transaction do |tx|
150
- records, failed = encode_failure(tx, operation, translated_error)
151
- appended = append_journal(tx, operation.root, records)
152
- failed = save_coordinated_execution(tx, operation.execution, failed)
153
- next_root = save_terminal_root(tx, operation.root, appended)
103
+ def encode_transfer(tx, operation, receipt)
104
+ request = operation.terminal_view.handoff
105
+ unless receipt.fetch("target_agent_id") == request.target_agent_id &&
106
+ !receipt.fetch("target_execution_id").to_s.empty? && !receipt.fetch("context_ref").to_s.empty?
107
+ raise Phronomy::Persistence::StateConflictError, "Control transfer receipt mismatch"
154
108
  end
155
- Outcome.new(type: :failed, execution: failed, root: next_root,
156
- appended_records: Array(appended).freeze,
157
- result: nil, error: translated_error, approval_request: nil)
109
+ current = operation.execution
110
+ encoded, calls = encode_runtime_records(tx, operation, context_candidate: false)
111
+ audit_ref = tx.contents.put_json("target_agent_id" => request.target_agent_id,
112
+ "responsibility" => request.responsibility, "selection" => request.selection.transform_keys(&:to_s))
113
+ audit = JournalRecord.new(agent_id: @agent.agent_id, execution_id: current.execution_id,
114
+ llm_call_id: request.llm_call_id, kind: :execution_handed_off, channel: :audit,
115
+ content_ref: audit_ref, context_generation: operation.root.transcript_generation, context_candidate: false,
116
+ metadata: {"target_agent_id" => request.target_agent_id, "handoff_tool_call_id" => request.tool_call_id}.compact)
117
+ updated = current.with(status: :handed_off, phase: :handed_off, working_records: [],
118
+ llm_calls: current.llm_calls + calls, approval_request: nil, terminal_reason: "handed_off",
119
+ metadata: current.metadata.merge("transfer_receipt" => receipt))
120
+ [current.working_records + encoded + [audit], updated]
158
121
  end
159
122
 
160
123
  def encode_suspension(tx, operation)
@@ -264,25 +227,6 @@ module Phronomy
264
227
  expected_position: root.journal_position, records: records)
265
228
  end
266
229
 
267
- def save_coordinated_execution(tx, current, updated)
268
- coordinated = @agent.__prepare_coordination_record(updated, tx: tx)
269
- synchronize_handoff_target(tx, coordinated)
270
- save_execution(tx, current, coordinated)
271
- coordinated
272
- end
273
-
274
- def synchronize_handoff_target(tx, execution)
275
- coordination = execution.metadata["coordination"]
276
- return unless coordination && coordination["kind"] == "handoff"
277
- routing = tx.handoff_states.load(coordination.fetch("main_agent_id"))
278
- return unless routing && routing.pending_target_execution_id == execution.execution_id && routing.phase != "stable"
279
- unless routing.active_agent_id == execution.agent_id
280
- raise Phronomy::Storage::ConflictError, "Handoff Target owner mismatch"
281
- end
282
- tx.handoff_states.save(routing.main_agent_id, expected_revision: routing.handoff_revision,
283
- state: routing.with(phase: "stable"))
284
- end
285
-
286
230
  def save_execution(tx, current, updated)
287
231
  tx.executions.save(current.execution_id,
288
232
  expected_revision: current.execution_revision, execution: updated)
@@ -318,7 +262,7 @@ module Phronomy
318
262
  end
319
263
 
320
264
  def transcript_messages(root, journal_records, persistence: @persistence)
321
- materializer = RubyLLMMaterializer.new(
265
+ materializer = RuntimeInput.new(
322
266
  agent: @agent,
323
267
  persistence: persistence
324
268
  )
@@ -6,6 +6,10 @@ module Phronomy
6
6
  # The EventLoop remains the sole writer during execution.
7
7
  # @api private
8
8
  class ExecutionRegistry < Phronomy::ExecutionReceiver
9
+ def self.existing_current
10
+ ExecutionEnvironment.current.existing_registry
11
+ end
12
+
9
13
  # Immutable Agent-owned value. The map containing these records is the
10
14
  # mutable authority; records are replaced rather than mutated in place.
11
15
  AgentExecutionState = Data.define(
@@ -36,7 +40,7 @@ module Phronomy
36
40
  PhysicalCompletion = Data.define(:execution_id, :token)
37
41
  private_constant :UNSET, :PhysicalCompletion
38
42
 
39
- def initialize(event_loop:)
43
+ def initialize(channel:)
40
44
  super
41
45
  @agent_admissions = {}
42
46
  @agent_executions = {}
@@ -364,7 +368,7 @@ module Phronomy
364
368
  callback = lambda do
365
369
  accepted = post_message(PhysicalCompletion.new(execution_id: key, token: token))
366
370
  unless accepted
367
- Phronomy.configuration.logger&.warn(
371
+ Phronomy::RuntimeSettings.current.logger&.warn(
368
372
  "[Phronomy] EventLoop rejected physical-completion delivery for #{key}"
369
373
  )
370
374
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../../engine/concurrency/worker_input_restricted"
3
+ require_relative "../../execution/concurrency/worker_input_restricted"
4
4
 
5
5
  module Phronomy
6
6
  module Agent
@@ -11,10 +11,9 @@ module Phronomy
11
11
  class ExecutionSessionRunner
12
12
  include Phronomy::Concurrency::WorkerInputRestricted
13
13
 
14
- def initialize(runtime:, on_complete:)
14
+ def initialize(environment:, on_complete:)
15
15
  @on_complete = on_complete
16
- @runtime = runtime
17
- @event_loop = runtime.event_loop
16
+ @environment = environment
18
17
  end
19
18
 
20
19
  def register(session, result_task, source_name: "#{result_task.name}-source")
@@ -29,20 +28,19 @@ module Phronomy
29
28
  fsm_session_id: session.id
30
29
  )
31
30
  end
32
- @event_loop.register(session, completion: source)
31
+ @environment.register_session(session, completion: source)
33
32
  session
34
33
  end
35
34
 
36
35
  def resume(invocation, result_task, resume_event:, resume_phase:,
37
36
  source_name: "#{result_task.name}-source")
38
37
  assert_event_loop!
39
- session = AgentInvocationSessionBuilder.build_for_resume(
40
- agent_invocation: invocation,
38
+ session = @environment.build_agent_session(
39
+ invocation: invocation,
41
40
  resume_event: resume_event,
42
- resume_phase: resume_phase,
43
- runtime: @runtime
41
+ resume_phase: resume_phase
44
42
  )
45
- ExecutionRegistry.for(@event_loop).replace_agent_execution(
43
+ @environment.registry.replace_agent_execution(
46
44
  invocation.execution_id,
47
45
  invocation: invocation,
48
46
  fsm_session_id: session.id
@@ -58,20 +56,18 @@ module Phronomy
58
56
  resume_event: :resume, resume_phase: :suspended)
59
57
  invocation.tool_invocations.each do |child|
60
58
  session = if child.awaiting_approval?
61
- ToolInvocationSessionBuilder.build_for_resume(
62
- tool_invocation: child,
63
- parent_event_sink: parent.event_sink,
59
+ @environment.build_tool_session(
60
+ invocation: child,
61
+ parent_sink: parent.event_sink,
64
62
  resume_event: approved ? :approve : :reject,
65
- resume_phase: :awaiting_approval,
66
- runtime: @runtime
63
+ resume_phase: :awaiting_approval
67
64
  )
68
65
  elsif !approved && child.authorized?
69
- ToolInvocationSessionBuilder.build_for_resume(
70
- tool_invocation: child,
71
- parent_event_sink: parent.event_sink,
66
+ @environment.build_tool_session(
67
+ invocation: child,
68
+ parent_sink: parent.event_sink,
72
69
  resume_event: :cancel,
73
- resume_phase: :authorized,
74
- runtime: @runtime
70
+ resume_phase: :authorized
75
71
  )
76
72
  end
77
73
  register_child(child, session, parent.event_sink) if session
@@ -84,12 +80,11 @@ module Phronomy
84
80
  resume_event: :resume, resume_phase: :suspended,
85
81
  source_name: "framework-tool-recovery-source")
86
82
  invocation.tool_invocations.select(&:authorized?).each do |child|
87
- session = ToolInvocationSessionBuilder.build_for_resume(
88
- tool_invocation: child,
89
- parent_event_sink: parent.event_sink,
83
+ session = @environment.build_tool_session(
84
+ invocation: child,
85
+ parent_sink: parent.event_sink,
90
86
  resume_event: :dispatch,
91
- resume_phase: :authorized,
92
- runtime: @runtime
87
+ resume_phase: :authorized
93
88
  )
94
89
  register_child(child, session, parent.event_sink)
95
90
  end
@@ -105,11 +100,11 @@ module Phronomy
105
100
  child.mark_framework_failed!(error)
106
101
  parent_event_sink.post(:tool_failed, {tool_invocation_id: child.id})
107
102
  end
108
- @event_loop.register(session, completion: completion)
103
+ @environment.register_session(session, completion: completion)
109
104
  end
110
105
 
111
106
  def assert_event_loop!
112
- return if @event_loop.current?
107
+ return if @environment.executing?
113
108
 
114
109
  raise Phronomy::Error, "Agent session registration must run on EventLoop"
115
110
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../../engine/concurrency/worker_input_restricted"
3
+ require_relative "../../execution/concurrency/worker_input_restricted"
4
4
 
5
5
  require "securerandom"
6
6
  require "time"
@@ -49,9 +49,9 @@ module Phronomy
49
49
  # A durable nonterminal execution keeps Runtime admission fail-closed
50
50
  # until recovery can establish its lineage, including after F4 loss.
51
51
  return unadmitted_result(operation, error, :recovery_required)
52
- rescue Phronomy::Storage::ConflictError,
53
- Phronomy::Storage::NotFoundError,
54
- Phronomy::Storage::SerializationError,
52
+ rescue Phronomy::Persistence::ConflictError,
53
+ Phronomy::Persistence::NotFoundError,
54
+ Phronomy::Persistence::SerializationError,
55
55
  ArgumentError,
56
56
  Phronomy::ConfigurationError => error
57
57
  return unadmitted_result(operation, error, :not_established)
@@ -97,7 +97,7 @@ module Phronomy
97
97
  filtered_input: nil,
98
98
  config: operation.config,
99
99
  appended_records: [].freeze,
100
- error: translated(error),
100
+ error: error,
101
101
  admission_outcome: outcome
102
102
  )
103
103
  end
@@ -114,7 +114,7 @@ module Phronomy
114
114
  # Advance the failure base only after the commit response is known.
115
115
  # F1 response loss must not be reported as a confirmed terminal result.
116
116
  current_execution = active_execution
117
- projection = RubyLLMMaterializer.new(
117
+ projection = RuntimeInput.new(
118
118
  agent: @agent, persistence: @persistence
119
119
  ).materialize(manifest: manifest, manifest_ref: manifest_ref)
120
120
  Result.new(
@@ -166,7 +166,7 @@ module Phronomy
166
166
  end
167
167
 
168
168
  def prepare_context(staged, root, input, config, journal_records)
169
- assembler = ContextAssembler.new(
169
+ assembler = ContextPreparation.new(
170
170
  agent: @agent, persistence: @persistence, journal_records: journal_records
171
171
  )
172
172
  prepared = assembler.prepare_initial(
@@ -184,7 +184,7 @@ module Phronomy
184
184
  active = manifest = manifest_ref = nil
185
185
  @persistence.transaction do |tx|
186
186
  assert_local_durable_base!(tx, root)
187
- manifest, manifest_ref = assembler.finalize(prepared, persistence: tx)
187
+ manifest, manifest_ref = Phronomy::Context::Assembly.new.store(prepared, contents: tx.contents)
188
188
  active = execution_with_initial_manifest(staged, manifest_ref)
189
189
  tx.executions.save(
190
190
  staged.execution_id,
@@ -208,26 +208,15 @@ module Phronomy
208
208
  end
209
209
 
210
210
  def admit_execution(raw_message, root:, mode: :invoke, config: {}, preparation_replayable: false)
211
- if root.lifecycle_status == :closed
212
- raise Phronomy::Error, "agent is closed: #{@agent.agent_id}"
211
+ admission = Admission.new(persistence: @persistence, root: root,
212
+ input: raw_message, config: config,
213
+ preparation_metadata: initial_preparation_metadata(mode.to_sym, preparation_replayable))
214
+ participant = config[:phronomy_admission]
215
+ if admission.correlation && !participant
216
+ raise Phronomy::ConfigurationError, "Coordinated admission requires its current owner"
213
217
  end
214
-
215
- execution = next_root = nil
216
- preparation_metadata = initial_preparation_metadata(mode.to_sym, preparation_replayable)
217
- @persistence.transaction do |tx|
218
- execution = build_admitted_execution(
219
- tx, raw_message, root: root, config: config,
220
- preparation_metadata: preparation_metadata
221
- )
222
- validate_coordination_admission!(tx, execution)
223
- tx.executions.create_active(execution)
224
- next_root = root.with(
225
- agent_revision: root.agent_revision + 1,
226
- lifecycle_status: :active
227
- )
228
- tx.agents.save(root.agent_id, expected_revision: root.agent_revision, root: next_root)
229
- end
230
- [execution, next_root]
218
+ participant ? participant.admit(admission) : admission.accept
219
+ admission.result
231
220
  end
232
221
 
233
222
  def initial_preparation_metadata(mode, replayable)
@@ -240,92 +229,9 @@ module Phronomy
240
229
  }
241
230
  end
242
231
 
243
- def build_admitted_execution(tx, raw_message, root:, config:, preparation_metadata:)
244
- input_ref = tx.contents.put_text(raw_message)
245
- durable_context_ref = if config.key?(:durable_context)
246
- tx.contents.put_json(config.fetch(:durable_context))
247
- end
248
- input_record = JournalRecord.new(
249
- agent_id: @agent.agent_id,
250
- kind: :input_received,
251
- channel: :external,
252
- role: :user,
253
- content_ref: input_ref,
254
- context_generation: root.transcript_generation,
255
- context_candidate: false
256
- )
257
- execution = AgentExecution.start(
258
- agent_root: root,
259
- input_record: input_record,
260
- execution_id: config[:phronomy_reserved_execution_id] || SecureRandom.uuid,
261
- metadata: {
262
- "coordination" => config[:phronomy_coordination],
263
- "current_input_ref" => input_ref,
264
- "durable_context_ref" => durable_context_ref
265
- }.merge(preparation_metadata).compact
266
- )
267
- input_record = JournalRecord.from_h(input_record.to_h.merge("execution_id" => execution.execution_id))
268
- execution.with(execution_revision: 0, working_records: [input_record])
269
- end
270
-
271
- def validate_coordination_admission!(tx, execution)
272
- owner = execution.metadata["coordination"]
273
- return unless owner
274
-
275
- case owner.fetch("kind")
276
- when "team" then validate_team_admission!(tx, execution, owner)
277
- when "subagent" then validate_subagent_admission!(tx, execution, owner)
278
- when "handoff" then validate_handoff_admission!(tx, execution, owner)
279
- else
280
- raise Phronomy::ConfigurationError, "Unknown coordination owner kind"
281
- end
282
- end
283
-
284
- def validate_team_admission!(tx, execution, owner)
285
- team = tx.team_executions.load(owner.fetch("team_execution_id"))
286
- unless team.team_id == owner.fetch("team_id") && team.active? && !team.metadata["cancel_requested"]
287
- raise Phronomy::CancellationError, "Team run is no longer dispatchable"
288
- end
289
- slot = if owner.fetch("slot") == "coordinator"
290
- team.coordinator
291
- else
292
- assignment = team.assignments.find { |entry| entry.fetch("task_id") == owner.fetch("slot") }
293
- worker = assignment && team.workers.fetch(assignment.fetch("worker"))
294
- worker&.merge("execution_id" => assignment.fetch("execution_id"))
295
- end
296
- unless slot && slot.fetch("execution_id") == execution.execution_id && slot.fetch("agent_id") == execution.agent_id
297
- raise Phronomy::Storage::ConflictError, "Team reserved child identity mismatch"
298
- end
299
- end
300
-
301
- def validate_subagent_admission!(tx, execution, owner)
302
- parent = tx.executions.load(owner.fetch("parent_execution_id"))
303
- unless parent.agent_id == owner.fetch("parent_agent_id") && parent.active? && !parent.metadata["coordination_cancel_requested"]
304
- raise Phronomy::CancellationError, "Parent run is no longer dispatchable"
305
- end
306
- snapshot = tx.contents.fetch_json(parent.metadata.fetch("multi_agent_coordination_ref"))
307
- slot = snapshot.fetch("children").find { |entry| entry.fetch("slot") == owner.fetch("slot") }
308
- unless slot && slot.fetch("agent_id") == execution.agent_id && slot.fetch("execution_id") == execution.execution_id
309
- raise Phronomy::Storage::ConflictError, "Parent reserved child identity mismatch"
310
- end
311
- end
312
-
313
- def validate_handoff_admission!(tx, execution, owner)
314
- routing = tx.handoff_states.load(owner.fetch("main_agent_id"))
315
- unless routing && routing.active_agent_id == execution.agent_id
316
- raise Phronomy::Storage::ConflictError, "Handoff responsibility changed before admission"
317
- end
318
- if Array(routing.metadata["cancelled_execution_ids"]).include?(execution.execution_id)
319
- raise Phronomy::CancellationError, "Handoff reservation was cancelled"
320
- end
321
- if routing.phase != "stable" && routing.pending_target_execution_id != execution.execution_id
322
- raise Phronomy::Storage::ConflictError, "Handoff reserved Target identity mismatch"
323
- end
324
- end
325
-
326
232
  def recovered_config(execution)
327
233
  mode = (execution.metadata[ExecutionMetadata::INVOCATION_MODE_KEY] || "invoke").to_sym
328
- config = {phronomy_recovery_mode: mode}.merge(@agent.__coordination_config)
234
+ config = {phronomy_recovery_mode: mode}.merge(@agent.__execution_wiring)
329
235
  if execution.metadata.key?("durable_context_ref")
330
236
  context = @persistence.contents.fetch_json(execution.metadata.fetch("durable_context_ref"))
331
237
  unless context.is_a?(Hash)
@@ -353,7 +259,7 @@ module Phronomy
353
259
  end
354
260
 
355
261
  def commit_preparation_failure(execution, root, error)
356
- translated_error = translated(error)
262
+ translated_error = error
357
263
  failed = next_root = appended = nil
358
264
  @persistence.transaction do |tx|
359
265
  error_ref = tx.contents.put_json(
@@ -412,10 +318,6 @@ module Phronomy
412
318
  journal_position: root.journal_position
413
319
  )
414
320
  end
415
-
416
- def translated(error)
417
- @agent.send(:_translated_error, error)
418
- end
419
321
  end
420
322
  end
421
323
  end