phronomy 0.25.0 → 0.27.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 (323) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +71 -0
  3. data/CONTRIBUTING.md +4 -4
  4. data/README.md +14 -7
  5. data/VERIFY.sh +27 -27
  6. data/benchmark/bench_agent_invoke.rb +26 -22
  7. data/benchmark/bench_context_assembler.rb +4 -5
  8. data/benchmark/bench_regression.rb +1 -1
  9. data/docs/architecture/agent-chat-and-state-ownership.md +147 -0
  10. data/docs/architecture/agent-configuration-and-tool-binding.md +139 -0
  11. data/docs/architecture/agent-context.md +5 -3
  12. data/docs/architecture/agent-transition-ownership.md +89 -0
  13. data/docs/architecture/before-llm-input.md +6 -0
  14. data/docs/architecture/context-management.md +30 -0
  15. data/docs/architecture/context-preparation-steps.md +90 -0
  16. data/docs/architecture/entry-action-and-team-wording.md +72 -0
  17. data/docs/architecture/execution-metadata-and-values.md +80 -0
  18. data/docs/architecture/generator-verifier-ownership.md +111 -0
  19. data/docs/architecture/multi-agent-handoff.md +8 -2
  20. data/docs/architecture/persistence-refactoring-plan.md +50 -0
  21. data/docs/architecture/persistence.md +29 -6
  22. data/docs/architecture/refactoring-closure.md +334 -0
  23. data/docs/architecture/remaining-refactoring-plan.md +374 -0
  24. data/docs/architecture/rubyllm-2-token-ownership.md +82 -0
  25. data/docs/architecture/tool-schema-recording-gap.md +50 -0
  26. data/docs/architecture/tracing.md +4 -4
  27. data/docs/architecture/workflow-terminal-ownership-design.md +104 -0
  28. data/docs/architecture.md +209 -0
  29. data/docs/async-composition.md +205 -0
  30. data/docs/decisions/010-cooperative-first-concurrency.md +23 -23
  31. data/docs/decisions/014-unified-persistence-durable-state.md +11 -0
  32. data/docs/decisions/024-event-loop-single-writer-agent-runtime.md +5 -1
  33. data/docs/decisions/025-process-local-agent-ownership-and-runtime-admission.md +13 -3
  34. data/docs/decisions/026-workflow-runtime-admission-and-durable-terminal-barrier.md +5 -1
  35. data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +5 -0
  36. data/docs/decisions/031-durable-multi-agent-coordination.md +5 -0
  37. data/docs/decisions/032-storage-backend-composition.md +80 -0
  38. data/docs/decisions/033-domain-persistence-ownership.md +79 -0
  39. data/docs/decisions/034-handoff-runner-coordination-ownership.md +76 -0
  40. data/docs/decisions/035-tool-executor-capability-ownership.md +68 -0
  41. data/docs/decisions/036-context-contract-ownership.md +86 -0
  42. data/docs/decisions/037-common-definition-ownership.md +62 -0
  43. data/docs/decisions/038-responsibility-based-source-layout.md +131 -0
  44. data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +74 -0
  45. data/docs/decisions/040-configuration-default-composition.md +94 -0
  46. data/docs/decisions/041-feature-owned-identity-registries.md +100 -0
  47. data/docs/decisions/042-feature-owned-execution-state.md +106 -0
  48. data/docs/decisions/043-storage-execution-constraint-notifications.md +75 -0
  49. data/docs/decisions/044-agent-default-and-one-shot-composition.md +106 -0
  50. data/docs/decisions/045-worker-input-restriction-ownership.md +100 -0
  51. data/docs/decisions/046-agent-responsibility-layout-and-shared-records.md +110 -0
  52. data/docs/decisions/047-recovered-execution-continuation-contract.md +116 -0
  53. data/docs/decisions/048-dispatch-preparation-worker-ownership.md +120 -0
  54. data/docs/decisions/049-initial-preparation-worker-ownership.md +111 -0
  55. data/docs/decisions/050-approval-resume-snapshot-and-commit-ownership.md +98 -0
  56. data/docs/decisions/051-execution-outcome-worker-ownership.md +151 -0
  57. data/docs/decisions/052-tool-invocation-restoration-ownership.md +92 -0
  58. data/docs/decisions/053-shared-state-coordination-ownership.md +71 -0
  59. data/docs/decisions/054-workflow-terminal-save-single-owner.md +76 -0
  60. data/docs/decisions/055-terminal-observer-failure-settlement.md +60 -0
  61. data/docs/decisions/056-workflow-terminal-policy-ownership.md +87 -0
  62. data/docs/decisions/057-storage-transaction-boundaries.md +73 -0
  63. data/docs/decisions/058-neutral-storage-primitives.md +78 -0
  64. data/docs/decisions/README.md +35 -6
  65. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_DESIGN_V2.md +6 -0
  66. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md +6 -0
  67. data/docs/features.md +21 -12
  68. data/docs/getting-started.md +10 -10
  69. data/docs/migrations/0.15.md +5 -0
  70. data/docs/migrations/durable-semantic-coordination-v2.md +7 -1
  71. data/docs/migrations/handoff-runner-multi-agent.md +35 -0
  72. data/docs/migrations/neutral-storage-spi.md +43 -0
  73. data/docs/migrations/parallel-tool-chat-removal.md +49 -0
  74. data/docs/migrations/shared-state-multi-agent.md +44 -0
  75. data/docs/migrations/storage-backend-composition.md +117 -0
  76. data/docs/migrations/storage-transaction-boundaries.md +74 -0
  77. data/docs/persistence-backends.md +162 -204
  78. data/docs/runtime-and-concurrency.md +205 -60
  79. data/lib/phronomy/agent/api/agent.rb +17 -0
  80. data/lib/phronomy/agent/async_event_api.rb +2 -2
  81. data/lib/phronomy/agent/base.rb +55 -251
  82. data/lib/phronomy/{agent.rb → agent/composition/run_once.rb} +4 -13
  83. data/lib/phronomy/agent/context/capability/base.rb +67 -26
  84. data/lib/phronomy/agent/context/capability/tool_executor.rb +62 -0
  85. data/lib/phronomy/agent/{context_assembler.rb → context_assembly/context_assembler.rb} +143 -88
  86. data/lib/phronomy/agent/{context_importer.rb → context_assembly/context_importer.rb} +2 -2
  87. data/lib/phronomy/agent/{ruby_llm_materializer.rb → context_assembly/ruby_llm_materializer.rb} +4 -7
  88. data/lib/phronomy/agent/context_assembly/runtime_chat_builder.rb +36 -0
  89. data/lib/phronomy/agent/context_assembly/saved_context_reader.rb +53 -0
  90. data/lib/phronomy/agent/context_assembly/state_writer.rb +165 -0
  91. data/lib/phronomy/agent/context_assembly/token_budget_resolver.rb +22 -0
  92. data/lib/phronomy/agent/{context_plan.rb → context_contract/context_plan.rb} +1 -1
  93. data/lib/phronomy/agent/{context_policy_input.rb → context_contract/context_policy_input.rb} +6 -6
  94. data/lib/phronomy/agent/{llm_input_build_context.rb → context_contract/llm_input_build_context.rb} +1 -1
  95. data/lib/phronomy/agent/{llm_input_manifest.rb → context_contract/llm_input_manifest.rb} +20 -20
  96. data/lib/phronomy/agent/{llm_input_patch.rb → context_contract/llm_input_patch.rb} +2 -2
  97. data/lib/phronomy/agent/{agent_execution.rb → execution/agent_execution.rb} +8 -4
  98. data/lib/phronomy/agent/{agent_invocation.rb → execution/agent_invocation.rb} +8 -13
  99. data/lib/phronomy/agent/{agent_invocation_session_builder.rb → execution/agent_invocation_session_builder.rb} +20 -96
  100. data/lib/phronomy/agent/execution/approval_resume_commit.rb +108 -0
  101. data/lib/phronomy/agent/execution/dispatch_preparation.rb +305 -0
  102. data/lib/phronomy/agent/{exact_execution.rb → execution/exact_execution.rb} +12 -13
  103. data/lib/phronomy/agent/{execution_cancellation.rb → execution/execution_cancellation.rb} +2 -3
  104. data/lib/phronomy/agent/execution/execution_coordinator.rb +1925 -0
  105. data/lib/phronomy/agent/execution/execution_failure.rb +30 -0
  106. data/lib/phronomy/agent/execution/execution_metadata.rb +53 -0
  107. data/lib/phronomy/agent/execution/execution_outcome_committer.rb +344 -0
  108. data/lib/phronomy/agent/execution/execution_registry.rb +459 -0
  109. data/lib/phronomy/agent/execution/execution_session_runner.rb +118 -0
  110. data/lib/phronomy/agent/execution/initial_preparation.rb +421 -0
  111. data/lib/phronomy/agent/execution/invocation_transitions.rb +86 -0
  112. data/lib/phronomy/agent/{phase_machine_builder.rb → execution/phase_machine_builder.rb} +22 -73
  113. data/lib/phronomy/agent/{provider_call_outcome.rb → execution/provider_call_outcome.rb} +9 -9
  114. data/lib/phronomy/agent/execution/runtime_record_encoder.rb +210 -0
  115. data/lib/phronomy/agent/{handoff_context.rb → handoff/handoff_context.rb} +2 -2
  116. data/lib/phronomy/agent/handoff/handoff_execution_coordinator.rb +15 -0
  117. data/lib/phronomy/agent/handoff/handoff_outcome_committer.rb +131 -0
  118. data/lib/phronomy/agent/{handoff_state.rb → handoff/handoff_state.rb} +1 -1
  119. data/lib/phronomy/agent/{journal_projection.rb → journal/journal_projection.rb} +4 -0
  120. data/lib/phronomy/agent/{journal_record.rb → journal/journal_record.rb} +3 -3
  121. data/lib/phronomy/agent/{llm_call_record.rb → journal/llm_call_record.rb} +2 -2
  122. data/lib/phronomy/agent/{agent_root.rb → lifecycle/agent_root.rb} +6 -2
  123. data/lib/phronomy/agent/lifecycle/default_persistence.rb +29 -0
  124. data/lib/phronomy/{engine/runtime/agent_ownership_registry.rb → agent/lifecycle/ownership_registry.rb} +24 -10
  125. data/lib/phronomy/{agent_already_exists_error.rb → agent/lifecycle_contract/agent_already_exists_error.rb} +2 -0
  126. data/lib/phronomy/{agent_busy_error.rb → agent/lifecycle_contract/agent_busy_error.rb} +2 -0
  127. data/lib/phronomy/{agent_purged_error.rb → agent/lifecycle_contract/agent_purged_error.rb} +2 -0
  128. data/lib/phronomy/agent/lifecycle_contract/handoff_error.rb +7 -0
  129. data/lib/phronomy/{stream_callback_error.rb → agent/lifecycle_contract/stream_callback_error.rb} +2 -0
  130. data/lib/phronomy/agent/persistence/agent_repository.rb +61 -0
  131. data/lib/phronomy/agent/persistence/codec.rb +358 -0
  132. data/lib/phronomy/agent/persistence/execution_repository.rb +108 -0
  133. data/lib/phronomy/agent/persistence/handoff_state_repository.rb +58 -0
  134. data/lib/phronomy/agent/persistence/journal_repository.rb +54 -0
  135. data/lib/phronomy/agent/persistence/queries.rb +61 -0
  136. data/lib/phronomy/agent/persistence/storage_schema.rb +24 -0
  137. data/lib/phronomy/agent/persistence/watermark.rb +27 -0
  138. data/lib/phronomy/agent/recovery/invocation_restorer.rb +132 -0
  139. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/continuation.rb +21 -45
  140. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/installation.rb +36 -41
  141. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/resolution.rb +37 -40
  142. data/lib/phronomy/agent/{recovery_coordinator.rb → recovery/recovery_coordinator.rb} +9 -13
  143. data/lib/phronomy/agent/recovery/recovery_support.rb +227 -0
  144. data/lib/phronomy/agent/selection/candidate.rb +1 -1
  145. data/lib/phronomy/agent/{approval_evaluation_request.rb → tool_execution/approval_evaluation_request.rb} +1 -12
  146. data/lib/phronomy/agent/{tool_approval_request.rb → tool_execution/tool_approval_request.rb} +1 -10
  147. data/lib/phronomy/agent/tool_execution/tool_binding.rb +90 -0
  148. data/lib/phronomy/agent/{tool_call_intercepted.rb → tool_execution/tool_call_intercepted.rb} +2 -2
  149. data/lib/phronomy/agent/{tool_definition_set.rb → tool_execution/tool_definition_set.rb} +9 -4
  150. data/lib/phronomy/agent/{tool_invocation.rb → tool_execution/tool_invocation.rb} +69 -34
  151. data/lib/phronomy/agent/{tool_invocation_session_builder.rb → tool_execution/tool_invocation_session_builder.rb} +2 -2
  152. data/lib/phronomy/common/configuration_error.rb +7 -0
  153. data/lib/phronomy/common/error.rb +5 -0
  154. data/lib/phronomy/{agent → common/values}/immutable.rb +9 -1
  155. data/lib/phronomy/common/values/serializable.rb +32 -0
  156. data/lib/phronomy/{configuration.rb → configuration/configuration.rb} +14 -5
  157. data/lib/phronomy/configuration/global_configuration.rb +26 -0
  158. data/lib/phronomy/content_store/storage_schema.rb +11 -0
  159. data/lib/phronomy/content_store/stored_contents.rb +43 -0
  160. data/lib/phronomy/engine/backpressure_error.rb +7 -0
  161. data/lib/phronomy/{blocking.rb → engine/blocking.rb} +15 -8
  162. data/lib/phronomy/engine/cancellation_error.rb +7 -0
  163. data/lib/phronomy/engine/concurrency/cancellation_token.rb +4 -0
  164. data/lib/phronomy/engine/concurrency/offload_pool.rb +29 -16
  165. data/lib/phronomy/engine/concurrency/operation_binding.rb +43 -0
  166. data/lib/phronomy/engine/concurrency/physical_completion_task.rb +4 -62
  167. data/lib/phronomy/engine/concurrency/result_collector.rb +99 -0
  168. data/lib/phronomy/engine/concurrency/result_composition.rb +145 -0
  169. data/lib/phronomy/engine/concurrency/subscriptions.rb +68 -0
  170. data/lib/phronomy/engine/concurrency/worker_input_restricted.rb +14 -0
  171. data/lib/phronomy/engine/event_loop.rb +160 -626
  172. data/lib/phronomy/engine/event_loop_reentrancy_error.rb +8 -0
  173. data/lib/phronomy/engine/execution.rb +229 -0
  174. data/lib/phronomy/engine/execution_cancellation_error.rb +14 -0
  175. data/lib/phronomy/engine/execution_receiver.rb +65 -0
  176. data/lib/phronomy/engine/execution_timeout_error.rb +14 -0
  177. data/lib/phronomy/engine/fsm_protocol.rb +14 -0
  178. data/lib/phronomy/engine/fsm_session.rb +38 -31
  179. data/lib/phronomy/{invalid_async_entry_action_error.rb → engine/invalid_async_entry_action_error.rb} +3 -1
  180. data/lib/phronomy/{invalid_async_transition_action_error.rb → engine/invalid_async_transition_action_error.rb} +3 -1
  181. data/lib/phronomy/{invalid_async_workflow_action_error.rb → engine/invalid_async_workflow_action_error.rb} +3 -1
  182. data/lib/phronomy/{invocation_context.rb → engine/invocation_context.rb} +13 -1
  183. data/lib/phronomy/engine/pool_shutdown_error.rb +7 -0
  184. data/lib/phronomy/engine/recursion_limit_error.rb +7 -0
  185. data/lib/phronomy/engine/runtime/timer_queue.rb +11 -0
  186. data/lib/phronomy/engine/runtime.rb +80 -84
  187. data/lib/phronomy/engine/runtime_shutdown_error.rb +7 -0
  188. data/lib/phronomy/engine/runtime_shutdown_reentrancy_error.rb +7 -0
  189. data/lib/phronomy/engine/scheduler_reentrancy_error.rb +9 -0
  190. data/lib/phronomy/engine/{task.rb → task_result.rb} +101 -42
  191. data/lib/phronomy/engine/timeout_error.rb +7 -0
  192. data/lib/phronomy/filter/contract/filter_block_error.rb +14 -0
  193. data/lib/phronomy/generation/generator_verifier/agent_result_receiver.rb +89 -0
  194. data/lib/phronomy/generation/generator_verifier/pipeline_state.rb +57 -0
  195. data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +112 -0
  196. data/lib/phronomy/generation/generator_verifier.rb +118 -0
  197. data/lib/phronomy/generation/low_confidence_error.rb +14 -0
  198. data/lib/phronomy/llm_adapter/base.rb +2 -2
  199. data/lib/phronomy/llm_context_window/token_budget.rb +6 -7
  200. data/lib/phronomy/llm_contract/authentication_error.rb +7 -0
  201. data/lib/phronomy/{context_budget_exceeded_error.rb → llm_contract/context_budget_exceeded_error.rb} +2 -0
  202. data/lib/phronomy/llm_contract/context_length_error.rb +7 -0
  203. data/lib/phronomy/llm_contract/rate_limit_error.rb +7 -0
  204. data/lib/phronomy/{token_usage.rb → llm_contract/token_usage.rb} +2 -2
  205. data/lib/phronomy/llm_contract/transport_error.rb +7 -0
  206. data/lib/phronomy/multi_agent/admission_registry.rb +22 -2
  207. data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +8 -8
  208. data/lib/phronomy/{agent → multi_agent}/handoff_runner.rb +18 -17
  209. data/lib/phronomy/multi_agent/orchestrator.rb +36 -68
  210. data/lib/phronomy/multi_agent/persistence/codec.rb +55 -0
  211. data/lib/phronomy/multi_agent/persistence/queries.rb +30 -0
  212. data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +108 -0
  213. data/lib/phronomy/multi_agent/persistence/team_repository.rb +61 -0
  214. data/lib/phronomy/{agent → multi_agent}/shared_state.rb +56 -39
  215. data/lib/phronomy/multi_agent/storage_contract/team_storage_schema.rb +15 -0
  216. data/lib/phronomy/multi_agent/team_coordinator.rb +21 -18
  217. data/lib/phronomy/multi_agent/team_execution.rb +1 -1
  218. data/lib/phronomy/{engine/runtime → multi_agent}/team_ownership_registry.rb +14 -4
  219. data/lib/phronomy/multi_agent/team_root.rb +1 -1
  220. data/lib/phronomy/output_parser/contract/parse_error.rb +7 -0
  221. data/lib/phronomy/persistence/api/persistence.rb +140 -0
  222. data/lib/phronomy/persistence/migration/initial_format_migration.rb +19 -19
  223. data/lib/phronomy/persistence_composition/repositories.rb +77 -0
  224. data/lib/phronomy/persistence_composition/storage_schema.rb +24 -0
  225. data/lib/phronomy/{execution_rehydration_required_error.rb → recovery/execution_rehydration_required_error.rb} +2 -0
  226. data/lib/phronomy/{recovery.rb → recovery/recovery.rb} +1 -1
  227. data/lib/phronomy/runtime_composition/agent_defaults.rb +7 -0
  228. data/lib/phronomy/runtime_composition/configuration_defaults.rb +9 -0
  229. data/lib/phronomy/runtime_composition/global_runtime.rb +19 -0
  230. data/lib/phronomy/storage/backend.rb +101 -0
  231. data/lib/phronomy/storage/backends/in_memory.rb +157 -0
  232. data/lib/phronomy/storage/blob_conflict_error.rb +10 -0
  233. data/lib/phronomy/storage/blobs.rb +31 -0
  234. data/lib/phronomy/storage/condition.rb +25 -0
  235. data/lib/phronomy/storage/condition_failed_error.rb +16 -0
  236. data/lib/phronomy/storage/conflict_error.rb +9 -0
  237. data/lib/phronomy/{persistence → storage}/durable_record.rb +12 -12
  238. data/lib/phronomy/storage/entry.rb +33 -0
  239. data/lib/phronomy/storage/guard_ref.rb +13 -0
  240. data/lib/phronomy/storage/not_found_error.rb +9 -0
  241. data/lib/phronomy/storage/record_codec.rb +177 -0
  242. data/lib/phronomy/storage/records.rb +61 -0
  243. data/lib/phronomy/storage/resource.rb +126 -0
  244. data/lib/phronomy/storage/scope.rb +25 -0
  245. data/lib/phronomy/storage/serialization_error.rb +9 -0
  246. data/lib/phronomy/storage/streams.rb +44 -0
  247. data/lib/phronomy/storage/transaction_error.rb +10 -0
  248. data/lib/phronomy/storage/unique_constraint_error.rb +17 -0
  249. data/lib/phronomy/storage/unsupported_backend_error.rb +9 -0
  250. data/lib/phronomy/storage/validation.rb +53 -0
  251. data/lib/phronomy/storage/view.rb +131 -0
  252. data/lib/phronomy/testing/eval/scorer/llm_judge.rb +5 -3
  253. data/lib/phronomy/testing/fake_clock.rb +13 -9
  254. data/lib/phronomy/testing/persistence_contract/a_content_store.rb +1 -1
  255. data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +4 -4
  256. data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +10 -7
  257. data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +2 -2
  258. data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +6 -6
  259. data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +6 -6
  260. data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +8 -8
  261. data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +263 -0
  262. data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +123 -0
  263. data/lib/phronomy/testing/persistence_contract.rb +4 -0
  264. data/lib/phronomy/tool/contract/tool_error.rb +7 -0
  265. data/lib/phronomy/tools/agent.rb +6 -6
  266. data/lib/phronomy/vector_store/async_backend.rb +5 -5
  267. data/lib/phronomy/vector_store/embeddings/base.rb +2 -2
  268. data/lib/phronomy/version.rb +1 -1
  269. data/lib/phronomy/{workflow.rb → workflow/execution/workflow.rb} +10 -8
  270. data/lib/phronomy/{workflow_context.rb → workflow/execution/workflow_context.rb} +4 -0
  271. data/lib/phronomy/workflow/execution/workflow_context_ownership_error.rb +7 -0
  272. data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +188 -0
  273. data/lib/phronomy/{workflow_runner.rb → workflow/execution/workflow_runner.rb} +126 -75
  274. data/lib/phronomy/workflow/execution/workflow_terminal_policy.rb +40 -0
  275. data/lib/phronomy/workflow/persistence/codec.rb +153 -0
  276. data/lib/phronomy/workflow/persistence/state_repository.rb +57 -0
  277. data/lib/phronomy/workflow/phase_machine_builder.rb +8 -8
  278. data/lib/phronomy/workflow/storage_contract/workflow_storage_schema.rb +9 -0
  279. data/lib/phronomy.rb +52 -94
  280. data/scripts/api_snapshot.rb +4 -2
  281. data/scripts/storage_spi_snapshot.rb +36 -0
  282. data/sig/phronomy/agent.rbs +4 -5
  283. data/sig/phronomy/execution_receiver.rbs +34 -0
  284. data/sig/phronomy/extensions.rbs +5 -5
  285. data/sig/phronomy/handoff.rbs +4 -2
  286. data/sig/phronomy/multi_agent.rbs +19 -3
  287. data/sig/phronomy/persistence.rbs +7 -91
  288. data/sig/phronomy/runtime.rbs +34 -7
  289. data/sig/phronomy/storage.rbs +174 -0
  290. data/sig/phronomy/tool.rbs +11 -2
  291. data/sig/phronomy/workflow.rbs +1 -1
  292. metadata +236 -99
  293. data/lib/phronomy/agent/execution_coordinator.rb +0 -3138
  294. data/lib/phronomy/agent/handoff_execution_coordinator.rb +0 -143
  295. data/lib/phronomy/agent/recovery_support.rb +0 -504
  296. data/lib/phronomy/agent/token_budget_resolver.rb +0 -70
  297. data/lib/phronomy/agent/tool_executor.rb +0 -55
  298. data/lib/phronomy/generator_verifier.rb +0 -369
  299. data/lib/phronomy/invalid_context_budget_configuration_error.rb +0 -8
  300. data/lib/phronomy/multi_agent/fan_out_invocation.rb +0 -137
  301. data/lib/phronomy/multi_agent/fan_out_session_builder.rb +0 -118
  302. data/lib/phronomy/multi_agent/parallel_tool_chat.rb +0 -116
  303. data/lib/phronomy/persistence/durable_codec.rb +0 -706
  304. data/lib/phronomy/persistence/in_memory.rb +0 -690
  305. data/lib/phronomy/persistence/repository_facades.rb +0 -535
  306. data/lib/phronomy/persistence.rb +0 -276
  307. data/lib/phronomy/ruby_llm_patches.rb +0 -24
  308. data/lib/phronomy/workflow_recovery.rb +0 -123
  309. /data/lib/phronomy/agent/{context_candidate_resolver.rb → context_assembly/context_candidate_resolver.rb} +0 -0
  310. /data/lib/phronomy/agent/{context_policy_input_builder.rb → context_assembly/context_policy_input_builder.rb} +0 -0
  311. /data/lib/phronomy/agent/{context_plan_validator.rb → context_contract/context_plan_validator.rb} +0 -0
  312. /data/lib/phronomy/agent/{context_policy.rb → context_contract/context_policy.rb} +0 -0
  313. /data/lib/phronomy/agent/{llm_operation_result.rb → execution/llm_operation_result.rb} +0 -0
  314. /data/lib/phronomy/agent/{handoff.rb → handoff/handoff.rb} +0 -0
  315. /data/lib/phronomy/agent/{handoff_capability_factory.rb → handoff/handoff_capability_factory.rb} +0 -0
  316. /data/lib/phronomy/agent/{handoff_policy.rb → handoff/handoff_policy.rb} +0 -0
  317. /data/lib/phronomy/agent/{handoff_projection.rb → handoff/handoff_projection.rb} +0 -0
  318. /data/lib/phronomy/agent/{handoff_request.rb → handoff/handoff_request.rb} +0 -0
  319. /data/lib/phronomy/{canonical_json.rb → common/canonical_json.rb} +0 -0
  320. /data/lib/phronomy/{diagnostics.rb → engine/diagnostics.rb} +0 -0
  321. /data/lib/phronomy/{event.rb → engine/event.rb} +0 -0
  322. /data/lib/phronomy/{metrics.rb → engine/metrics.rb} +0 -0
  323. /data/lib/phronomy/{runnable.rb → engine/runnable.rb} +0 -0
