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
@@ -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
@@ -214,8 +214,8 @@ module Phronomy
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
@@ -247,7 +247,7 @@ module Phronomy
247
247
  )
248
248
  end
249
249
  command.completion.complete(agent)
250
- when Phronomy::Recovery::RESUMABLE
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
  )
@@ -311,8 +311,8 @@ 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}")
@@ -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,
@@ -9,8 +9,8 @@ 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)
@@ -26,7 +26,7 @@ module Phronomy
26
26
  unless current.execution_revision ==
27
27
  request.expected_execution_revision
28
28
  request.completion.fail(
29
- Phronomy::Storage::ConflictError.new(
29
+ Phronomy::Persistence::StateConflictError.new(
30
30
  "Recovery resolution revision conflict: expected " \
31
31
  "#{request.expected_execution_revision}, actual " \
32
32
  "#{current.execution_revision}"
@@ -38,7 +38,7 @@ module Phronomy
38
38
  descriptor =
39
39
  coordination_recovery_descriptor(current)
40
40
  unless descriptor &&
41
- Phronomy::Recovery.subject_equal?(
41
+ Phronomy::Agent::RecoveryRules.subject_equal?(
42
42
  descriptor.fetch(:subject),
43
43
  request.subject
44
44
  )
@@ -69,14 +69,12 @@ module Phronomy
69
69
  result: request.result,
70
70
  failure: request.failure
71
71
  )
72
- Phronomy::Agent::ExecutionRegistry.for(event_loop).mark_agent_execution_admission(
72
+ environment.registry.mark_agent_execution_admission(
73
73
  agent.agent_id,
74
74
  execution_id: current.execution_id,
75
75
  state: :recovery_required
76
76
  )
77
- task = @runtime.offload.submit(
78
- on_full: :raise
79
- ) do
77
+ task = @environment.submit(on_full: :raise) do
80
78
  prepare_resolution(operation)
81
79
  end
82
80
  task.on_complete do |result, error|
@@ -118,7 +116,7 @@ module Phronomy
118
116
  unless current.metadata[
119
117
  ExecutionMetadata::PENDING_LLM_ID_KEY
120
118
  ].to_s == llm_call_id
121
- raise Phronomy::Storage::ConflictError,
119
+ raise Phronomy::Persistence::StateConflictError,
122
120
  "LLM Recovery subject is no longer pending"
123
121
  end
124
122
 
@@ -165,7 +163,7 @@ module Phronomy
165
163
  when :succeeded
166
164
  # simplecov:disable
167
165
  outcome =
168
- Phronomy::Agent::ProviderCallOutcome.from_h(
166
+ Phronomy::LLMAdapter::Response.from_h(
169
167
  operation.result
170
168
  )
171
169
  updated = nil
@@ -267,7 +265,7 @@ module Phronomy
267
265
  recovery = RecoverySupport.build_recovery_hash(subjects)
268
266
  end
269
267
  unless recovery
270
- raise Phronomy::Storage::ConflictError,
268
+ raise Phronomy::Persistence::StateConflictError,
271
269
  "Tool Recovery state is missing"
272
270
  end
273
271
 
@@ -280,7 +278,7 @@ module Phronomy
280
278
  hash.fetch("state", "unresolved") == "unresolved"
281
279
  end
282
280
  unless subject_entry
283
- raise Phronomy::Storage::ConflictError,
281
+ raise Phronomy::Persistence::StateConflictError,
284
282
  "Tool Recovery subject is no longer unresolved"
285
283
  end
286
284
  subject_entry =
@@ -415,10 +413,10 @@ module Phronomy
415
413
  # simplecov:enable
416
414
 
417
415
  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) ||
416
+ error.is_a?(Phronomy::Persistence::ConflictError) ||
417
+ error.is_a?(Phronomy::Persistence::NotFoundError) ||
418
+ error.is_a?(Phronomy::Persistence::SerializationError) ||
419
+ error.is_a?(Phronomy::Persistence::UnsupportedBackendError) ||
422
420
  error.is_a?(ArgumentError) ||
423
421
  error.is_a?(Phronomy::ConfigurationError)
424
422
  end
@@ -474,27 +472,27 @@ module Phronomy
474
472
  if current.to_h == operation.execution.to_h
475
473
  raise(error.is_a?(ResolutionOutcomeUnknownError) ? error.original_error : error)
476
474
  end
477
- raise Phronomy::Storage::ConflictError,
475
+ raise Phronomy::Persistence::StateConflictError,
478
476
  "Recovery resolution durable outcome conflicts with both expected pre-state and intended post-state"
479
477
  end
480
478
 
481
479
  def apply_resolve_on_event_loop(ready)
482
480
  request = ready.request
483
- event_loop = @runtime.event_loop
484
- state = Phronomy::Agent::ExecutionRegistry.for(event_loop).agent_execution_state(request.execution_id)
481
+ environment = @environment
482
+ state = environment.registry.agent_execution_state(request.execution_id)
485
483
  unless state && state.agent.equal?(agent)
486
484
  raise Phronomy::ExecutionRehydrationRequiredError,
487
485
  "recovered execution disappeared before resolution apply"
488
486
  end
489
487
  unless state.execution.equal?(ready.operation.execution) && state.fsm_session_id.nil?
490
- raise Phronomy::Storage::ConflictError,
488
+ raise Phronomy::Persistence::StateConflictError,
491
489
  "Recovery execution changed before resolution apply"
492
490
  end
493
491
  raise ready.error if ready.error
494
492
 
495
493
  result = ready.result
496
494
  if result.execution
497
- Phronomy::Agent::ExecutionRegistry.for(event_loop).replace_agent_execution(request.execution_id, execution: result.execution)
495
+ environment.registry.replace_agent_execution(request.execution_id, execution: result.execution)
498
496
  end
499
497
  raise result.error if result.error
500
498
 
@@ -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
@@ -189,7 +196,7 @@ module Phronomy
189
196
  def post_control(command)
190
197
  admission = command.is_a?(InstallCommand) || command.is_a?(ResolveCommand)
191
198
  completion = admission ? command.completion : command.request.completion
192
- ExecutionRegistry.for(@runtime.event_loop).post(command,
199
+ @environment.registry.post(command,
193
200
  admission: admission, completion: completion)
194
201
  rescue Phronomy::RuntimeShutdownError
195
202
  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
@@ -15,17 +15,14 @@ module Phronomy
15
15
  end
16
16
 
17
17
  def normalize_provider_outcome(value)
18
- return value if value.is_a?(Phronomy::Agent::ProviderCallOutcome)
18
+ return value if value.is_a?(Phronomy::LLMAdapter::Response)
19
19
 
20
20
  if value.is_a?(Hash)
21
- return Phronomy::Agent::ProviderCallOutcome.from_h(value)
21
+ return Phronomy::LLMAdapter::Response.from_h(value)
22
22
  end
23
23
 
24
- captured = Phronomy::Agent::ProviderCallOutcome.capture(value)
25
- return captured if captured
26
-
27
24
  raise ArgumentError,
28
- "LLM Recovery :succeeded requires a Provider result or ProviderCallOutcome-compatible Hash"
25
+ "LLM Recovery :succeeded requires an LLMAdapter::Response or its canonical Hash"
29
26
  end
30
27
 
31
28
  def resolution_failure(error)
@@ -47,7 +44,7 @@ module Phronomy
47
44
  execution_id: execution.execution_id,
48
45
  execution_revision: execution.execution_revision,
49
46
  reason: descriptor.fetch(:reason),
50
- subject: Phronomy::Recovery.normalize_subject(
47
+ subject: Phronomy::Agent::RecoveryRules.normalize_subject(
51
48
  descriptor.fetch(:subject)
52
49
  ),
53
50
  allowed_outcomes: Array(
@@ -69,7 +66,7 @@ module Phronomy
69
66
  type: :llm_call,
70
67
  llm_call_id: llm_call_id
71
68
  },
72
- allowed_outcomes: Phronomy::Recovery::OUTCOMES,
69
+ allowed_outcomes: Phronomy::Agent::RecoveryRules::OUTCOMES,
73
70
  facts: {
74
71
  manifest_ref: execution.metadata["manifest_ref"],
75
72
  call_sequence: execution.llm_calls.length + 1
@@ -103,7 +100,7 @@ module Phronomy
103
100
  allowed_outcomes: Array(
104
101
  recovery["allowed_outcomes"] ||
105
102
  recovery[:allowed_outcomes] ||
106
- Phronomy::Recovery::OUTCOMES
103
+ Phronomy::Agent::RecoveryRules::OUTCOMES
107
104
  ).map(&:to_sym),
108
105
  facts: {
109
106
  tool_call_id: hash["tool_call_id"],
@@ -145,7 +142,7 @@ module Phronomy
145
142
  type: :tool_invocation,
146
143
  tool_invocation_id: first.fetch("tool_invocation_id")
147
144
  },
148
- allowed_outcomes: Phronomy::Recovery::OUTCOMES,
145
+ allowed_outcomes: Phronomy::Agent::RecoveryRules::OUTCOMES,
149
146
  facts: {
150
147
  tool_call_id: first["tool_call_id"],
151
148
  tool_name: first["tool_name"],
@@ -194,7 +191,7 @@ module Phronomy
194
191
  end.freeze
195
192
  end
196
193
 
197
- def build_recovery_hash(subjects, reason: :outcome_unknown, allowed_outcomes: Phronomy::Recovery::OUTCOMES)
194
+ def build_recovery_hash(subjects, reason: :outcome_unknown, allowed_outcomes: Phronomy::Agent::RecoveryRules::OUTCOMES)
198
195
  {
199
196
  "version" => ExecutionMetadata::CONTRACT_VERSION,
200
197
  "reason" => reason.to_s,
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Agent
5
+ # Exact identity reserved by a coordinating domain. Correlation is immutable;
6
+ # mutable routing fences belong to the coordinator, never to this identity.
7
+ # @api public
8
+ ReservedExecution = Data.define(:agent_id, :execution_id, :correlation) do
9
+ def initialize(agent_id:, execution_id:, correlation: nil)
10
+ raise ArgumentError, "reserved identities must not be empty" if agent_id.to_s.empty? || execution_id.to_s.empty?
11
+ super(agent_id: agent_id.to_s.freeze, execution_id: execution_id.to_s.freeze,
12
+ correlation: Phronomy::Values::Immutable.copy(correlation))
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Agent
5
+ # A durable reference prevents whole-Agent purge. The referencing domain
6
+ # decides when its opaque owner key can be released.
7
+ # @api public
8
+ Retention = Data.define(:agent_id, :execution_id, :owner_key) do
9
+ def initialize(agent_id:, owner_key:, execution_id: nil)
10
+ raise ArgumentError, "retention identities must not be empty" if agent_id.to_s.empty? || owner_key.to_s.empty?
11
+ super(agent_id: agent_id.to_s.freeze, execution_id: execution_id&.to_s&.freeze, owner_key: owner_key.to_s.freeze)
12
+ end
13
+
14
+ def to_h = {"agent_id" => agent_id, "execution_id" => execution_id, "owner_key" => owner_key}.freeze
15
+ end
16
+ end
17
+ end