phronomy 0.27.0 → 0.29.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (517) hide show
  1. checksums.yaml +4 -4
  2. data/.mutant.yml +9 -9
  3. data/CHANGELOG.md +52 -0
  4. data/CONTRIBUTING.md +8 -8
  5. data/README.md +28 -7
  6. data/benchmark/bench_agent_invoke.rb +25 -50
  7. data/benchmark/bench_context_assembler.rb +14 -11
  8. data/benchmark/bench_regression.rb +1 -1
  9. data/benchmark/bench_tool_schema.rb +1 -1
  10. data/docs/application-recipes.md +229 -0
  11. data/docs/architecture/agent-chat-and-state-ownership.md +7 -1
  12. data/docs/architecture/agent-transition-ownership.md +13 -89
  13. data/docs/architecture/backend-groups.md +152 -0
  14. data/docs/architecture/before-llm-input.md +3 -3
  15. data/docs/architecture/context-management.md +1 -1
  16. data/docs/architecture/dx05-agent-scope.md +119 -0
  17. data/docs/architecture/dx05-workflow-task-state.md +134 -0
  18. data/docs/architecture/error-api-review.md +245 -0
  19. data/docs/architecture/execution-abstraction-review.md +88 -0
  20. data/docs/architecture/execution-boundaries.md +28 -0
  21. data/docs/architecture/execution-metadata-and-values.md +6 -0
  22. data/docs/architecture/multi-agent-handoff.md +6 -5
  23. data/docs/architecture/persistence.md +27 -9
  24. data/docs/architecture/r8-unit1.md +104 -0
  25. data/docs/architecture/r8-unit10.md +72 -0
  26. data/docs/architecture/r8-unit11.md +71 -0
  27. data/docs/architecture/r8-unit12.md +67 -0
  28. data/docs/architecture/r8-unit13.md +61 -0
  29. data/docs/architecture/r8-unit14.md +72 -0
  30. data/docs/architecture/r8-unit15.md +65 -0
  31. data/docs/architecture/r8-unit16.md +54 -0
  32. data/docs/architecture/r8-unit17.md +73 -0
  33. data/docs/architecture/r8-unit18.md +92 -0
  34. data/docs/architecture/r8-unit19.md +61 -0
  35. data/docs/architecture/r8-unit2.md +55 -0
  36. data/docs/architecture/r8-unit20.md +51 -0
  37. data/docs/architecture/r8-unit21.md +79 -0
  38. data/docs/architecture/r8-unit22.md +74 -0
  39. data/docs/architecture/r8-unit23.md +81 -0
  40. data/docs/architecture/r8-unit3.md +130 -0
  41. data/docs/architecture/r8-unit4.md +79 -0
  42. data/docs/architecture/r8-unit5.md +115 -0
  43. data/docs/architecture/r8-unit6.md +71 -0
  44. data/docs/architecture/r8-unit7.md +70 -0
  45. data/docs/architecture/r8-unit8.md +78 -0
  46. data/docs/architecture/r8-unit9.md +73 -0
  47. data/docs/architecture/refactoring-closure.md +10 -1
  48. data/docs/architecture/remaining-refactoring-plan.md +16 -0
  49. data/docs/architecture/storage-execution-boundaries.md +54 -0
  50. data/docs/architecture/tool-schema-recording-gap.md +6 -0
  51. data/docs/architecture/tracing.md +1 -1
  52. data/docs/architecture/workflow-terminal-ownership-design.md +2 -2
  53. data/docs/architecture.md +24 -11
  54. data/docs/decisions/015-tool-public-facade-and-rbs-boundary.md +3 -3
  55. data/docs/decisions/027-llm-adapter-provider-boundary.md +2 -0
  56. data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +2 -0
  57. data/docs/decisions/031-durable-multi-agent-coordination.md +2 -0
  58. data/docs/decisions/032-storage-backend-composition.md +5 -0
  59. data/docs/decisions/034-handoff-runner-coordination-ownership.md +2 -0
  60. data/docs/decisions/035-tool-executor-capability-ownership.md +1 -1
  61. data/docs/decisions/038-responsibility-based-source-layout.md +14 -0
  62. data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +8 -0
  63. data/docs/decisions/040-configuration-default-composition.md +12 -1
  64. data/docs/decisions/045-worker-input-restriction-ownership.md +4 -0
  65. data/docs/decisions/051-execution-outcome-worker-ownership.md +2 -0
  66. data/docs/decisions/056-workflow-terminal-policy-ownership.md +1 -1
  67. data/docs/decisions/058-neutral-storage-primitives.md +5 -0
  68. data/docs/decisions/059-backend-contracts-and-async-clients.md +74 -0
  69. data/docs/decisions/060-runtime-settings-and-application-configuration.md +86 -0
  70. data/docs/decisions/061-persistence-failure-contracts.md +89 -0
  71. data/docs/decisions/062-content-store-service.md +52 -0
  72. data/docs/decisions/063-execution-contracts-and-services.md +88 -0
  73. data/docs/decisions/064-agent-execution-and-multi-agent-responsibility.md +36 -0
  74. data/docs/decisions/065-vector-and-embeddings-common-operations.md +38 -0
  75. data/docs/decisions/066-llm-and-tool-operation-contracts.md +20 -0
  76. data/docs/decisions/067-agent-progress-and-engine-connection.md +29 -0
  77. data/docs/decisions/068-agent-tool-child-progress-and-tool-operation.md +36 -0
  78. data/docs/decisions/README.md +18 -1
  79. data/docs/execution-boundaries-preview.md +45 -0
  80. data/docs/failure-reason-codes.md +45 -0
  81. data/docs/features.md +12 -4
  82. data/docs/getting-started.md +52 -48
  83. data/docs/migrations/0.22.md +2 -2
  84. data/docs/migrations/durable-semantic-coordination-v2.md +2 -0
  85. data/docs/migrations/handoff-runner-multi-agent.md +2 -0
  86. data/docs/migrations/persistence-failure-contracts.md +67 -0
  87. data/docs/migrations/r8-unit10.md +36 -0
  88. data/docs/migrations/r8-unit11.md +33 -0
  89. data/docs/migrations/r8-unit12.md +31 -0
  90. data/docs/migrations/r8-unit13.md +36 -0
  91. data/docs/migrations/r8-unit14.md +31 -0
  92. data/docs/migrations/r8-unit15.md +31 -0
  93. data/docs/migrations/r8-unit16.md +26 -0
  94. data/docs/migrations/r8-unit17.md +28 -0
  95. data/docs/migrations/r8-unit18.md +29 -0
  96. data/docs/migrations/r8-unit19.md +28 -0
  97. data/docs/migrations/r8-unit20.md +24 -0
  98. data/docs/migrations/r8-unit21.md +28 -0
  99. data/docs/migrations/r8-unit22.md +29 -0
  100. data/docs/migrations/r8-unit23.md +20 -0
  101. data/docs/migrations/r8-unit4.md +67 -0
  102. data/docs/migrations/r8-unit5.md +75 -0
  103. data/docs/migrations/r8-unit6.md +109 -0
  104. data/docs/migrations/r8-unit7.md +31 -0
  105. data/docs/migrations/r8-unit8.md +33 -0
  106. data/docs/migrations/r8-unit9.md +28 -0
  107. data/docs/migrations/storage-async-client.md +103 -0
  108. data/docs/migrations/vector-async-clients.md +110 -0
  109. data/docs/ordered-event-delivery.md +54 -0
  110. data/docs/persistence-backends.md +38 -21
  111. data/docs/runtime-and-concurrency.md +26 -1
  112. data/docs/workflow-completion.md +92 -0
  113. data/gemfiles/http_minimum.gemfile +5 -0
  114. data/lib/phronomy/agent/admission.rb +78 -0
  115. data/lib/phronomy/agent/{lifecycle_contract/agent_already_exists_error.rb → agent_already_exists_error.rb} +1 -1
  116. data/lib/phronomy/agent/{lifecycle_contract/agent_busy_error.rb → agent_busy_error.rb} +1 -1
  117. data/lib/phronomy/agent/agent_not_found_error.rb +16 -0
  118. data/lib/phronomy/agent/{lifecycle_contract/agent_purged_error.rb → agent_purged_error.rb} +1 -1
  119. data/lib/phronomy/agent/agent_unloaded_error.rb +14 -0
  120. data/lib/phronomy/agent/api/agent.rb +13 -0
  121. data/lib/phronomy/{recovery → agent/api}/execution_rehydration_required_error.rb +1 -1
  122. data/lib/phronomy/agent/async_event_api.rb +132 -103
  123. data/lib/phronomy/agent/base.rb +146 -113
  124. data/lib/phronomy/agent/before_llm_input.rb +70 -0
  125. data/lib/phronomy/agent/composition/run_once.rb +1 -1
  126. data/lib/phronomy/agent/{context_assembly/context_candidate_resolver.rb → context_candidate_resolver.rb} +2 -2
  127. data/lib/phronomy/agent/{context_assembly/context_assembler.rb → context_preparation.rb} +35 -221
  128. data/lib/phronomy/agent/control_request.rb +21 -0
  129. data/lib/phronomy/agent/execution/agent_execution.rb +1 -1
  130. data/lib/phronomy/agent/execution/agent_invocation.rb +27 -53
  131. data/lib/phronomy/agent/execution/approval_resume_commit.rb +1 -1
  132. data/lib/phronomy/agent/execution/dispatch_preparation.rb +17 -127
  133. data/lib/phronomy/agent/execution/exact_execution.rb +55 -32
  134. data/lib/phronomy/agent/execution/execution_cancellation.rb +8 -4
  135. data/lib/phronomy/agent/execution/execution_coordinator.rb +229 -407
  136. data/lib/phronomy/agent/execution/execution_metadata.rb +1 -0
  137. data/lib/phronomy/agent/execution/execution_outcome_committer.rb +71 -127
  138. data/lib/phronomy/agent/execution/execution_registry.rb +6 -2
  139. data/lib/phronomy/agent/execution/execution_session_runner.rb +22 -27
  140. data/lib/phronomy/agent/execution/initial_preparation.rb +19 -120
  141. data/lib/phronomy/agent/execution/{agent_invocation_session_builder.rb → invocation_actions.rb} +54 -194
  142. data/lib/phronomy/agent/execution/invocation_transitions.rb +39 -1
  143. data/lib/phronomy/agent/execution/runtime_record_encoder.rb +8 -11
  144. data/lib/phronomy/agent/execution_change.rb +161 -0
  145. data/lib/phronomy/agent/execution_environment.rb +36 -0
  146. data/lib/phronomy/agent/execution_extension_state.rb +21 -0
  147. data/lib/phronomy/agent/execution_observation.rb +35 -0
  148. data/lib/phronomy/agent/filterable.rb +188 -0
  149. data/lib/phronomy/agent/handoff/{handoff_context.rb → transfer_context.rb} +4 -4
  150. data/lib/phronomy/agent/handoff/{handoff_projection.rb → transfer_projection.rb} +14 -33
  151. data/lib/phronomy/{llm_contract/transport_error.rb → agent/handoff_error.rb} +1 -1
  152. data/lib/phronomy/agent/journal/journal_projection.rb +1 -1
  153. data/lib/phronomy/agent/knowledge_item.rb +13 -0
  154. data/lib/phronomy/agent/lifecycle/agent_root.rb +1 -1
  155. data/lib/phronomy/agent/lifecycle/lifetime_api.rb +76 -0
  156. data/lib/phronomy/agent/lifecycle/operation_lease.rb +33 -0
  157. data/lib/phronomy/agent/lifecycle/operation_result.rb +53 -0
  158. data/lib/phronomy/agent/lifecycle/ownership_registry.rb +117 -31
  159. data/lib/phronomy/agent/lifecycle/scope.rb +198 -0
  160. data/lib/phronomy/agent/lifecycle/scope_cleanup_error.rb +19 -0
  161. data/lib/phronomy/agent/lifecycle/unload_transition.rb +78 -0
  162. data/lib/phronomy/agent/persistence/agent_repository.rb +29 -21
  163. data/lib/phronomy/agent/persistence/cancellation_repository.rb +51 -0
  164. data/lib/phronomy/agent/persistence/codec.rb +1 -21
  165. data/lib/phronomy/agent/persistence/execution_repository.rb +57 -41
  166. data/lib/phronomy/agent/persistence/journal_repository.rb +27 -18
  167. data/lib/phronomy/agent/persistence/records.rb +33 -0
  168. data/lib/phronomy/agent/persistence/retention_repository.rb +70 -0
  169. data/lib/phronomy/agent/persistence/storage_schema.rb +7 -2
  170. data/lib/phronomy/agent/persistence/watermark.rb +13 -11
  171. data/lib/phronomy/agent/recovery/invocation_restorer.rb +13 -21
  172. data/lib/phronomy/agent/recovery/recovery_coordinator/continuation.rb +13 -12
  173. data/lib/phronomy/agent/recovery/recovery_coordinator/installation.rb +34 -34
  174. data/lib/phronomy/agent/recovery/recovery_coordinator/resolution.rb +33 -39
  175. data/lib/phronomy/agent/recovery/recovery_coordinator.rb +28 -25
  176. data/lib/phronomy/agent/recovery/recovery_rules.rb +119 -0
  177. data/lib/phronomy/agent/recovery/recovery_support.rb +10 -19
  178. data/lib/phronomy/agent/reserved_execution.rb +16 -0
  179. data/lib/phronomy/agent/retention.rb +17 -0
  180. data/lib/phronomy/agent/runtime_binding/engine_environment.rb +60 -0
  181. data/lib/phronomy/agent/runtime_binding/engine_session_builder.rb +34 -0
  182. data/lib/phronomy/agent/runtime_binding/phase_machine_builder.rb +64 -0
  183. data/lib/phronomy/agent/runtime_binding/tool_session_builder.rb +34 -0
  184. data/lib/phronomy/agent/{context_assembly/ruby_llm_materializer.rb → runtime_input.rb} +18 -35
  185. data/lib/phronomy/agent/{context_assembly/saved_context_reader.rb → saved_context_reader.rb} +6 -4
  186. data/lib/phronomy/agent/settings.rb +47 -0
  187. data/lib/phronomy/agent/{context_assembly/state_writer.rb → state_writer.rb} +4 -3
  188. data/lib/phronomy/agent/store.rb +253 -0
  189. data/lib/phronomy/agent/{lifecycle_contract/stream_callback_error.rb → stream_callback_error.rb} +3 -3
  190. data/lib/phronomy/agent/tool_execution/tool_binding.rb +4 -64
  191. data/lib/phronomy/agent/tool_execution/tool_invocation.rb +50 -219
  192. data/lib/phronomy/agent/tool_execution/tool_invocation_actions.rb +107 -0
  193. data/lib/phronomy/agent/tool_execution/tool_invocation_transitions.rb +70 -0
  194. data/lib/phronomy/common/error.rb +65 -1
  195. data/lib/phronomy/configuration/runtime_settings.rb +49 -0
  196. data/lib/phronomy/content_store/base.rb +7 -2
  197. data/lib/phronomy/content_store/storage_schema.rb +1 -0
  198. data/lib/phronomy/content_store/stored_contents.rb +2 -1
  199. data/lib/phronomy/context/assembly.rb +195 -0
  200. data/lib/phronomy/{llm_contract/context_budget_exceeded_error.rb → context/budget_exceeded_error.rb} +3 -1
  201. data/lib/phronomy/context/candidate.rb +51 -0
  202. data/lib/phronomy/context/constraint.rb +47 -0
  203. data/lib/phronomy/{agent/context_contract → context}/context_plan.rb +1 -1
  204. data/lib/phronomy/{agent/context_contract → context}/context_plan_validator.rb +3 -3
  205. data/lib/phronomy/{agent/context_contract → context}/context_policy.rb +1 -1
  206. data/lib/phronomy/{agent/context_contract → context}/context_policy_input.rb +1 -1
  207. data/lib/phronomy/{agent/context_assembly → context}/context_policy_input_builder.rb +1 -1
  208. data/lib/phronomy/context/default_policy.rb +140 -0
  209. data/lib/phronomy/context/final_budget_validator.rb +33 -0
  210. data/lib/phronomy/{agent/context_contract → context}/llm_input_build_context.rb +1 -1
  211. data/lib/phronomy/{agent/context_contract → context}/llm_input_manifest.rb +19 -19
  212. data/lib/phronomy/{agent/context_contract → context}/llm_input_patch.rb +1 -1
  213. data/lib/phronomy/context/manifest_error.rb +10 -0
  214. data/lib/phronomy/context/prompt_template.rb +98 -0
  215. data/lib/phronomy/{vector_store → documents}/loader/base.rb +1 -1
  216. data/lib/phronomy/{vector_store → documents}/loader/csv_loader.rb +2 -2
  217. data/lib/phronomy/{vector_store → documents}/loader/markdown_loader.rb +3 -3
  218. data/lib/phronomy/{vector_store → documents}/loader/plain_text_loader.rb +2 -2
  219. data/lib/phronomy/{vector_store → documents}/splitter/base.rb +1 -1
  220. data/lib/phronomy/{vector_store → documents}/splitter/fixed_size_splitter.rb +2 -2
  221. data/lib/phronomy/{vector_store → documents}/splitter/recursive_splitter.rb +2 -2
  222. data/lib/phronomy/embeddings/async/async_client.rb +44 -0
  223. data/lib/phronomy/embeddings/authentication_error.rb +11 -0
  224. data/lib/phronomy/embeddings/backends/ruby_llm_embeddings.rb +52 -0
  225. data/lib/phronomy/embeddings/base.rb +43 -0
  226. data/lib/phronomy/embeddings/invalid_result_error.rb +11 -0
  227. data/lib/phronomy/embeddings/rate_limit_error.rb +11 -0
  228. data/lib/phronomy/embeddings/transport_error.rb +11 -0
  229. data/lib/phronomy/engine/concurrency/offload_pool.rb +36 -21
  230. data/lib/phronomy/engine/concurrency/pool_registry.rb +47 -12
  231. data/lib/phronomy/engine/diagnostics.rb +1 -1
  232. data/lib/phronomy/engine/event_loop.rb +7 -7
  233. data/lib/phronomy/engine/execution_binding.rb +23 -0
  234. data/lib/phronomy/engine/execution_receiver_binding.rb +43 -0
  235. data/lib/phronomy/engine/fsm_protocol.rb +1 -6
  236. data/lib/phronomy/engine/fsm_session.rb +3 -3
  237. data/lib/phronomy/engine/metrics.rb +16 -14
  238. data/lib/phronomy/engine/runtime/timer_queue.rb +1 -1
  239. data/lib/phronomy/engine/runtime.rb +25 -12
  240. data/lib/phronomy/engine/terminal_decision.rb +7 -0
  241. data/lib/phronomy/execution/async_operation.rb +41 -0
  242. data/lib/phronomy/{engine → execution}/blocking.rb +2 -2
  243. data/lib/phronomy/{engine → execution}/concurrency/cancellation_token.rb +1 -1
  244. data/lib/phronomy/{engine → execution}/concurrency/deadline.rb +0 -17
  245. data/lib/phronomy/{engine → execution}/concurrency/physical_completion_task.rb +1 -1
  246. data/lib/phronomy/{engine → execution}/concurrency/result_collector.rb +1 -1
  247. data/lib/phronomy/{engine → execution}/concurrency/result_composition.rb +33 -12
  248. data/lib/phronomy/{engine/concurrency/subscriptions.rb → execution/concurrency/result_subscriptions.rb} +14 -18
  249. data/lib/phronomy/execution/concurrency/subscriptions.rb +25 -0
  250. data/lib/phronomy/{engine → execution}/execution.rb +61 -27
  251. data/lib/phronomy/{engine → execution}/execution_cancellation_error.rb +2 -2
  252. data/lib/phronomy/execution/execution_receiver.rb +78 -0
  253. data/lib/phronomy/execution/execution_scope_state.rb +35 -0
  254. data/lib/phronomy/{engine → execution}/execution_timeout_error.rb +2 -2
  255. data/lib/phronomy/{engine → execution}/invocation_context.rb +0 -15
  256. data/lib/phronomy/execution/invocation_controls.rb +73 -0
  257. data/lib/phronomy/execution/runnable.rb +24 -0
  258. data/lib/phronomy/{engine → execution}/task_result.rb +9 -4
  259. data/lib/phronomy/execution/wait_policy.rb +24 -0
  260. data/lib/phronomy/filter/{contract/filter_block_error.rb → filter_block_error.rb} +3 -3
  261. data/lib/phronomy/generation/generator_verifier/agent_operation.rb +65 -0
  262. data/lib/phronomy/generation/generator_verifier/default_parser.rb +27 -0
  263. data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +2 -2
  264. data/lib/phronomy/generation/generator_verifier.rb +7 -7
  265. data/lib/phronomy/generation/low_confidence_error.rb +2 -2
  266. data/lib/phronomy/integrations/ordered_event_delivery.rb +208 -0
  267. data/lib/phronomy/llm_adapter/async/async_client.rb +47 -0
  268. data/lib/phronomy/{llm_contract → llm_adapter}/authentication_error.rb +3 -1
  269. data/lib/phronomy/llm_adapter/backends/ruby_llm.rb +183 -0
  270. data/lib/phronomy/llm_adapter/base.rb +49 -68
  271. data/lib/phronomy/{llm_contract → llm_adapter}/context_length_error.rb +3 -1
  272. data/lib/phronomy/llm_adapter/invalid_result_error.rb +10 -0
  273. data/lib/phronomy/llm_adapter/message.rb +62 -0
  274. data/lib/phronomy/{llm_contract → llm_adapter}/rate_limit_error.rb +3 -1
  275. data/lib/phronomy/llm_adapter/request.rb +79 -0
  276. data/lib/phronomy/llm_adapter/response.rb +36 -0
  277. data/lib/phronomy/llm_adapter/stream_chunk.rb +18 -0
  278. data/lib/phronomy/llm_adapter/token_usage.rb +64 -0
  279. data/lib/phronomy/llm_adapter/transport_error.rb +9 -0
  280. data/lib/phronomy/llm_adapter.rb +3 -16
  281. data/lib/phronomy/multi_agent/admission_registry.rb +1 -8
  282. data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +108 -84
  283. data/lib/phronomy/multi_agent/execution_environment.rb +29 -0
  284. data/lib/phronomy/{agent/handoff → multi_agent}/handoff.rb +2 -2
  285. data/lib/phronomy/{agent/handoff → multi_agent}/handoff_capability_factory.rb +21 -2
  286. data/lib/phronomy/multi_agent/handoff_participant.rb +79 -0
  287. data/lib/phronomy/{agent/handoff → multi_agent}/handoff_policy.rb +1 -1
  288. data/lib/phronomy/multi_agent/handoff_runner.rb +77 -100
  289. data/lib/phronomy/{agent/handoff → multi_agent}/handoff_state.rb +1 -1
  290. data/lib/phronomy/multi_agent/orchestrator.rb +61 -75
  291. data/lib/phronomy/multi_agent/persistence/codec.rb +20 -0
  292. data/lib/phronomy/multi_agent/persistence/handoff_state_repository.rb +77 -0
  293. data/lib/phronomy/multi_agent/persistence/records.rb +27 -0
  294. data/lib/phronomy/multi_agent/persistence/storage_schema.rb +21 -0
  295. data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +60 -44
  296. data/lib/phronomy/multi_agent/persistence/team_repository.rb +29 -21
  297. data/lib/phronomy/multi_agent/reserved_child_admission.rb +88 -0
  298. data/lib/phronomy/multi_agent/runtime_binding/engine_environment.rb +35 -0
  299. data/lib/phronomy/multi_agent/shared_state.rb +2 -2
  300. data/lib/phronomy/multi_agent/store.rb +156 -0
  301. data/lib/phronomy/multi_agent/team_busy_error.rb +18 -0
  302. data/lib/phronomy/multi_agent/team_coordinator.rb +99 -77
  303. data/lib/phronomy/multi_agent/team_not_found_error.rb +18 -0
  304. data/lib/phronomy/multi_agent/team_ownership_registry.rb +2 -12
  305. data/lib/phronomy/output_parser/parse_error.rb +7 -0
  306. data/lib/phronomy/persistence/content_repository.rb +26 -0
  307. data/lib/phronomy/persistence/errors.rb +16 -0
  308. data/lib/phronomy/persistence/migration/initial_format_migration.rb +2 -2
  309. data/lib/phronomy/persistence/persistence.rb +23 -0
  310. data/lib/phronomy/persistence/save_outcome.rb +24 -0
  311. data/lib/phronomy/persistence/snapshot_comparison.rb +81 -0
  312. data/lib/phronomy/persistence/storage_boundary.rb +32 -0
  313. data/lib/phronomy/persistence/transaction.rb +86 -0
  314. data/lib/phronomy/persistence_composition/storage_schema.rb +9 -4
  315. data/lib/phronomy/persistence_composition/stores.rb +34 -0
  316. data/lib/phronomy/persistence_composition/unit4_migration.rb +276 -0
  317. data/lib/phronomy/runtime_composition/agent_defaults.rb +5 -1
  318. data/lib/phronomy/runtime_composition/configuration.rb +146 -0
  319. data/lib/phronomy/runtime_composition/evaluation_defaults.rb +7 -0
  320. data/lib/phronomy/runtime_composition/execution_defaults.rb +5 -0
  321. data/lib/phronomy/runtime_composition/generation_defaults.rb +6 -0
  322. data/lib/phronomy/runtime_composition/global_configuration.rb +38 -0
  323. data/lib/phronomy/runtime_composition/multi_agent_defaults.rb +9 -0
  324. data/lib/phronomy/runtime_composition/workflow_defaults.rb +4 -0
  325. data/lib/phronomy/storage/async/async_client.rb +60 -0
  326. data/lib/phronomy/storage/backend.rb +8 -2
  327. data/lib/phronomy/storage/condition_failed_error.rb +2 -2
  328. data/lib/phronomy/storage/unique_constraint_error.rb +2 -2
  329. data/lib/phronomy/testing/eval/metrics.rb +1 -1
  330. data/lib/phronomy/testing/eval/scorer/llm_judge.rb +19 -3
  331. data/lib/phronomy/testing/persistence_contract/a_content_store.rb +2 -2
  332. data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +6 -6
  333. data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +55 -52
  334. data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +3 -3
  335. data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +7 -7
  336. data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +11 -11
  337. data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +57 -51
  338. data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +1 -1
  339. data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +23 -22
  340. data/lib/phronomy/tool/authorization.rb +77 -0
  341. data/lib/phronomy/tool/base.rb +551 -9
  342. data/lib/phronomy/tool/call_request.rb +40 -0
  343. data/lib/phronomy/{agent/tool_execution/tool_definition_set.rb → tool/definition_set.rb} +7 -8
  344. data/lib/phronomy/tool/operation.rb +88 -0
  345. data/lib/phronomy/tool/schema.rb +214 -0
  346. data/lib/phronomy/tool/settings.rb +31 -0
  347. data/lib/phronomy/tool/tool_error.rb +7 -0
  348. data/lib/phronomy/tool/tool_executor.rb +58 -0
  349. data/lib/phronomy/tools/agent.rb +22 -90
  350. data/lib/phronomy/tools/mcp.rb +21 -14
  351. data/lib/phronomy/tools/vector_search.rb +4 -4
  352. data/lib/phronomy/tracing/automatic.rb +11 -6
  353. data/lib/phronomy/tracing/base.rb +36 -7
  354. data/lib/phronomy/tracing/observation.rb +37 -0
  355. data/lib/phronomy/tracing/open_telemetry_tracer.rb +4 -29
  356. data/lib/phronomy/tracing/settings.rb +36 -0
  357. data/lib/phronomy/vector_store/{async_backend.rb → async/async_client.rb} +35 -29
  358. data/lib/phronomy/vector_store/{in_memory.rb → backends/in_memory.rb} +12 -10
  359. data/lib/phronomy/vector_store/{pgvector.rb → backends/pgvector.rb} +44 -16
  360. data/lib/phronomy/vector_store/{redis_search.rb → backends/redis_search.rb} +74 -37
  361. data/lib/phronomy/vector_store/base.rb +118 -49
  362. data/lib/phronomy/vector_store/invalid_result_error.rb +12 -0
  363. data/lib/phronomy/vector_store.rb +2 -17
  364. data/lib/phronomy/version.rb +1 -1
  365. data/lib/phronomy/workflow/completion.rb +11 -0
  366. data/lib/phronomy/workflow/execution/workflow.rb +37 -9
  367. data/lib/phronomy/workflow/execution/workflow_action_rules.rb +67 -0
  368. data/lib/phronomy/workflow/execution/workflow_context.rb +2 -2
  369. data/lib/phronomy/workflow/execution/workflow_execution_environment.rb +28 -0
  370. data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +77 -8
  371. data/lib/phronomy/workflow/execution/workflow_runner.rb +105 -92
  372. data/lib/phronomy/workflow/execution/workflow_settings.rb +28 -0
  373. data/lib/phronomy/workflow/execution/workflow_task_execution.rb +153 -0
  374. data/lib/phronomy/workflow/execution/workflow_task_reception.rb +72 -0
  375. data/lib/phronomy/workflow/execution/workflow_task_resume_error.rb +14 -0
  376. data/lib/phronomy/workflow/persistence/state_repository.rb +28 -22
  377. data/lib/phronomy/workflow/persistence/storage_schema.rb +13 -0
  378. data/lib/phronomy/workflow/runtime_binding/workflow_engine_environment.rb +61 -0
  379. data/lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb +158 -0
  380. data/lib/phronomy/workflow/runtime_binding/workflow_task_session.rb +76 -0
  381. data/lib/phronomy/workflow/{execution → runtime_binding}/workflow_terminal_policy.rb +4 -4
  382. data/lib/phronomy.rb +63 -16
  383. data/scripts/api_snapshot.rb +47 -15
  384. data/scripts/check_readme_ruby.rb +24 -29
  385. data/scripts/check_readme_runnable.rb +92 -81
  386. data/sig/_private/phronomy/agent/settings.rbs +19 -0
  387. data/sig/_private/phronomy/configuration/runtime_settings.rbs +20 -0
  388. data/sig/_private/phronomy/llm_adapter/async/async_client.rbs +10 -0
  389. data/sig/_private/phronomy/multi_agent/store.rbs +8 -0
  390. data/sig/_private/phronomy/persistence/storage_boundary.rbs +7 -0
  391. data/sig/_private/phronomy/tool/settings.rbs +13 -0
  392. data/sig/_private/phronomy/tracing/settings.rbs +15 -0
  393. data/sig/_private/phronomy/workflow/execution/workflow_settings.rbs +13 -0
  394. data/sig/phronomy/agent/admission.rbs +19 -0
  395. data/sig/phronomy/agent/agent_not_found_error.rbs +6 -0
  396. data/sig/phronomy/agent/agent_unloaded_error.rbs +6 -0
  397. data/sig/phronomy/agent/execution_contract.rbs +132 -0
  398. data/sig/phronomy/agent/execution_environment.rbs +20 -0
  399. data/sig/phronomy/agent/store.rbs +73 -0
  400. data/sig/phronomy/agent/stream_callback_error.rbs +8 -0
  401. data/sig/phronomy/agent.rbs +29 -135
  402. data/sig/phronomy/common.rbs +7 -0
  403. data/sig/phronomy/content_store/contracts.rbs +13 -0
  404. data/sig/phronomy/context.rbs +152 -0
  405. data/sig/phronomy/embeddings/async/async_client.rbs +9 -0
  406. data/sig/phronomy/embeddings/base.rbs +23 -0
  407. data/sig/phronomy/engine/concurrency/timer_bindings.rbs +22 -0
  408. data/sig/phronomy/engine/control_bindings.rbs +11 -0
  409. data/sig/phronomy/engine/control_protocols.rbs +23 -0
  410. data/sig/phronomy/engine/execution_binding.rbs +6 -0
  411. data/sig/phronomy/engine/execution_receiver.rbs +15 -0
  412. data/sig/phronomy/engine/observability.rbs +13 -0
  413. data/sig/phronomy/execution/async_operation.rbs +7 -0
  414. data/sig/phronomy/execution/blocking.rbs +5 -0
  415. data/sig/phronomy/execution/concurrency/cancellation_token.rbs +13 -0
  416. data/sig/phronomy/execution/concurrency/deadline.rbs +10 -0
  417. data/sig/phronomy/execution/concurrency/result_composition.rbs +7 -0
  418. data/sig/phronomy/execution/control_protocols.rbs +18 -0
  419. data/sig/phronomy/execution/errors.rbs +7 -0
  420. data/sig/phronomy/execution/execution.rbs +41 -0
  421. data/sig/phronomy/execution/execution_receiver.rbs +40 -0
  422. data/sig/phronomy/execution/execution_scope_state.rbs +9 -0
  423. data/sig/phronomy/execution/invocation_context.rbs +18 -0
  424. data/sig/phronomy/execution/invocation_controls.rbs +13 -0
  425. data/sig/phronomy/execution/task_result.rbs +29 -0
  426. data/sig/phronomy/extensions.rbs +1 -21
  427. data/sig/phronomy/filter/filter_block_error.rbs +6 -0
  428. data/sig/phronomy/generation/low_confidence_error.rbs +6 -0
  429. data/sig/phronomy/handoff.rbs +10 -4
  430. data/sig/phronomy/integrations.rbs +14 -0
  431. data/sig/phronomy/llm_adapter/backends/ruby_llm.rbs +6 -0
  432. data/sig/phronomy/llm_adapter/base.rbs +14 -0
  433. data/sig/phronomy/llm_adapter/usage.rbs +14 -0
  434. data/sig/phronomy/llm_adapter/values.rbs +43 -0
  435. data/sig/phronomy/llm_context_window.rbs +10 -0
  436. data/sig/phronomy/multi_agent/reserved_child_admission.rbs +8 -0
  437. data/sig/phronomy/multi_agent/store.rbs +54 -0
  438. data/sig/phronomy/multi_agent/team_busy_error.rbs +8 -0
  439. data/sig/phronomy/multi_agent/team_not_found_error.rbs +8 -0
  440. data/sig/phronomy/multi_agent.rbs +6 -3
  441. data/sig/phronomy/persistence/errors.rbs +18 -0
  442. data/sig/phronomy/persistence/migration.rbs +13 -0
  443. data/sig/phronomy/persistence/transaction.rbs +19 -0
  444. data/sig/phronomy/persistence.rbs +1 -99
  445. data/sig/phronomy/persistence_composition.rbs +19 -0
  446. data/sig/phronomy/runtime_composition/configuration.rbs +36 -0
  447. data/sig/phronomy/storage/async/async_client.rbs +8 -0
  448. data/sig/phronomy/storage/backends/in_memory.rbs +9 -0
  449. data/sig/phronomy/{storage.rbs → storage/contracts.rbs} +4 -18
  450. data/sig/phronomy/tool.rbs +48 -4
  451. data/sig/phronomy/tracing/observation.rbs +7 -0
  452. data/sig/phronomy/vector_store/async/async_client.rbs +13 -0
  453. data/sig/phronomy/vector_store/base.rbs +23 -0
  454. data/sig/phronomy/workflow/execution/workflow_execution_environment.rbs +15 -0
  455. data/sig/phronomy/workflow/runtime_binding/workflow_terminal_policy.rbs +7 -0
  456. data/sig/phronomy/workflow/store.rbs +8 -0
  457. data/sig/phronomy/workflow.rbs +5 -1
  458. data/sig/phronomy.rbs +4 -49
  459. metadata +365 -117
  460. data/lib/phronomy/agent/concerns/before_llm_input.rb +0 -66
  461. data/lib/phronomy/agent/concerns/error_translation.rb +0 -45
  462. data/lib/phronomy/agent/concerns/filterable.rb +0 -184
  463. data/lib/phronomy/agent/context/capability/base.rb +0 -548
  464. data/lib/phronomy/agent/context/capability/tool_executor.rb +0 -62
  465. data/lib/phronomy/agent/context/instruction/prompt_template.rb +0 -102
  466. data/lib/phronomy/agent/context_assembly/runtime_chat_builder.rb +0 -36
  467. data/lib/phronomy/agent/context_assembly/token_budget_resolver.rb +0 -22
  468. data/lib/phronomy/agent/context_parts/validators/final_budget_validator.rb +0 -37
  469. data/lib/phronomy/agent/context_policies/default.rb +0 -142
  470. data/lib/phronomy/agent/execution/phase_machine_builder.rb +0 -101
  471. data/lib/phronomy/agent/execution/provider_call_outcome.rb +0 -106
  472. data/lib/phronomy/agent/handoff/handoff_execution_coordinator.rb +0 -15
  473. data/lib/phronomy/agent/handoff/handoff_outcome_committer.rb +0 -131
  474. data/lib/phronomy/agent/handoff/handoff_request.rb +0 -45
  475. data/lib/phronomy/agent/lifecycle_contract/handoff_error.rb +0 -7
  476. data/lib/phronomy/agent/persistence/handoff_state_repository.rb +0 -58
  477. data/lib/phronomy/agent/persistence/queries.rb +0 -61
  478. data/lib/phronomy/agent/selection/candidate.rb +0 -53
  479. data/lib/phronomy/agent/selection/constraint.rb +0 -49
  480. data/lib/phronomy/agent/tool_execution/tool_call_intercepted.rb +0 -29
  481. data/lib/phronomy/agent/tool_execution/tool_invocation_session_builder.rb +0 -276
  482. data/lib/phronomy/configuration/configuration.rb +0 -68
  483. data/lib/phronomy/configuration/global_configuration.rb +0 -26
  484. data/lib/phronomy/engine/concurrency/cancellation_scope.rb +0 -124
  485. data/lib/phronomy/engine/concurrency/operation_binding.rb +0 -43
  486. data/lib/phronomy/engine/execution_receiver.rb +0 -65
  487. data/lib/phronomy/engine/runnable.rb +0 -54
  488. data/lib/phronomy/llm_adapter/ruby_llm.rb +0 -47
  489. data/lib/phronomy/llm_contract/token_usage.rb +0 -85
  490. data/lib/phronomy/multi_agent/persistence/queries.rb +0 -30
  491. data/lib/phronomy/multi_agent/storage_contract/team_storage_schema.rb +0 -15
  492. data/lib/phronomy/output_parser/contract/parse_error.rb +0 -7
  493. data/lib/phronomy/persistence/api/persistence.rb +0 -140
  494. data/lib/phronomy/persistence_composition/repositories.rb +0 -77
  495. data/lib/phronomy/recovery/recovery.rb +0 -186
  496. data/lib/phronomy/tool/contract/tool_error.rb +0 -7
  497. data/lib/phronomy/vector_store/embeddings/base.rb +0 -44
  498. data/lib/phronomy/vector_store/embeddings/ruby_llm_embeddings.rb +0 -47
  499. data/lib/phronomy/workflow/phase_machine_builder.rb +0 -205
  500. data/lib/phronomy/workflow/storage_contract/workflow_storage_schema.rb +0 -9
  501. data/sig/phronomy/execution_receiver.rbs +0 -34
  502. data/sig/phronomy/llm_adapter.rbs +0 -11
  503. data/sig/phronomy/runtime.rbs +0 -76
  504. /data/lib/phronomy/agent/{context_assembly/context_importer.rb → context_importer.rb} +0 -0
  505. /data/lib/phronomy/{engine → execution}/backpressure_error.rb +0 -0
  506. /data/lib/phronomy/{engine → execution}/cancellation_error.rb +0 -0
  507. /data/lib/phronomy/{engine → execution}/concurrency/worker_input_restricted.rb +0 -0
  508. /data/lib/phronomy/{engine → execution}/event_loop_reentrancy_error.rb +0 -0
  509. /data/lib/phronomy/{engine → execution}/invalid_async_entry_action_error.rb +0 -0
  510. /data/lib/phronomy/{engine → execution}/invalid_async_transition_action_error.rb +0 -0
  511. /data/lib/phronomy/{engine → execution}/invalid_async_workflow_action_error.rb +0 -0
  512. /data/lib/phronomy/{engine → execution}/pool_shutdown_error.rb +0 -0
  513. /data/lib/phronomy/{engine → execution}/recursion_limit_error.rb +0 -0
  514. /data/lib/phronomy/{engine → execution}/runtime_shutdown_error.rb +0 -0
  515. /data/lib/phronomy/{engine → execution}/runtime_shutdown_reentrancy_error.rb +0 -0
  516. /data/lib/phronomy/{engine → execution}/scheduler_reentrancy_error.rb +0 -0
  517. /data/lib/phronomy/{engine → execution}/timeout_error.rb +0 -0