@@ -0,0 +1,305 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../engine/concurrency/worker_input_restricted"
4
+
5
+ module Phronomy
6
+ module Agent
7
+ # Commits the operation-specific prerequisites for Provider/Tool dispatch.
8
+ # The Agent supplies existing context/hook services, never live-state writes.
9
+ # Inputs and results belong to this worker boundary; delivery stays with the
10
+ # execution owner. No operation-local state is retained between calls.
11
+ # @api private
12
+ class DispatchPreparation
13
+ include Phronomy::Concurrency::WorkerInputRestricted
14
+
15
+ ProviderCommand = Data.define(
16
+ :execution_id, :fsm_session_id, :expected_execution_revision,
17
+ :root, :journal_records, :execution, :base_manifest,
18
+ :invocation_config, :runtime_snapshot, :streaming,
19
+ :pending_llm_call_id, :pending_llm_started_at
20
+ )
21
+ ToolCommand = Data.define(
22
+ :execution_id, :fsm_session_id, :expected_execution_revision,
23
+ :root, :execution, :runtime_snapshot, :tool_batch_snapshot
24
+ )
25
+ ProviderReconciliationCommand = Data.define(
26
+ :operation, :intended_result, :original_error
27
+ )
28
+ ToolReconciliationCommand = Data.define(
29
+ :operation, :intended_result, :original_error
30
+ )
31
+ ProviderResult = Data.define(:execution, :runtime_projection, :error)
32
+ ToolResult = Data.define(:execution)
33
+ ProviderReconciliationResult = Data.define(:disposition, :preparation_result)
34
+ ToolReconciliationResult = Data.define(:disposition, :preparation_result)
35
+
36
+ # A failed response does not prove that the execution save rolled back.
37
+ # @api private
38
+ class OutcomeUnknownError < Phronomy::Error
39
+ attr_reader :original_error, :intended_result
40
+
41
+ def initialize(original_error, intended_result)
42
+ @original_error = original_error
43
+ @intended_result = intended_result
44
+ super(
45
+ "durable dispatch preparation outcome is uncertain: " \
46
+ "#{original_error.class}: #{original_error.message}"
47
+ )
48
+ set_backtrace(original_error.backtrace)
49
+ end
50
+ end
51
+
52
+ # @api private
53
+ def initialize(agent:, persistence:)
54
+ @agent = agent
55
+ @persistence = persistence
56
+ end
57
+
58
+ # @api private
59
+ def prepare_provider(operation)
60
+ staged = stage_provider_call(operation)
61
+ recorded = encode_provider_records(operation, staged)
62
+ assembler, prepared = prepare_provider_context(operation, recorded)
63
+ committed, manifest, manifest_ref = commit_provider_preparation(
64
+ operation, recorded, assembler, prepared
65
+ )
66
+ materialize_provider_result(committed.execution, manifest, manifest_ref)
67
+ end
68
+
69
+ # @api private
70
+ def prepare_tools(operation)
71
+ intended = nil
72
+ begin
73
+ @persistence.transaction do |tx|
74
+ assert_local_durable_base!(tx, operation.root)
75
+ updated = build_tool_dispatch_execution(operation, tx)
76
+ updated = @agent.__prepare_coordination_record(updated, tx: tx)
77
+ tx.executions.save(
78
+ operation.execution.execution_id,
79
+ expected_revision: operation.execution.execution_revision,
80
+ execution: updated
81
+ )
82
+ intended = ToolResult.new(execution: updated)
83
+ end
84
+ rescue => caught
85
+ raise if known_durable_failure?(caught) || intended.nil?
86
+
87
+ raise OutcomeUnknownError.new(caught, intended)
88
+ end
89
+ intended
90
+ end
91
+
92
+ # @api private
93
+ def reconcile_provider(command)
94
+ disposition, current = reconcile_preparation(
95
+ command.operation, command.intended_result.execution
96
+ )
97
+ result = restore_provider_result(current) if disposition == :committed
98
+ ProviderReconciliationResult.new(
99
+ disposition: disposition, preparation_result: result
100
+ )
101
+ end
102
+
103
+ # @api private
104
+ def reconcile_tools(command)
105
+ disposition, current = reconcile_preparation(
106
+ command.operation, command.intended_result.execution
107
+ )
108
+ result = ToolResult.new(execution: current) if disposition == :committed
109
+ ToolReconciliationResult.new(
110
+ disposition: disposition, preparation_result: result
111
+ )
112
+ end
113
+
114
+ private
115
+
116
+ def stage_provider_call(operation)
117
+ metadata = operation.execution.metadata.dup
118
+ metadata.delete(ExecutionMetadata::TOOL_BATCH_METADATA_KEY)
119
+ metadata.delete(ExecutionMetadata::RECOVERY_METADATA_KEY)
120
+ metadata[ExecutionMetadata::PENDING_LLM_ID_KEY] = operation.pending_llm_call_id
121
+ metadata[ExecutionMetadata::PENDING_LLM_STARTED_AT_KEY] = operation.pending_llm_started_at
122
+ metadata[ExecutionMetadata::CONTRACT_VERSION_KEY] = ExecutionMetadata::CONTRACT_VERSION
123
+ operation.execution.with(
124
+ execution_revision: operation.execution.execution_revision,
125
+ metadata: metadata
126
+ )
127
+ end
128
+
129
+ def encode_provider_records(operation, staged)
130
+ encoded_records = call_records = nil
131
+ # Content-addressed records may be added here; Execution is not advanced.
132
+ @persistence.transaction do |tx|
133
+ assert_local_durable_base!(tx, operation.root)
134
+ encoded_records, call_records = RuntimeRecordEncoder.encode(
135
+ staged,
136
+ agent_id: @agent.agent_id,
137
+ tx: tx,
138
+ snapshot: operation.runtime_snapshot,
139
+ context_candidate: true,
140
+ agent_root: operation.root
141
+ )
142
+ end
143
+ staged.with(
144
+ execution_revision: staged.execution_revision,
145
+ phase: :preparing_llm_call,
146
+ working_records: staged.working_records + encoded_records,
147
+ llm_calls: staged.llm_calls + call_records
148
+ )
149
+ end
150
+
151
+ def prepare_provider_context(operation, recorded)
152
+ patch = @agent.send(
153
+ :run_before_llm_input_hooks,
154
+ call_sequence: recorded.llm_calls.length + 1,
155
+ config: operation.invocation_config
156
+ )
157
+ assembler = ContextAssembler.new(
158
+ agent: @agent,
159
+ persistence: @persistence,
160
+ journal_records: operation.journal_records
161
+ )
162
+ prepared = assembler.prepare_followup(
163
+ base_manifest: operation.base_manifest,
164
+ agent_root: operation.root,
165
+ execution: recorded,
166
+ config: operation.invocation_config,
167
+ patch: patch
168
+ )
169
+ @agent.send(
170
+ :check_cancellation!,
171
+ operation.invocation_config,
172
+ "invocation cancelled after context policy"
173
+ )
174
+ [assembler, prepared]
175
+ end
176
+
177
+ def commit_provider_preparation(operation, recorded, assembler, prepared)
178
+ manifest = manifest_ref = intended = nil
179
+ begin
180
+ @persistence.transaction do |tx|
181
+ assert_local_durable_base!(tx, operation.root)
182
+ manifest, manifest_ref = assembler.finalize(prepared, persistence: tx)
183
+ updated = execution_with_provider_manifest(recorded, manifest_ref)
184
+ updated = @agent.__prepare_coordination_record(updated, tx: tx)
185
+ tx.executions.save(
186
+ operation.execution.execution_id,
187
+ expected_revision: operation.execution.execution_revision,
188
+ execution: updated
189
+ )
190
+ intended = ProviderResult.new(
191
+ execution: updated, runtime_projection: nil, error: nil
192
+ )
193
+ end
194
+ rescue => caught
195
+ raise if known_durable_failure?(caught) || intended.nil?
196
+
197
+ raise OutcomeUnknownError.new(caught, intended)
198
+ end
199
+ [intended, manifest, manifest_ref]
200
+ end
201
+
202
+ def execution_with_provider_manifest(recorded, manifest_ref)
203
+ refs = Array(recorded.metadata["manifest_refs"]) + [manifest_ref]
204
+ recorded.with(
205
+ phase: :calling_llm,
206
+ metadata: recorded.metadata.merge(
207
+ "manifest_ref" => manifest_ref,
208
+ "manifest_refs" => refs
209
+ )
210
+ )
211
+ end
212
+
213
+ def materialize_provider_result(execution, manifest, manifest_ref)
214
+ projection = materialization_error = nil
215
+ begin
216
+ projection = RubyLLMMaterializer.new(
217
+ agent: @agent, persistence: @persistence
218
+ ).materialize(manifest: manifest, manifest_ref: manifest_ref)
219
+ rescue => error
220
+ materialization_error = error
221
+ end
222
+ ProviderResult.new(
223
+ execution: execution, runtime_projection: projection,
224
+ error: materialization_error
225
+ )
226
+ end
227
+
228
+ def restore_provider_result(execution)
229
+ projection = materialization_error = nil
230
+ begin
231
+ _manifest, projection = SavedContextReader.materialize_projection(
232
+ @agent, execution.metadata.fetch("manifest_ref")
233
+ )
234
+ rescue => error
235
+ materialization_error = error
236
+ end
237
+ ProviderResult.new(
238
+ execution: execution, runtime_projection: projection,
239
+ error: materialization_error
240
+ )
241
+ end
242
+
243
+ def build_tool_dispatch_execution(operation, tx)
244
+ encoded_records, call_records = RuntimeRecordEncoder.encode(
245
+ operation.execution,
246
+ agent_id: @agent.agent_id,
247
+ tx: tx,
248
+ snapshot: operation.runtime_snapshot,
249
+ context_candidate: true,
250
+ agent_root: operation.root
251
+ )
252
+ operation.execution.with(
253
+ phase: :dispatching_tools,
254
+ working_records: operation.execution.working_records + encoded_records,
255
+ llm_calls: operation.execution.llm_calls + call_records,
256
+ metadata: tool_dispatch_metadata(operation)
257
+ )
258
+ end
259
+
260
+ def tool_dispatch_metadata(operation)
261
+ metadata = operation.execution.metadata.dup
262
+ metadata.delete(ExecutionMetadata::PENDING_LLM_ID_KEY)
263
+ metadata.delete(ExecutionMetadata::PENDING_LLM_STARTED_AT_KEY)
264
+ metadata.delete(ExecutionMetadata::RECOVERY_METADATA_KEY)
265
+ metadata.delete("framework_calls_pending")
266
+ metadata[ExecutionMetadata::TOOL_BATCH_METADATA_KEY] =
267
+ Phronomy::Values::Serializable.convert(operation.tool_batch_snapshot,
268
+ unsupported_message: "Recovery value is not canonically serializable")
269
+ metadata[ExecutionMetadata::CONTRACT_VERSION_KEY] = ExecutionMetadata::CONTRACT_VERSION
270
+ metadata
271
+ end
272
+
273
+ def reconcile_preparation(operation, intended_execution)
274
+ current = @persistence.executions.load(operation.execution_id)
275
+ if current.execution_revision == intended_execution.execution_revision &&
276
+ current.to_h == intended_execution.to_h
277
+ return [:committed, current]
278
+ end
279
+ if current.execution_revision == operation.execution.execution_revision &&
280
+ current.to_h == operation.execution.to_h
281
+ return [:not_committed, current]
282
+ end
283
+
284
+ [:conflict, current]
285
+ end
286
+
287
+ def known_durable_failure?(error)
288
+ error.is_a?(Phronomy::Storage::ConflictError) ||
289
+ error.is_a?(Phronomy::Storage::NotFoundError) ||
290
+ error.is_a?(Phronomy::Storage::SerializationError) ||
291
+ error.is_a?(Phronomy::Storage::UnsupportedBackendError) ||
292
+ error.is_a?(ArgumentError) ||
293
+ error.is_a?(Phronomy::ConfigurationError)
294
+ end
295
+
296
+ def assert_local_durable_base!(tx, root)
297
+ tx.assert_agent_watermark!(
298
+ agent_id: root.agent_id,
299
+ agent_revision: root.agent_revision,
300
+ journal_position: root.journal_position
301
+ )
302
+ end
303
+ end
304
+ end
305
+ end
@@ -4,7 +4,7 @@ module Phronomy
4
4
  module Agent
