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,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Concurrency
5
+ # Connects explicit invocation controls before operation admission. Its token
6
+ # is private: cancelling it never cancels the application's shared tokens.
7
+ # Used by Agent, Blocking and Orchestrator admission; it owns no worker.
8
+ # @api private
9
+ class OperationBinding
10
+ attr_reader :token, :scope
11
+
12
+ def initialize(invocation_context:, cancellation_token:)
13
+ if !invocation_context.nil? && !invocation_context.is_a?(InvocationContext)
14
+ raise TypeError, "invocation_context must be a Phronomy::InvocationContext or nil"
15
+ end
16
+ @scope = invocation_context&.__execution_scope
17
+ @token = CancellationToken.new
18
+ @subscriptions = Subscriptions.new
19
+ [invocation_context&.cancellation_token, cancellation_token].compact.uniq.each do |source|
20
+ @subscriptions.cancellation(source) { @token.cancel! }
21
+ end
22
+ if !@scope && invocation_context&.deadline
23
+ @subscriptions.after(invocation_context.deadline.remaining_seconds) { @token.cancel! }
24
+ end
25
+ @token.cancel! if @scope && !@scope.__open?
26
+ end
27
+
28
+ def bind(result)
29
+ result.__bind_execution(@scope)
30
+ track(result)
31
+ end
32
+
33
+ def track(result)
34
+ @subscriptions.result(result) { @subscriptions.close }
35
+ result
36
+ end
37
+
38
+ def close
39
+ @subscriptions.close
40
+ end
41
+ end
42
+ end
43
+ end
@@ -2,16 +2,16 @@
2
2
 
3
3
  module Phronomy
4
4
  module Concurrency
5
- # Private Task specialization used when logical completion and physical
5
+ # Private TaskResult specialization used when logical completion and physical
6
6
  # completion are intentionally different boundaries.
7
7
  #
8
- # Offload cancellation/timeout may settle the caller-facing Task while a
8
+ # Offload cancellation/timeout may settle the caller-facing TaskResult while a
9
9
  # worker is still executing. EventLoop supervision must observe the later
10
10
  # physical-completion boundary without adding domain-specific state to the
11
- # public Phronomy::Task contract.
11
+ # public Phronomy::TaskResult contract.
12
12
  #
13
13
  # @api private
14
- class PhysicalCompletionTask < Phronomy::Task
14
+ class PhysicalCompletionTask < Phronomy::TaskResult
15
15
  # @api private
16
16
  def initialize(name: nil, parent: nil)
17
17
  super
@@ -63,64 +63,6 @@ module Phronomy
63
63
  self
64
64
  end
65
65
 
66
- # Preserves the physical-completion boundary across Task#map.
67
- #
68
- # A mapped PhysicalCompletionTask is physically complete only after both:
69
- # 1. the source Task's underlying physical work is complete; and
70
- # 2. the mapping callback itself has finished running.
71
- #
72
- # The second condition is required because Task completion callbacks may run
73
- # on the OffloadPool worker that settles the source Task. Propagating the
74
- # source physical signal immediately could otherwise let EventLoop declare
75
- # the owning Execution quiescent while the mapping callback is still active.
76
- #
77
- # Logical success/failure semantics intentionally remain the same as
78
- # Phronomy::Task#map.
79
- #
80
- # @api public
81
- def map(&block)
82
- raise ArgumentError, "map requires a block" unless block
83
-
84
- mapped = self.class.deferred(name: "#{name}-mapped", parent: parent)
85
- propagation_mutex = Mutex.new
86
- source_physical_complete = physical_complete?
87
- mapping_complete = false
88
-
89
- mark_mapped_physical_if_ready = lambda do
90
- ready = propagation_mutex.synchronize do
91
- source_physical_complete && mapping_complete
92
- end
93
- mapped.mark_physical_complete! if ready
94
- end
95
-
96
- on_physical_complete do
97
- propagation_mutex.synchronize { source_physical_complete = true }
98
- mark_mapped_physical_if_ready.call
99
- end
100
-
101
- on_complete do |value, error|
102
- if error
103
- propagation_mutex.synchronize { mapping_complete = true }
104
- mark_mapped_physical_if_ready.call
105
- mapped.fail(error)
106
- next
107
- end
108
-
109
- begin
110
- transformed = block.call(value)
111
- propagation_mutex.synchronize { mapping_complete = true }
112
- mark_mapped_physical_if_ready.call
113
- mapped.complete(transformed)
114
- rescue => mapped_error
115
- propagation_mutex.synchronize { mapping_complete = true }
116
- mark_mapped_physical_if_ready.call
117
- mapped.fail(mapped_error)
118
- end
119
- end
120
-
121
- mapped
122
- end
123
-
124
66
  private
125
67
 
126
68
  def deliver_physical_callback(callback)