@@ -38,7 +38,7 @@ module Phronomy
38
38
  end
39
39
 
40
40
  def prepare_plan(execution)
41
- root = agent.agent_root
41
+ root = agent.send(:__root_for_operation)
42
42
  manifest_ref = execution.metadata["manifest_ref"]
43
43
  base_ref = execution.metadata["base_manifest_ref"] ||
44
44
  manifest_ref
@@ -70,7 +70,7 @@ module Phronomy
70
70
 
71
71
  def classify(execution)
72
72
  if framework_batch?(execution)
73
- return Phronomy::Recovery::Classification.new(disposition: Phronomy::Recovery::RESUMABLE, reason: :framework_tools)
73
+ return Phronomy::Agent::RecoveryRules::Classification.new(disposition: Phronomy::Agent::RecoveryRules::RESUMABLE, reason: :framework_tools)
74
74
  end
75
75
  if execution.status == :preparing &&
76
76
  execution.phase.to_sym == :preparing
@@ -79,16 +79,16 @@ module Phronomy
79
79
  "execution #{execution.execution_id} is :preparing but replay safety " \
80
80
  "was not durably established"
81
81
  end
82
- return Phronomy::Recovery::Classification.new(
83
- disposition: Phronomy::Recovery::RESUMABLE,
82
+ return Phronomy::Agent::RecoveryRules::Classification.new(
83
+ disposition: Phronomy::Agent::RecoveryRules::RESUMABLE,
84
84
  reason: :initial_preparation
85
85
  )
86
86
  end
87
87
 
88
88
  if execution.status == :suspended &&
89
89
  execution.phase.to_sym == :approval
90
- return Phronomy::Recovery::Classification.new(
91
- disposition: Phronomy::Recovery::RESUMABLE,
90
+ return Phronomy::Agent::RecoveryRules::Classification.new(
91
+ disposition: Phronomy::Agent::RecoveryRules::RESUMABLE,
92
92
  reason: :approval_wait,
93
93
  facts: {
94
94
  approval_request_id:
@@ -105,8 +105,8 @@ module Phronomy
105
105
  when :recovery_provider_completed,
106
106
  :recovery_tools_completed,
107
107
  :recovery_resolved_failed
108
- return Phronomy::Recovery::Classification.new(
109
- disposition: Phronomy::Recovery::RESUMABLE,
108
+ return Phronomy::Agent::RecoveryRules::Classification.new(
109
+ disposition: Phronomy::Agent::RecoveryRules::RESUMABLE,
110
110
  reason: execution.phase
111
111
  )
112
112
  when :resuming
@@ -116,8 +116,8 @@ module Phronomy
116
116
  execution.approval_request[:approved]
117
117
  )
118
118
  unless approved
119
- return Phronomy::Recovery::Classification.new(
120
- disposition: Phronomy::Recovery::RESUMABLE,
119
+ return Phronomy::Agent::RecoveryRules::Classification.new(
120
+ disposition: Phronomy::Agent::RecoveryRules::RESUMABLE,
121
121
  reason: :approval_rejection_committed
122
122
  )
123
123
  end
@@ -127,8 +127,8 @@ module Phronomy
127
127
  execution
128
128
  )
129
129
  if descriptor
130
- return Phronomy::Recovery::Classification.new(
131
- disposition: Phronomy::Recovery::RESOLUTION_REQUIRED,
130
+ return Phronomy::Agent::RecoveryRules::Classification.new(
131
+ disposition: Phronomy::Agent::RecoveryRules::RESOLUTION_REQUIRED,
132
132
  reason: descriptor.fetch(:reason),
133
133
  subject: descriptor.fetch(:subject),
134
134
  allowed_outcomes:
@@ -143,20 +143,20 @@ module Phronomy
143
143
  end
144
144
 
145
145
  def install_on_event_loop(command)
146
- event_loop = @runtime.event_loop
146
+ environment = @environment
147
147
  plan = command.plan
148
148
  execution = plan.execution
149
- main = agent.send(:execution_coordinator_for, agent.__coordination_config)
149
+ main = agent.send(:execution_coordinator_for, agent.__execution_wiring)
150
150
  admitted = false
151
151
  bound = false
152
152
  installed = false
153
153
 
154
- Phronomy::Agent::ExecutionRegistry.for(event_loop).admit_agent_execution(
154
+ environment.registry.admit_agent_execution(
155
155
  agent.agent_id,
156
156
  owner_token: command.owner_token
157
157
  )
158
158
  admitted = true
159
- Phronomy::Agent::ExecutionRegistry.for(event_loop).bind_agent_execution_admission(
159
+ environment.registry.bind_agent_execution_admission(
160
160
  agent.agent_id,
161
161
  owner_token: command.owner_token,
162
162
  execution_id: execution.execution_id
@@ -167,11 +167,11 @@ module Phronomy
167
167
  if execution.status == :suspended || execution.phase.to_sym == :resuming
168
168
  invocation = InvocationRestorer.build_invocation_for_suspended(
169
169
  agent, execution, plan.projection, main, agent.send(:_phronomy_event_listener),
170
- assistant_message: plan.material.assistant_message
170
+ assistant_message: plan.material.assistant_message, cancellation_requested: plan.material.cancellation_requested
171
171
  )
172
172
  end
173
173
 
174
- Phronomy::Agent::ExecutionRegistry.for(event_loop).install_agent_execution(
174
+ environment.registry.install_agent_execution(
175
175
  execution_id: execution.execution_id,
176
176
  agent: agent,
177
177
  coordinator: main,
@@ -184,7 +184,7 @@ module Phronomy
184
184
  installed = true
185
185
 
186
186
  if execution.status == :suspended
187
- Phronomy::Agent::ExecutionRegistry.for(event_loop).mark_agent_execution_admission(
187
+ environment.registry.mark_agent_execution_admission(
188
188
  agent.agent_id,
189
189
  execution_id: execution.execution_id,
190
190
  state: :suspended
@@ -209,13 +209,13 @@ module Phronomy
209
209
  }
210
210
  )
211
211
  end
212
- command.completion.complete(agent)
212
+ agent.send(:_complete_result_task, command.completion, agent)
213
213
  return
214
214
  end
215
215
 
216
216
  case plan.classification.disposition
217
- when Phronomy::Recovery::RESOLUTION_REQUIRED
218
- Phronomy::Agent::ExecutionRegistry.for(event_loop).mark_agent_execution_admission(
217
+ when Phronomy::Agent::RecoveryRules::RESOLUTION_REQUIRED
218
+ environment.registry.mark_agent_execution_admission(
219
219
  agent.agent_id,
220
220
  execution_id: execution.execution_id,
221
221
  state: :recovery_required
@@ -246,8 +246,8 @@ module Phronomy
246
246
  result: payload
247
247
  )
248
248
  end
249
- command.completion.complete(agent)
250
- when Phronomy::Recovery::RESUMABLE
249
+ agent.send(:_complete_result_task, command.completion, agent)
250
+ when Phronomy::Agent::RecoveryRules::RESUMABLE
251
251
  continue_resumable_on_event_loop(
252
252
  execution,
253
253
  completion: command.completion, material: plan.material
@@ -260,7 +260,7 @@ module Phronomy
260
260
  # simplecov:disable
261
261
  if installed
262
262
  begin
263
- Phronomy::Agent::ExecutionRegistry.for(event_loop).release_agent_execution(
263
+ environment.registry.release_agent_execution(
264
264
  execution.execution_id
265
265
  )
266
266
  rescue
@@ -270,7 +270,7 @@ module Phronomy
270
270
  if admitted
271
271
  if bound
272
272
  begin
273
- Phronomy::Agent::ExecutionRegistry.for(event_loop).release_agent_execution_admission(
273
+ environment.registry.release_agent_execution_admission(
274
274
  agent.agent_id,
275
275
  execution_id: execution&.execution_id
276
276
  )
@@ -279,7 +279,7 @@ module Phronomy
279
279
  end
280
280
  else
281
281
  begin
282
- Phronomy::Agent::ExecutionRegistry.for(event_loop).release_agent_execution_admission(
282
+ environment.registry.release_agent_execution_admission(
283
283
  agent.agent_id,
284
284
  owner_token: command.owner_token
285
285
  )
@@ -288,7 +288,7 @@ module Phronomy
288
288
  end
289
289
  end
290
290
  end
291
- command.completion.fail(caught)
291
+ agent.send(:_fail_result_task, command.completion, caught)
292
292
  # simplecov:enable
293
293
  end
294
294
 
@@ -311,13 +311,13 @@ module Phronomy
311
311
  completion:, material:
312
312
  )
313
313
  # simplecov:disable
314
- event_loop = @runtime.event_loop
315
- main = agent.send(:execution_coordinator_for, agent.__coordination_config)
314
+ environment = @environment
315
+ main = agent.send(:execution_coordinator_for, agent.__execution_wiring)
316
316
 
317
317
  if framework_batch?(execution)
318
318
  internal = Phronomy::TaskResult.deferred(name: "framework-tool-recovery:#{execution.execution_id}")
319
319
  continue_recovery_on_event_loop(execution, internal, material: material)
320
- completion.complete(agent)
320
+ agent.send(:_complete_result_task, completion, agent)
321
321
  return
322
322
  end
323
323
 
@@ -348,7 +348,7 @@ module Phronomy
348
348
  name: "agent-recovery-auto:#{execution.execution_id}"
349
349
  )
350
350
  observe_recovery_execution(internal_task, execution)
351
- state = ExecutionRegistry.for(event_loop).agent_execution_state(execution.execution_id)
351
+ state = environment.registry.agent_execution_state(execution.execution_id)
352
352
  main.deliver_on_event_loop(ExecutionCoordinator::ContinueRecoveredCommand.new(
353
353
  coordinator: main,
354
354
  execution_id: execution.execution_id,
@@ -359,11 +359,11 @@ module Phronomy
359
359
  result_task: internal_task,
360
360
  error: nil
361
361
  ))
362
- completion.complete(agent)
362
+ agent.send(:_complete_result_task, completion, agent)
363
363
  when :recovery_tools_completed, :recovery_provider_completed, :recovery_resolved_failed
364
364
  internal_task = Phronomy::TaskResult.deferred(name: "agent-recovery-auto:#{execution.execution_id}")
365
365
  continue_recovery_on_event_loop(execution, internal_task, material: material)
366
- completion.complete(agent)
366
+ agent.send(:_complete_result_task, completion, agent)
367
367
  else
368
368
  raise Phronomy::ExecutionRehydrationRequiredError,
369
369
  "no automatic continuation for #{execution.phase.inspect}"
@@ -9,76 +9,71 @@ module Phronomy
9
9
  private
10
10
 
11
11
  def begin_resolve_on_event_loop(request)
12
- event_loop = @runtime.event_loop
13
- state = Phronomy::Agent::ExecutionRegistry.for(event_loop).agent_execution_state(
12
+ environment = @environment
13
+ state = environment.registry.agent_execution_state(
14
14
  request.execution_id
15
15
  )
16
16
  unless state && state.agent.equal?(agent)
17
- request.completion.fail(
17
+ agent.send(:_fail_result_task, request.completion,
18
18
  Phronomy::ExecutionRehydrationRequiredError.new(
19
19
  "no live recovered execution #{request.execution_id}"
20
- )
21
- )
20
+ ))
22
21
  return
23
22
  end
24
23
 
25
24
  current = state.execution
26
25
  unless current.execution_revision ==
27
26
  request.expected_execution_revision
28
- request.completion.fail(
29
- Phronomy::Storage::ConflictError.new(
27
+ agent.send(:_fail_result_task, request.completion,
28
+ Phronomy::Persistence::StateConflictError.new(
30
29
  "Recovery resolution revision conflict: expected " \
31
30
  "#{request.expected_execution_revision}, actual " \
32
31
  "#{current.execution_revision}"
33
- )
34
- )
32
+ ))
35
33
  return
36
34
  end
37
35
 
38
36
  descriptor =
39
37
  coordination_recovery_descriptor(current)
40
38
  unless descriptor &&
41
- Phronomy::Recovery.subject_equal?(
39
+ Phronomy::Agent::RecoveryRules.subject_equal?(
42
40
  descriptor.fetch(:subject),
43
41
  request.subject
44
42
  )
45
- request.completion.fail(
43
+ agent.send(:_fail_result_task, request.completion,
46
44
  ArgumentError.new(
47
45
  "Recovery subject is not current for execution #{request.execution_id}"
48
- )
49
- )
46
+ ))
50
47
  return
51
48
  end
52
49
 
53
50
  unless Array(
54
51
  descriptor.fetch(:allowed_outcomes)
55
52
  ).map(&:to_sym).include?(request.outcome)
56
- request.completion.fail(
53
+ agent.send(:_fail_result_task, request.completion,
57
54
  ArgumentError.new(
58
55
  "Recovery outcome #{request.outcome.inspect} is not allowed for the current subject"
59
- )
60
- )
56
+ ))
61
57
  return
62
58
  end
63
59
 
64
60
  operation = ResolutionOperation.new(
65
61
  execution: current,
66
- root: agent.agent_root,
62
+ root: agent.send(:__root_for_operation),
67
63
  subject: request.subject,
68
64
  outcome: request.outcome,
69
65
  result: request.result,
70
66
  failure: request.failure
71
67
  )
72
- Phronomy::Agent::ExecutionRegistry.for(event_loop).mark_agent_execution_admission(
68
+ environment.registry.mark_agent_execution_admission(
73
69
  agent.agent_id,
74
70
  execution_id: current.execution_id,
75
71
  state: :recovery_required
76
72
  )
77
- task = @runtime.offload.submit(
78
- on_full: :raise
79
- ) do
73
+ task = @environment.submit(on_full: :raise) do
80
74
  prepare_resolution(operation)
81
75
  end
76
+ request.completion.track(task) if request.completion.respond_to?(:track)
82
77
  task.on_complete do |result, error|
83
78
  ready = ResolveReady.new(
84
79
  coordinator: self,
@@ -88,15 +83,14 @@ module Phronomy
88
83
  error: error
89
84
  )
90
85
  unless post_control(ready)
91
- request.completion.fail(
86
+ agent.send(:_fail_result_task, request.completion,
92
87
  Phronomy::RuntimeShutdownError.new(
93
88
  "EventLoop rejected Recovery resolution apply"
94
- )
95
- )
89
+ ))
96
90
  end
97
91
  end
98
92
  rescue => caught
99
- request.completion.fail(caught)
93
+ agent.send(:_fail_result_task, request.completion, caught)
100
94
  end
101
95
 
102
96
  def perform_resolution(operation)
@@ -118,7 +112,7 @@ module Phronomy
118
112
  unless current.metadata[
119
113
  ExecutionMetadata::PENDING_LLM_ID_KEY
120
114
  ].to_s == llm_call_id
121
- raise Phronomy::Storage::ConflictError,
115
+ raise Phronomy::Persistence::StateConflictError,
122
116
  "LLM Recovery subject is no longer pending"
123
117
  end
124
118
 
@@ -165,7 +159,7 @@ module Phronomy
165
159
  when :succeeded
166
160
  # simplecov:disable
167
161
  outcome =
168
- Phronomy::Agent::ProviderCallOutcome.from_h(
162
+ Phronomy::LLMAdapter::Response.from_h(
169
163
  operation.result
170
164
  )
171
165
  updated = nil
@@ -267,7 +261,7 @@ module Phronomy
267
261
  recovery = RecoverySupport.build_recovery_hash(subjects)
268
262
  end
269
263
  unless recovery
270
- raise Phronomy::Storage::ConflictError,
264
+ raise Phronomy::Persistence::StateConflictError,
271
265
  "Tool Recovery state is missing"
272
266
  end
273
267
 
@@ -280,7 +274,7 @@ module Phronomy
280
274
  hash.fetch("state", "unresolved") == "unresolved"
281
275
  end
282
276
  unless subject_entry
283
- raise Phronomy::Storage::ConflictError,
277
+ raise Phronomy::Persistence::StateConflictError,
284
278
  "Tool Recovery subject is no longer unresolved"
285
279
  end
286
280
  subject_entry =
@@ -415,10 +409,10 @@ module Phronomy
415
409
  # simplecov:enable
416
410
 
417
411
  def known_non_f1_error?(error)
418
- error.is_a?(Phronomy::Storage::ConflictError) ||
419
- error.is_a?(Phronomy::Storage::NotFoundError) ||
420
- error.is_a?(Phronomy::Storage::SerializationError) ||
421
- error.is_a?(Phronomy::Storage::UnsupportedBackendError) ||
412
+ error.is_a?(Phronomy::Persistence::ConflictError) ||
413
+ error.is_a?(Phronomy::Persistence::NotFoundError) ||
414
+ error.is_a?(Phronomy::Persistence::SerializationError) ||
415
+ error.is_a?(Phronomy::Persistence::UnsupportedBackendError) ||
422
416
  error.is_a?(ArgumentError) ||
423
417
  error.is_a?(Phronomy::ConfigurationError)
424
418
  end
@@ -474,33 +468,33 @@ module Phronomy
474
468
  if current.to_h == operation.execution.to_h
475
469
  raise(error.is_a?(ResolutionOutcomeUnknownError) ? error.original_error : error)
476
470
  end
477
- raise Phronomy::Storage::ConflictError,
471
+ raise Phronomy::Persistence::StateConflictError,
478
472
  "Recovery resolution durable outcome conflicts with both expected pre-state and intended post-state"
479
473
  end
480
474
 
481
475
  def apply_resolve_on_event_loop(ready)
482
476
  request = ready.request
483
- event_loop = @runtime.event_loop
484
- state = Phronomy::Agent::ExecutionRegistry.for(event_loop).agent_execution_state(request.execution_id)
477
+ environment = @environment
478
+ state = environment.registry.agent_execution_state(request.execution_id)
485
479
  unless state && state.agent.equal?(agent)
486
480
  raise Phronomy::ExecutionRehydrationRequiredError,
487
481
  "recovered execution disappeared before resolution apply"
488
482
  end
489
483
  unless state.execution.equal?(ready.operation.execution) && state.fsm_session_id.nil?
490
- raise Phronomy::Storage::ConflictError,
484
+ raise Phronomy::Persistence::StateConflictError,
491
485
  "Recovery execution changed before resolution apply"
492
486
  end
493
487
  raise ready.error if ready.error
494
488
 
495
489
  result = ready.result
496
490
  if result.execution
497
- Phronomy::Agent::ExecutionRegistry.for(event_loop).replace_agent_execution(request.execution_id, execution: result.execution)
491
+ environment.registry.replace_agent_execution(request.execution_id, execution: result.execution)
498
492
  end
499
493
  raise result.error if result.error
500
494
 
501
495
  continue_recovery_on_event_loop(result.execution, request.completion, material: result.material)
502
496
  rescue => caught
503
- request.completion.fail(caught)
497
+ agent.send(:_fail_result_task, request.completion, caught)
504
498
  end
505
499
  end
506
500
  end
@@ -7,7 +7,7 @@ module Phronomy
7
7
  # Durable Agent Recovery owner/resolver.
8
8
  #
9
9
  # This class owns Agent-specific restart hydration and factual resolution.
10
- # Shared Recovery semantics remain in Phronomy::Recovery.
10
+ # Agent recovery semantics remain in RecoveryRules.
11
11
  # @api private
12
12
  class RecoveryCoordinator
13
13
  class ResolutionOutcomeUnknownError < Phronomy::Error
@@ -39,7 +39,7 @@ module Phronomy
39
39
  )
40
40
  ResolutionResult = Data.define(:execution)
41
41
  ResolutionPreparation = Data.define(:execution, :material, :error)
42
- RecoveryMaterial = Data.define(:projection, :messages, :assistant_message, :output, :usage)
42
+ RecoveryMaterial = Data.define(:projection, :messages, :assistant_message, :output, :usage, :cancellation_requested)
43
43
  RecoveryPlan = Data.define(
44
44
  :execution, :root, :manifest, :base_manifest,
45
45
  :projection, :classification, :material
@@ -49,7 +49,7 @@ module Phronomy
49
49
 
50
50
  def initialize(agent)
51
51
  @agent = agent
52
- @runtime = Phronomy::Runtime.instance
52
+ @environment = agent.__execution_environment
53
53
  end
54
54
 
55
55
  def recover_on_load!
@@ -57,26 +57,33 @@ module Phronomy
57
57
  Array(tx.executions.list_active(agent.agent_id))
58
58
  end
59
59
  if active.length > 1
60
- raise Phronomy::Storage::ConflictError,
60
+ raise Phronomy::Persistence::StateConflictError,
61
61
  "multiple active AgentExecutions exist for #{agent.agent_id}"
62
62
  end
63
63
  return agent if active.empty?
64
64
 
65
65
  execution = active.first
66
66
  unless execution.agent_id.to_s == agent.agent_id.to_s
67
- raise Phronomy::Storage::ConflictError,
67
+ raise Phronomy::Persistence::StateConflictError,
68
68
  "active AgentExecution belongs to another Agent: #{execution.agent_id}"
69
69
  end
70
70
 
71
- coordination = execution.metadata["coordination"]
72
- if coordination && agent.__coordination_config.empty?
71
+ coordination = execution.metadata["reservation"] || execution.metadata["execution_extension"]
72
+ if coordination && agent.__execution_wiring.empty?
73
73
  return agent
74
74
  end
75
75
 
76
+ if (raw = execution.metadata["execution_extension"])
77
+ extension = ExecutionExtensionState.from_h(raw)
78
+ binding = agent.__execution_wiring[:phronomy_execution_participant]&.binding
79
+ unless binding && binding.binding_key == extension.binding_key && binding.binding_version == extension.binding_version
80
+ raise Phronomy::ExecutionRehydrationRequiredError, "Execution #{execution.execution_id} needs its current participant binding"
81
+ end
82
+ end
76
83
  plan = prepare_plan(execution)
77
84
  classification = plan.classification
78
85
  if classification.disposition ==
79
- Phronomy::Recovery::RESOLUTION_REQUIRED &&
86
+ Phronomy::Agent::RecoveryRules::RESOLUTION_REQUIRED &&
80
87
  !agent.send(:_phronomy_event_listener)
81
88
  raise Phronomy::ConfigurationError,
82
89
  "Agent #{agent.agent_id.inspect} requires Recovery resolution; " \
@@ -114,21 +121,21 @@ module Phronomy
114
121
  expected_execution_revision:,
115
122
  subject:,
116
123
  outcome:,
117
- result: Phronomy::Recovery::MISSING,
118
- error: Phronomy::Recovery::MISSING
124
+ result: Phronomy::Agent::RecoveryRules::MISSING,
125
+ error: Phronomy::Agent::RecoveryRules::MISSING
119
126
  )
120
127
  agent.send(:__assert_live_agent!)
121
- normalized_outcome = Phronomy::Recovery.normalize_outcome(outcome)
122
- result_present = !result.equal?(Phronomy::Recovery::MISSING)
123
- error_present = !error.equal?(Phronomy::Recovery::MISSING)
124
- Phronomy::Recovery.validate_resolution_material!(
128
+ normalized_outcome = Phronomy::Agent::RecoveryRules.normalize_outcome(outcome)
129
+ result_present = !result.equal?(Phronomy::Agent::RecoveryRules::MISSING)
130
+ error_present = !error.equal?(Phronomy::Agent::RecoveryRules::MISSING)
131
+ Phronomy::Agent::RecoveryRules.validate_resolution_material!(
125
132
  outcome: normalized_outcome,
126
133
  result_present: result_present,
127
134
  error_present: error_present
128
135
  )
129
136
 
130
137
  normalized_subject =
131
- Phronomy::Recovery.normalize_subject(subject)
138
+ Phronomy::Agent::RecoveryRules.normalize_subject(subject)
132
139
  canonical_result = if result_present
133
140
  if normalized_subject[:type] == :llm_call &&
134
141
  normalized_outcome == :succeeded
@@ -140,9 +147,8 @@ module Phronomy
140
147
  failure = error_present ?
141
148
  RecoverySupport.resolution_failure(error) : nil
142
149
 
143
- completion = Phronomy::TaskResult.deferred(
144
- name: "agent-recovery-resolve:#{execution_id}"
145
- )
150
+ completion = OperationResult.new(agent: agent,
151
+ name: "agent-recovery-resolve:#{execution_id}")
146
152
  command = ResolveCommand.new(
147
153
  coordinator: self,
148
154
  execution_id: execution_id.to_s.freeze,
@@ -155,18 +161,15 @@ module Phronomy
155
161
  completion: completion
156
162
  )
157
163
  unless post_control(command)
158
- completion.fail(
159
- Phronomy::RuntimeShutdownError.new(
160
- "EventLoop rejected Agent Recovery resolution"
161
- )
162
- )
164
+ agent.send(:_fail_result_task, completion,
165
+ Phronomy::RuntimeShutdownError.new("EventLoop rejected Agent Recovery resolution"))
163
166
  end
164
167
  completion
165
168
  rescue => caught
166
169
  completion ||= Phronomy::TaskResult.deferred(
167
170
  name: "agent-recovery-resolve:#{execution_id}"
168
171
  )
169
- completion.fail(caught)
172
+ agent.send(:_fail_result_task, completion, caught)
170
173
  completion
171
174
  end
172
175
 
@@ -189,7 +192,7 @@ module Phronomy
189
192
  def post_control(command)
190
193
  admission = command.is_a?(InstallCommand) || command.is_a?(ResolveCommand)
191
194
  completion = admission ? command.completion : command.request.completion
192
- ExecutionRegistry.for(@runtime.event_loop).post(command,
195
+ @environment.registry.post(command,
193
196
  admission: admission, completion: completion)
194
197
  rescue Phronomy::RuntimeShutdownError
195
198
  false
@@ -0,0 +1,119 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Agent
5
+ # Agent recovery subjects, factual resolutions and resumption classifications.
6
+ # Persistence evidence comparison belongs to Persistence::SnapshotComparison.
7
+ # @api private
8
+ module RecoveryRules
9
+ RESUMABLE = :resumable
10
+ RECONCILABLE = :reconcilable
11
+ RESOLUTION_REQUIRED = :resolution_required
12
+ DISPOSITIONS = [RESUMABLE, RECONCILABLE, RESOLUTION_REQUIRED].freeze
13
+
14
+ ALLOWED_OUTCOMES = %i[succeeded failed not_performed].freeze
15
+ # Internal shorthand used by the Agent Recovery implementation. This is not
16
+ # a public API constant; both names refer to the same frozen value.
17
+ OUTCOMES = ALLOWED_OUTCOMES
18
+
19
+ MISSING = Object.new.freeze
20
+
21
+ Classification = Data.define(
22
+ :disposition, :reason, :subject, :allowed_outcomes, :facts
23
+ ) do
24
+ def initialize(
25
+ disposition:,
26
+ reason:,
27
+ subject: nil,
28
+ allowed_outcomes: [],
29
+ facts: {}
30
+ )
31
+ normalized = disposition.to_sym
32
+ unless Phronomy::Agent::RecoveryRules::DISPOSITIONS.include?(normalized)
33
+ raise ArgumentError, "unknown Recovery disposition: #{disposition.inspect}"
34
+ end
35
+ super(
36
+ disposition: normalized,
37
+ reason: reason.to_sym,
38
+ subject: subject && Phronomy::Agent::RecoveryRules.normalize_subject(subject),
39
+ allowed_outcomes: Array(allowed_outcomes).map(&:to_sym).freeze,
40
+ facts: Phronomy::Values::Immutable.copy(facts || {})
41
+ )
42
+ end
43
+ end
44
+
45
+ module_function
46
+
47
+ def normalize_outcome(outcome)
48
+ normalized = outcome.to_sym
49
+ unless OUTCOMES.include?(normalized)
50
+ raise ArgumentError, "unsupported Recovery outcome: #{outcome.inspect}"
51
+ end
52
+ normalized
53
+ end
54
+
55
+ def validate_resolution_material!(outcome:, result_present:, error_present:)
56
+ normalized = normalize_outcome(outcome)
57
+ case normalized
58
+ when :succeeded
59
+ raise ArgumentError, "Recovery :succeeded requires result:" unless result_present
60
+ raise ArgumentError, "Recovery :succeeded forbids error:" if error_present
61
+ when :failed
62
+ raise ArgumentError, "Recovery :failed requires error:" unless error_present
63
+ raise ArgumentError, "Recovery :failed forbids result:" if result_present
64
+ when :not_performed
65
+ if result_present || error_present
66
+ raise ArgumentError, "Recovery :not_performed accepts neither result: nor error:"
67
+ end
68
+ end
69
+ true
70
+ end
71
+
72
+ def normalize_subject(subject)
73
+ hash = subject.to_h.transform_keys(&:to_sym)
74
+ type = hash.fetch(:type).to_sym
75
+ normalized = {type: type}
76
+ case type
77
+ when :llm_call
78
+ normalized[:llm_call_id] = required_id(hash, :llm_call_id)
79
+ when :tool_invocation
80
+ normalized[:tool_invocation_id] = required_id(hash, :tool_invocation_id)
81
+ when :persistence_operation
82
+ # Persistence operation references are structural only. Do not introduce
83
+ # a generic durable operation/recovery identity here.
84
+ normalized[:entity] = hash.fetch(:entity).to_sym
85
+ else
86
+ raise ArgumentError, "unsupported Recovery subject type: #{type.inspect}"
87
+ end
88
+ normalized.freeze
89
+ end
90
+
91
+ def subject_key(subject)
92
+ normalized = normalize_subject(subject)
93
+ case normalized.fetch(:type)
94
+ when :llm_call
95
+ "llm_call:#{normalized.fetch(:llm_call_id)}"
96
+ when :tool_invocation
97
+ "tool_invocation:#{normalized.fetch(:tool_invocation_id)}"
98
+ when :persistence_operation
99
+ "persistence_operation:#{normalized.fetch(:entity)}"
100
+ end
101
+ end
102
+
103
+ def subject_equal?(left, right)
104
+ normalize_subject(left) == normalize_subject(right)
105
+ rescue ArgumentError, KeyError
106
+ false
107
+ end
108
+
109
+ def required_id(hash, key)
110
+ value = hash.fetch(key)
111
+ text = value.to_s
112
+ raise ArgumentError, "#{key} must not be empty" if text.empty?
113
+
114
+ text.freeze
115
+ end
116
+ private_class_method :required_id
117
+ end
118
+ end
119
+ end