5
5
  # Observes a reserved execution using ordinary Agent admission and Recovery.
6
6
  # Durable reads/materialization run off EventLoop. Workers never wait for a
7
- # child lifecycle: the existing EventLoop completion waiter settles Task.
7
+ # child lifecycle: the existing EventLoop completion waiter settles TaskResult.
8
8
  # @api private
9
9
  class ExactExecution
10
10
  Wait = Data.define(:coordinator, :agent, :execution_id)
@@ -17,7 +17,7 @@ module Phronomy
17
17
  def initialize(agent, execution_id, input, config)
18
18
  @agent, @id, @input, @config = agent, execution_id.to_s.freeze, input, config.freeze
19
19
  @runtime = Phronomy::Runtime.instance
20
- @completion = Phronomy::Task.deferred(name: "exact-execution:#{@id}")
20
+ @completion = Phronomy::TaskResult.deferred(name: "exact-execution:#{@id}")
21
21
  end
22
22
 
23
23
  def start
@@ -26,7 +26,7 @@ module Phronomy
26
26
  if execution&.terminal?
27
27
  [:terminal, materialize(execution)]
28
28
  elsif execution
29
- unless @runtime.__agent_execution_owner(@id)
29
+ unless Phronomy::Agent::ExecutionRegistry.existing_for(@runtime)&.agent_execution_owner(@id)
30
30
  @agent.instance_variable_set(:@_phronomy_coordination_config, @config)
