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
@@ -12,7 +12,7 @@ module Phronomy
12
12
  # the framework rather than by each backend.
13
13
  #
14
14
  # A future genuine native-async backend may adapt its completion into a
15
- # {Phronomy::Task} without an OffloadPool worker, but native async override is
15
+ # {Phronomy::TaskResult} without an OffloadPool worker, but native async override is
16
16
  # not part of the current backend SPI.
17
17
  #
18
18
  # @api private
@@ -24,7 +24,7 @@ module Phronomy
24
24
  # @param metadata [Hash]
25
25
  # @param cancellation_token [Phronomy::Concurrency::CancellationToken, nil]
26
26
  # @param timeout [Numeric, nil]
27
- # @return [Phronomy::Task]
27
+ # @return [Phronomy::TaskResult]
28
28
  # @api public
29
29
  def add_async(id:, embedding:, metadata: {}, cancellation_token: nil, timeout: nil)
30
30
  Phronomy::Runtime.instance.offload.submit(
@@ -42,7 +42,7 @@ module Phronomy
42
42
  # @param k [Integer]
43
43
  # @param cancellation_token [Phronomy::Concurrency::CancellationToken, nil]
44
44
  # @param timeout [Numeric, nil]
45
- # @return [Phronomy::Task]
45
+ # @return [Phronomy::TaskResult]
46
46
  # @api public
47
47
  def search_async(query_embedding:, k: 5, cancellation_token: nil, timeout: nil)
48
48
  Phronomy::Runtime.instance.offload.submit(
@@ -59,7 +59,7 @@ module Phronomy
59
59
  # @param id [String]
60
60
  # @param cancellation_token [Phronomy::Concurrency::CancellationToken, nil]
61
61
  # @param timeout [Numeric, nil]
62
- # @return [Phronomy::Task]
62
+ # @return [Phronomy::TaskResult]
63
63
  # @api public
64
64
  def remove_async(id:, cancellation_token: nil, timeout: nil)
65
65
  Phronomy::Runtime.instance.offload.submit(
@@ -75,7 +75,7 @@ module Phronomy
75
75
  #
76
76
  # @param cancellation_token [Phronomy::Concurrency::CancellationToken, nil]
77
77
  # @param timeout [Numeric, nil]
78
- # @return [Phronomy::Task]
78
+ # @return [Phronomy::TaskResult]
79
79
  # @api public
80
80
  def clear_async(cancellation_token: nil, timeout: nil)
81
81
  Phronomy::Runtime.instance.offload.submit(
@@ -7,7 +7,7 @@ module Phronomy
7
7
  #
8
8
  # Concrete implementations override {#embed}. Phronomy owns the async
9
9
  # bridge: {#embed_async} routes the synchronous implementation through the
10
- # bounded OffloadPool and returns a {Phronomy::Task}.
10
+ # bounded OffloadPool and returns a {Phronomy::TaskResult}.
11
11
  #
12
12
  # @api public
13
13
  class Base
@@ -27,7 +27,7 @@ module Phronomy
27
27
  # @param text [String]
28
28
  # @param cancellation_token [Phronomy::Concurrency::CancellationToken, nil]
29
29
  # @param timeout [Numeric, nil] operation-wide submit timeout
30
- # @return [Phronomy::Task]
30
+ # @return [Phronomy::TaskResult]
31
31
  # @api public
32
32
  def embed_async(text, cancellation_token = nil, timeout: nil)
33
33
  Phronomy::Runtime.instance.offload.submit(
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Phronomy
4
- VERSION = "0.25.0"
4
+ VERSION = "0.27.0"
5
5
  end
@@ -1,11 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../../engine/concurrency/worker_input_restricted"
4
+
3
5
  require_relative "workflow_runner"
4
- require_relative "runnable"
6
+ require_relative "../../engine/runnable"
5
7
 
6
8
  module Phronomy
7
9
  # StateChart-style Workflow definition DSL.
8
10
  class Workflow
11
+ include Phronomy::Concurrency::WorkerInputRestricted
12
+
9
13
  include Phronomy::Runnable
10
14
 
11
15
  def self.define(context_class, persistence: nil, &block)
@@ -72,8 +76,6 @@ module Phronomy
72
76
  public
73
77
 
74
78
  class Builder
75
- FINISH = Phronomy::WorkflowRunner::FINISH
76
-
77
79
  def initialize(context_class, persistence: nil)
78
80
  @context_class = context_class
79
81
  @persistence = persistence
@@ -93,7 +95,7 @@ module Phronomy
93
95
  #
94
96
  # Entry actions are synchronous Run-to-Completion callbacks. To start
95
97
  # asynchronous work, register its listener/callback inside the action and
96
- # return the context or nil. Returning Phronomy::Task is an error.
98
+ # return the context or nil. Returning Phronomy::TaskResult is an error.
97
99
  def state(name, action: nil)
98
100
  @declared_states << name
99
101
  entry(name, action) if action
@@ -117,9 +119,9 @@ module Phronomy
117
119
  # Transition actions are synchronous Run-to-Completion callbacks executed
118
120
  # after the source exit callbacks and before the target entry callbacks.
119
121
  # They may start asynchronous work and register listeners, but returning
120
- # Phronomy::Task is an error; completion must arrive as a later event.
122
+ # Phronomy::TaskResult is an error; completion must arrive as a later event.
121
123
  def transition(from:, to:, guard: nil, on: nil, action: nil)
122
- destination = (to == :__finish__) ? FINISH : to
124
+ destination = (to == :__finish__) ? FSMProtocol::FINISH : to
123
125
  @transitions << {
124
126
  from: from,
125
127
  to: destination,
@@ -184,7 +186,7 @@ module Phronomy
184
186
 
185
187
  undefined_targets = @transitions
186
188
  .map { |transition| transition[:to] }
187
- .reject { |target| target == FINISH } - all_states
189
+ .reject { |target| target == FSMProtocol::FINISH } - all_states
188
190
  unless undefined_targets.empty?
189
191
  raise ArgumentError,
190
192
  "Workflow transition(s) reference undefined state(s): " \
@@ -205,7 +207,7 @@ module Phronomy
205
207
  current = queue.shift
206
208
  @transitions.each do |transition|
207
209
  next unless transition[:from] == current
208
- next if transition[:to] == FINISH
210
+ next if transition[:to] == FSMProtocol::FINISH
209
211
  next if reachable.include?(transition[:to])
210
212
 
211
213
  reachable.add(transition[:to])
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../../engine/concurrency/worker_input_restricted"
4
+
3
5
  module Phronomy
4
6
  # Module for defining Workflow context data.
5
7
  #
@@ -12,6 +14,8 @@ module Phronomy
12
14
  # evaluation, return a replacement WorkflowContext, or return +:consume+ to
13
15
  # discard a stale/unrelated event without firing a transition.
14
16
  module WorkflowContext
17
+ include Phronomy::Concurrency::WorkerInputRestricted
18
+
15
19
  def self.included(base)
16
20
  base.extend(ClassMethods)
17
21
  base.instance_variable_set(:@fields, {})
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../common/error"
4
+
5
+ module Phronomy
6
+ class WorkflowContextOwnershipError < Error; end
7
+ end
@@ -0,0 +1,188 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ # Workflow-owned segment admission and routing, mutated only on EventLoop.
5
+ # @api private
6
+ class WorkflowExecutionRegistry < Phronomy::ExecutionReceiver
7
+ # Workflow-owned process-local Workflow execution-segment admission. The
8
+ # owner token is a Runtime coordination capability, not a domain identity or
9
+ # routing identity. fsm_session_id is bound only after durable load/hydration
10
+ # completes and a concrete FSMSession has been constructed.
11
+ WorkflowAdmission = Data.define(
12
+ :workflow_instance_id, :owner_token, :fsm_session_id, :state
13
+ )
14
+ private_constant :WorkflowAdmission
15
+
16
+ def initialize(event_loop:)
17
+ super
18
+ @workflow_admissions = {}
19
+ end
20
+
21
+ def post(command, admission: false, completion: nil)
22
+ post_message(command, admission: admission, completion: completion)
23
+ end
24
+
25
+ def deliver(command)
26
+ assert_event_loop_thread!
27
+ command.runner.deliver_on_event_loop(command)
28
+ end
29
+
30
+ def idle?
31
+ !workflow_admission_transition_in_progress_locked?
32
+ end
33
+
34
+ def session_retired(fsm_session_id, reason:)
35
+ assert_event_loop_thread!
36
+ mark_workflow_recovery_required_for_session(fsm_session_id) if reason == :recovery_required
37
+ end
38
+
39
+ def shutdown(error:)
40
+ synchronize { @workflow_admissions.clear }
41
+ end
42
+
43
+ # Reserves one logical Workflow execution segment before durable load or
44
+ # hydration. The owner token is independent from any later FSMSession id.
45
+ # @api private
46
+ def admit_workflow(workflow_instance_id, owner_token:)
47
+ assert_event_loop_thread!
48
+ key = workflow_instance_id.to_s
49
+ raise ArgumentError, "workflow_instance_id must not be empty" if key.empty?
50
+ raise ArgumentError, "owner_token is required" unless owner_token
51
+
52
+ admit do
53
+ if @workflow_admissions.key?(key)
54
+ raise Phronomy::Error,
55
+ "Workflow instance #{key.inspect} already has a live execution segment"
56
+ end
57
+ @workflow_admissions[key] = WorkflowAdmission.new(
58
+ workflow_instance_id: key.freeze,
59
+ owner_token: owner_token,
60
+ fsm_session_id: nil,
61
+ state: :admitting
62
+ )
63
+ end
64
+ true
65
+ end
66
+
67
+ # Binds the concrete routing identity after admission and durable hydration.
68
+ # @api private
69
+ def bind_workflow_session(workflow_instance_id, owner_token:, fsm_session_id:)
70
+ assert_event_loop_thread!
71
+ key = workflow_instance_id.to_s
72
+ fsm_key = fsm_session_id.to_s
73
+ raise ArgumentError, "fsm_session_id must not be empty" if fsm_key.empty?
74
+
75
+ synchronize do
76
+ current = @workflow_admissions.fetch(key) do
77
+ raise Phronomy::Error, "Workflow instance #{key.inspect} has no Runtime admission"
78
+ end
79
+ unless current.owner_token.equal?(owner_token) && current.fsm_session_id.nil?
80
+ raise Phronomy::Error, "stale Workflow admission bind for #{key.inspect}"
81
+ end
82
+ @workflow_admissions[key] = WorkflowAdmission.new(
83
+ workflow_instance_id: current.workflow_instance_id,
84
+ owner_token: current.owner_token,
85
+ fsm_session_id: fsm_key.freeze,
86
+ state: :executing
87
+ )
88
+ end
89
+ true
90
+ end
91
+
92
+ # @api private
93
+ def mark_workflow_admission(workflow_instance_id, owner_token:, state:)
94
+ assert_event_loop_thread!
95
+ key = workflow_instance_id.to_s
96
+ next_state = state.to_sym
97
+ unless %i[executing persisting_terminal recovery_required].include?(next_state)
98
+ raise ArgumentError, "unsupported Workflow admission state: #{next_state.inspect}"
99
+ end
100
+
101
+ synchronize do
102
+ current = @workflow_admissions.fetch(key) do
103
+ raise Phronomy::Error, "Workflow instance #{key.inspect} has no Runtime admission"
104
+ end
105
+ unless current.owner_token.equal?(owner_token)
106
+ raise Phronomy::Error, "stale Workflow admission update for #{key.inspect}"
107
+ end
108
+ @workflow_admissions[key] = WorkflowAdmission.new(
109
+ workflow_instance_id: current.workflow_instance_id,
110
+ owner_token: current.owner_token,
111
+ fsm_session_id: current.fsm_session_id,
112
+ state: next_state
113
+ )
114
+ end
115
+ true
116
+ end
117
+
118
+ # Owner-aware release. A competing or stale attempt cannot release the
119
+ # current Workflow execution segment.
120
+ # @api private
121
+ def release_workflow(workflow_instance_id, owner_token:)
122
+ assert_event_loop_thread!
123
+ key = workflow_instance_id.to_s
124
+ synchronize do
125
+ current = @workflow_admissions[key]
126
+ next false unless current&.owner_token&.equal?(owner_token)
127
+
128
+ @workflow_admissions.delete(key)
129
+ true
130
+ end
131
+ end
132
+
133
+ # Read-only diagnostics used by internal tests and routing assertions.
134
+ def workflow_admission_owner(workflow_instance_id)
135
+ synchronize do
136
+ @workflow_admissions[workflow_instance_id.to_s]&.owner_token
137
+ end
138
+ end
139
+
140
+ def workflow_admission_fsm_session_id(workflow_instance_id)
141
+ synchronize do
142
+ @workflow_admissions[workflow_instance_id.to_s]&.fsm_session_id
143
+ end
144
+ end
145
+
146
+ def workflow_admission_state(workflow_instance_id)
147
+ synchronize do
148
+ @workflow_admissions[workflow_instance_id.to_s]&.state
149
+ end
150
+ end
151
+
152
+ def post_to_workflow(workflow_instance_id:, event:, payload: nil)
153
+ @event_loop.__route_execution_event(self) do
154
+ admission = @workflow_admissions[workflow_instance_id.to_s]
155
+ next unless admission&.state == :executing && admission.fsm_session_id
156
+
157
+ Phronomy::Event.new(
158
+ type: event.to_sym, target_id: admission.fsm_session_id, payload: payload
159
+ )
160
+ end
161
+ end
162
+
163
+ private
164
+
165
+ def mark_workflow_recovery_required_for_session(fsm_session_id)
166
+ synchronize do
167
+ key, admission = @workflow_admissions.find do |_workflow_instance_id, candidate|
168
+ candidate.fsm_session_id == fsm_session_id.to_s
169
+ end
170
+ return false unless admission
171
+
172
+ @workflow_admissions[key] = WorkflowAdmission.new(
173
+ workflow_instance_id: admission.workflow_instance_id,
174
+ owner_token: admission.owner_token,
175
+ fsm_session_id: nil,
176
+ state: :recovery_required
177
+ )
178
+ end
179
+ true
180
+ end
181
+
182
+ def workflow_admission_transition_in_progress_locked?
183
+ @workflow_admissions.values.any? do |admission|
184
+ %i[admitting executing persisting_terminal].include?(admission.state)
185
+ end
186
+ end
187
+ end
188
+ end