@@ -0,0 +1,99 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Concurrency
5
+ # Shared input-order fan-in and terminal arbitration. No application code or
6
+ # completion callback runs while this collector's lock is held.
7
+ # @api private
8
+ class ResultCollector
9
+ def initialize(size, &finished)
10
+ @mutex = Mutex.new
11
+ @outcomes = Array.new(size)
12
+ @remaining = size
13
+ @closed = false
14
+ @sources = {}.compare_by_identity
15
+ @subscriptions = Subscriptions.new
16
+ @finished = finished
17
+ end
18
+
19
+ def while_open
20
+ @mutex.synchronize { yield unless @closed }
21
+ end
22
+
23
+ def open?
24
+ @mutex.synchronize { !@closed }
25
+ end
26
+
27
+ def watch(index, result, &recorded)
28
+ subscribe = @mutex.synchronize do
29
+ return if @closed
30
+
31
+ first = !@sources.key?(result)
32
+ (@sources[result] ||= {})[index] = recorded
33
+ first
34
+ end
35
+ if subscribe
36
+ @subscriptions.result(result) { receive(result) }
37
+ elsif result.done?
38
+ receive(result)
39
+ end
40
+ end
41
+
42
+ def finish(kind)
43
+ snapshot = @mutex.synchronize do
44
+ return false if @closed
45
+ return false if kind == :completed && @remaining > 0
46
+
47
+ @closed = true
48
+ snapshot_locked
49
+ end
50
+ deliver(kind, snapshot)
51
+ true
52
+ end
53
+
54
+ private
55
+
56
+ def receive(result)
57
+ status, value, error = result.__snapshot
58
+ callbacks = []
59
+ snapshot = @mutex.synchronize do
60
+ return if @closed
61
+
62
+ @sources.fetch(result).each do |index, callback|
63
+ next if @outcomes[index]
64
+
65
+ @outcomes[index] = TaskResult::Outcome.new(index: index,
66
+ status: status, value: value, error: error)
67
+ @remaining -= 1
68
+ callbacks << callback if callback
69
+ end
70
+ if @remaining.zero?
71
+ @closed = true
72
+ snapshot_locked
73
+ end
74
+ end
75
+ deliver(:completed, snapshot) if snapshot
76
+ callbacks.each(&:call)
77
+ end
78
+
79
+ def snapshot_locked
80
+ @outcomes.each_with_index.map do |outcome, index|
81
+ outcome || TaskResult::Outcome.new(index: index,
82
+ status: :unfinished, value: nil, error: nil)
83
+ end.freeze
84
+ end
85
+
86
+ def deliver(kind, snapshot)
87
+ finished = @mutex.synchronize do
88
+ @sources.clear
89
+ @outcomes.clear
90
+ callback = @finished
91
+ @finished = nil
92
+ callback
93
+ end
94
+ @subscriptions.close
95
+ finished.call(kind, snapshot)
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Concurrency
5
+ # Shared map/flat_map implementation, including the private physical-work
6
+ # boundary. Observing a result never mutates that result or its cancellation.
7
+ # @api private
8
+ class ResultComposition
9
+ def initialize(source, flatten:, &block)
10
+ @source = source
11
+ @scope = source.__execution_scope
12
+ @flatten = flatten
13
+ @block = block
14
+ physical = flatten || source.respond_to?(:on_physical_complete)
15
+ klass = physical ? PhysicalCompletionTask : TaskResult
16
+ @result = klass.deferred(name: "#{source.name}-#{flatten ? "flat-mapped" : "mapped"}",
17
+ parent: source.parent).__bind_execution(@scope)
18
+ @mutex = Mutex.new
19
+ @phase = :waiting
20
+ @source_physical = !source.respond_to?(:on_physical_complete)
21
+ @work_done = false
22
+ @inner_physical = true
23
+ @subscriptions = Subscriptions.new
24
+ end
25
+
26
+ def start
27
+ unless @source_physical
28
+ @source.on_physical_complete do
29
+ @mutex.synchronize { @source_physical = true }
30
+ finish_physical
31
+ end
32
+ end
33
+ @subscriptions.result(@source) { receive_source }
34
+ if @scope
35
+ @subscriptions.cancellation(@scope.__cancellation_token) { suppress }
36
+ suppress unless @scope.__open?
37
+ end
38
+ @result
39
+ end
40
+
41
+ private
42
+
43
+ def receive_source
44
+ status, value, error = @source.__snapshot
45
+ if status != :completed
46
+ started = @mutex.synchronize do
47
+ next false unless @phase == :waiting
48
+ @phase = :finished
49
+ @work_done = true
50
+ true
51
+ end
52
+ if started
53
+ finish_physical
54
+ propagate(status, value, error)
55
+ @subscriptions.close
56
+ end
57
+ return
58
+ end
59
+
60
+ claim = lambda do
61
+ @mutex.synchronize do
62
+ next false unless @phase == :waiting
63
+ @phase = :running
64
+ true
65
+ end
66
+ end
67
+ allowed = @scope ? @scope.__while_open(&claim) : claim.call
68
+ unless allowed
69
+ suppress
70
+ return
71
+ end
72
+
73
+ begin
74
+ transformed = @block.call(value)
75
+ if @flatten && !transformed.is_a?(TaskResult)
76
+ raise TypeError, "flat_map block must return a Phronomy::TaskResult"
77
+ end
78
+ track_inner_physical(transformed) if @flatten
79
+ @mutex.synchronize do
80
+ @work_done = true
81
+ @phase = :inner unless @phase == :suppressed
82
+ end
83
+ finish_physical
84
+ if @flatten
85
+ unless @result.done?
86
+ @subscriptions.result(transformed) do
87
+ propagate(*transformed.__snapshot)
88
+ @subscriptions.close
89
+ end
90
+ end
91
+ else
92
+ @result.complete(transformed)
93
+ @subscriptions.close
94
+ end
95
+ rescue => error
96
+ @mutex.synchronize { @work_done = true }
97
+ finish_physical
98
+ @result.fail(error)
99
+ @subscriptions.close
100
+ ensure
101
+ # Even an exception deliberately outside StandardError cannot leave
102
+ # already-finished Ruby work looking physically active.
103
+ @mutex.synchronize { @work_done = true }
104
+ finish_physical
105
+ end
106
+ end
107
+
108
+ def suppress
109
+ @mutex.synchronize do
110
+ @work_done = true if @phase == :waiting
111
+ @phase = :suppressed
112
+ end
113
+ finish_physical
114
+ @result.cancel!(@scope.__cancellation_error)
115
+ @subscriptions.close
116
+ end
117
+
118
+ def propagate(status, value, error)
119
+ case status
120
+ when :completed then @result.complete(value)
121
+ when :cancelled then @result.cancel!(error)
122
+ when :failed then @result.fail(error)
123
+ end
124
+ end
125
+
126
+ def track_inner_physical(inner)
127
+ return unless inner.respond_to?(:on_physical_complete)
128
+ return unless inner.__execution_scope.equal?(@scope)
129
+
130
+ @mutex.synchronize { @inner_physical = false }
131
+ inner.on_physical_complete do
132
+ @mutex.synchronize { @inner_physical = true }
133
+ finish_physical
134
+ end
135
+ end
136
+
137
+ def finish_physical
138
+ return unless @result.respond_to?(:mark_physical_complete!)
139
+
140
+ done = @mutex.synchronize { @source_physical && @work_done && @inner_physical }
141
+ @result.mark_physical_complete! if done
142
+ end
143
+ end
144
+ end
145
+ end
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Concurrency
5
+ # Disposable registrations. add also handles synchronous notification during
6
+ # registration: cleanup added after close is performed immediately.
7
+ # @api private
8
+ class Subscriptions
9
+ def initialize
10
+ @mutex = Mutex.new
11
+ @closed = false
12
+ @cleanup = []
13
+ end
14
+
15
+ def add(&cleanup)
16
+ dispose = @mutex.synchronize do
17
+ if @closed
18
+ true
19
+ else
20
+ @cleanup << cleanup
21
+ false
22
+ end
23
+ end
24
+ cleanup.call if dispose
25
+ end
26
+
27
+ def result(result, &callback)
28
+ result.on_complete(&callback)
29
+ add { result.__unsubscribe(callback) }
30
+ end
31
+
32
+ def cancellation(token, &callback)
33
+ return unless token
34
+
35
+ token.on_cancel(&callback)
36
+ add { token.send(:unregister_cancel_callback, callback) }
37
+ remaining = token.remaining_monotonic_seconds
38
+ if token.cancelled?
39
+ callback.call
40
+ elsif remaining
41
+ after(remaining, &callback)
42
+ end
43
+ end
44
+
45
+ def after(seconds, &callback)
46
+ if seconds <= 0
47
+ callback.call
48
+ return
49
+ end
50
+ timer = Phronomy::Runtime.instance.timer_queue
51
+ timer.schedule(seconds: seconds, &callback)
52
+ add { timer.cancel(callback) }
53
+ end
54
+
55
+ def close
56
+ cleanup = @mutex.synchronize do
57
+ return if @closed
58
+
59
+ @closed = true
60
+ current = @cleanup
61
+ @cleanup = []
62
+ current
63
+ end
64
+ cleanup.each(&:call)
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Concurrency
5
+ # Marks framework-managed objects excluded from authorization worker value
6
+ # snapshots and application behavior handles. Each owning type opts in.
7
+ # This methodless contract does not validate arbitrary object graphs or
8
+ # restrict every OffloadPool command. Application-owned opaque values retain
9
+ # their existing contract (ADR-024/045).
10
+ # @api private
11
+ module WorkerInputRestricted
12
+ end
13
+ end
14
+ end