31
31
  begin
32
32
  RecoveryCoordinator.new(@agent).recover_on_load!
@@ -55,8 +55,7 @@ module Phronomy
55
55
  source.on_complete { |_value, failure| reconcile(failure) }
56
56
  when :active
57
57
  command = Wait.new(coordinator: self, agent: @agent, execution_id: @id)
58
- posted = @runtime.event_loop.post(Phronomy::Event.new(type: :agent_terminal_ready,
59
- target_id: Phronomy::EventLoop::SYSTEM_CHANNEL_ID, payload: {command: command}))
58
+ posted = ExecutionRegistry.for(@runtime.event_loop).post(command, completion: @completion)
60
59
  @completion.fail(Phronomy::RuntimeShutdownError.new("Exact execution observer rejected")) unless posted
61
60
  end
62
61
  end
@@ -70,7 +69,7 @@ module Phronomy
70
69
  end
71
70
 
72
71
  def deliver_on_event_loop(command)
73
- state = @runtime.event_loop.agent_execution_state(command.execution_id)
72
+ state = Phronomy::Agent::ExecutionRegistry.for(@runtime.event_loop).agent_execution_state(command.execution_id)
74
73
  unless state
75
74
  # Ownership can be released between the durable read and this command.
76
75
  # Reinstall once with the caller's current wiring/cancellation request.
@@ -81,7 +80,7 @@ module Phronomy
81
80
  return reconcile(nil)
82
81
  end
83
82
  unless state.agent.equal?(command.agent)
84
- raise Phronomy::Persistence::ConflictError, "Exact execution #{@id} owner mismatch"
83
+ raise Phronomy::Storage::ConflictError, "Exact execution #{@id} owner mismatch"
85
84
  end
86
85
  if @config[:cancellation_token]&.cancelled?
87
86
  state.invocation&.config&.fetch(:cancellation_token, nil)&.cancel!
@@ -92,9 +91,9 @@ module Phronomy
92
91
  end
93
92
  observers = Array(state.invocation.config[:phronomy_exact_observers])
94
93
  state.invocation.merge_config!(phronomy_exact_observers: (observers + [@completion]).uniq.freeze)
95
- waiter = Phronomy::Task.deferred(name: "exact-wait:#{@id}")
94
+ waiter = Phronomy::TaskResult.deferred(name: "exact-wait:#{@id}")
96
95
  waiter.on_complete { |_result, failure| reconcile(failure) }
97
- @runtime.event_loop.register_agent_completion_waiter(@id, waiter)
96
+ Phronomy::Agent::ExecutionRegistry.for(@runtime.event_loop).register_agent_completion_waiter(@id, waiter)
98
97
  rescue => failure
99
98
  @completion.fail(failure)
100
99
  end
@@ -104,11 +103,11 @@ module Phronomy
104
103
  def read_execution
105
104
  begin
106
105
  execution = @agent.persistence.executions.load(@id)
107
- rescue Phronomy::Persistence::NotFoundError
106
+ rescue Phronomy::Storage::NotFoundError
108
107
  return nil
109
108
  end
110
109
  unless execution.agent_id == @agent.agent_id
111
- raise Phronomy::Persistence::ConflictError, "Reserved execution #{@id} belongs to another Agent"
110
+ raise Phronomy::Storage::ConflictError, "Reserved execution #{@id} belongs to another Agent"
112
111
  end
113
112
  expected = @config[:phronomy_coordination]
114
113
  stored = execution.metadata["coordination"]
@@ -116,12 +115,12 @@ module Phronomy
116
115
  actual_identity = stored&.except("handoff_revision")
117
116
  expected_identity = expected.except("handoff_revision")
118
117
  unless actual_identity == expected_identity
119
- raise Phronomy::Persistence::ConflictError, "Reserved execution #{@id} coordination mismatch"
118
+ raise Phronomy::Storage::ConflictError, "Reserved execution #{@id} coordination mismatch"
120
119
  end
121
120
  end
122
121
  ref = execution.metadata["current_input_ref"]
123
122
  if ref && @agent.persistence.contents.fetch_text(ref) != @input
124
- raise Phronomy::Persistence::ConflictError, "Reserved execution #{@id} input mismatch"
123
+ raise Phronomy::Storage::ConflictError, "Reserved execution #{@id} input mismatch"
125
124
  end
126
125
  execution
127
126
  end
@@ -11,12 +11,11 @@ module Phronomy
11
11
 
12
12
  def self.signal(execution_id, agent_id)
13
13
  command = Command.new(coordinator: new, execution_id: execution_id, agent_id: agent_id)
14
- Phronomy::Runtime.instance.event_loop.post(Phronomy::Event.new(type: :agent_terminal_ready,
15
- target_id: Phronomy::EventLoop::SYSTEM_CHANNEL_ID, payload: {command: command}))
14
+ ExecutionRegistry.for(Phronomy::Runtime.instance.event_loop).post(command)
16
15
  end
17
16
 
18
17
  def deliver_on_event_loop(command)
19
- state = Phronomy::Runtime.instance.event_loop.agent_execution_state(command.execution_id)
18
+ state = Phronomy::Agent::ExecutionRegistry.for(Phronomy::Runtime.instance.event_loop).agent_execution_state(command.execution_id)
20
19
  return unless state && state.agent.agent_id == command.agent_id
21
20
  state.invocation&.config&.fetch(:cancellation_token, nil)&.cancel!
22
21
  end