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
data/lib/phronomy.rb CHANGED
@@ -2,12 +2,13 @@
2
2
 
3
3
  require "zeitwerk"
4
4
  require "ruby_llm"
5
- require_relative "phronomy/ruby_llm_patches"
5
+ require_relative "phronomy/version"
6
6
 
7
7
  loader = Zeitwerk::Loader.for_gem
8
8
  loader.inflector.inflect("ruby_llm_embeddings" => "RubyLLMEmbeddings")
9
9
  loader.inflector.inflect("rag" => "RAG")
10
10
  loader.inflector.inflect("fsm_session" => "FSMSession")
11
+ loader.inflector.inflect("fsm_protocol" => "FSMProtocol")
11
12
  loader.inflector.inflect("llm_adapter" => "LLMAdapter")
12
13
  loader.inflector.inflect("llm_operation_result" => "LLMOperationResult")
13
14
  loader.inflector.inflect("ruby_llm" => "RubyLLM")
@@ -18,104 +19,61 @@ loader.inflector.inflect("llm_input_manifest" => "LLMInputManifest")
18
19
  loader.inflector.inflect("llm_input_build_context" => "LLMInputBuildContext")
19
20
  loader.inflector.inflect("llm_input_patch" => "LLMInputPatch")
20
21
  loader.inflector.inflect("before_llm_input" => "BeforeLLMInput")
21
- loader.collapse("#{__dir__}/phronomy/engine")
22
- # Loaded via require_relative before loader.setup; ignore to avoid Zeitwerk constant-name mismatch.
23
- loader.ignore("#{__dir__}/phronomy/ruby_llm_patches.rb")
24
- # Persistence backend conformance tests are explicit test support. Keep them out
25
- # of production eager-load so ordinary `require "phronomy"` never requires RSpec.
22
+ # These responsibility directories do not add a public Ruby namespace.
23
+ %w[common configuration engine generation llm_contract recovery].each do |directory|
24
+ loader.collapse("#{__dir__}/phronomy/#{directory}")
25
+ end
26
+ # Agent responsibility directories retain the existing Agent constant names.
27
+ %w[
28
+ context_contract lifecycle execution tool_execution context_assembly
29
+ journal handoff recovery
30
+ ].each do |directory|
31
+ loader.collapse("#{__dir__}/phronomy/agent/#{directory}")
32
+ end
33
+
34
+ # A nested root is independent of its enclosing Ruby namespace. Keep existing
35
+ # Phronomy::Workflow, Phronomy::WorkflowContext, and other canonical constants
36
+ # beside the feature they belong to, without aliases or a new partial-load API.
37
+ %w[
38
+ agent/api
39
+ agent/lifecycle_contract
40
+ filter/contract
41
+ output_parser/contract
42
+ persistence/api
43
+ tool/contract
44
+ workflow/execution
45
+ workflow/storage_contract
46
+ multi_agent/storage_contract
47
+ ].each do |directory|
48
+ loader.push_dir("#{__dir__}/phronomy/#{directory}", namespace: Phronomy)
49
+ end
50
+
51
+ # These files wire composition, reopen namespaces, or patch a dependency.
52
+ loader.ignore(
53
+ "#{__dir__}/phronomy/configuration/global_configuration.rb",
54
+ "#{__dir__}/phronomy/agent/composition",
55
+ "#{__dir__}/phronomy/runtime_composition/agent_defaults.rb",
56
+ "#{__dir__}/phronomy/runtime_composition/configuration_defaults.rb",
57
+ "#{__dir__}/phronomy/runtime_composition/global_runtime.rb"
58
+ )
59
+ # Persistence conformance support must never make production loading require RSpec.
26
60
  loader.ignore(
27
61
  "#{__dir__}/phronomy/testing/persistence_contract.rb",
28
62
  "#{__dir__}/phronomy/testing/persistence_contract"
29
63
  )
30
64
  loader.setup
31
65
 
32
- require_relative "phronomy/version"
33
- require_relative "phronomy/token_usage"
34
-
35
- module Phronomy
36
- class Error < StandardError; end
37
- class ParseError < Error; end
38
- class RecursionLimitError < Error; end
39
- class ToolError < Error; end
40
- class TimeoutError < Error; end
41
- class ConfigurationError < Error; end
42
- class HandoffError < Error; end
43
-
44
- class TransportError < Error; end
45
- class RateLimitError < TransportError; end
46
- class AuthenticationError < TransportError; end
47
- class ContextLengthError < Error; end
48
- class CancellationError < Error; end
49
-
50
- # Raised when a synchronous API would block the EventLoop control thread.
51
- class EventLoopReentrancyError < Error; end
52
-
53
- # Backward-compatible error class name for callers that still rescue the old
54
- # scheduler-oriented exception. New code should use EventLoopReentrancyError.
55
- class SchedulerReentrancyError < EventLoopReentrancyError; end
66
+ require_relative "phronomy/runtime_composition/configuration_defaults"
67
+ require_relative "phronomy/llm_contract/token_usage"
68
+ require_relative "phronomy/configuration/global_configuration"
69
+ require_relative "phronomy/runtime_composition/global_runtime"
56
70
 
57
- class RuntimeShutdownError < Error; end
58
- class RuntimeShutdownReentrancyError < RuntimeShutdownError; end
59
-
60
- class LowConfidenceError < Error
61
- attr_reader :result
62
-
63
- def initialize(result)
64
- @result = result
65
- super("Answer confidence #{result.confidence} is below the required threshold")
66
- end
67
- end
68
-
69
- class FilterBlockError < Error
70
- attr_reader :filter
71
-
72
- def initialize(message, filter: nil)
73
- super(message)
74
- @filter = filter
75
- end
76
- end
77
-
78
- class PoolShutdownError < Error; end
79
- class BackpressureError < Error; end
80
-
81
- class WorkflowContextOwnershipError < Error; end
82
-
83
- class << self
84
- def configuration
85
- @configuration ||= Configuration.new
86
- end
87
-
88
- def configure
89
- yield configuration
90
- end
91
-
92
- def reset_configuration!
93
- @configuration = Configuration.new
94
- end
95
-
96
- def with_configuration
97
- original = @configuration&.dup
98
- yield configuration
99
- ensure
100
- @configuration = original
101
- end
102
-
103
- def reset_runtime!(timeout: configuration.event_loop_stop_grace_seconds)
104
- previous_grace = @configuration&.event_loop_stop_grace_seconds
105
- result = Runtime.reset_default!(timeout: timeout)
106
-
107
- new_configuration = Configuration.new
108
- if previous_grace
109
- new_configuration.event_loop_stop_grace_seconds = previous_grace
110
- end
111
- @configuration = new_configuration
112
- result
113
- end
114
- end
115
- end
71
+ # Explicitly install the Agent namespace extensions even if its factory
72
+ # contract was loaded first. Composition owns both concrete binding and the
73
+ # run_once method definition; neither is required from Agent execution files.
74
+ require_relative "phronomy/agent/api/agent"
75
+ require_relative "phronomy/runtime_composition/agent_defaults"
76
+ require_relative "phronomy/agent/composition/run_once"
116
77
 
117
- # Shared Recovery primitives and Workflow Persistence F1 reconciliation.
118
- # Entity-specific Agent Recovery is loaded from lib/phronomy/agent.rb when the
119
- # Agent namespace is materialized.
120
- require_relative "phronomy/recovery"
121
- require_relative "phronomy/workflow_recovery"
78
+ # Load the common recovery vocabulary during ordinary application loading.
79
+ require_relative "phronomy/recovery/recovery"
@@ -36,7 +36,7 @@ PUBLIC_API_ENTRIES = [
36
36
  # Beta
37
37
  Phronomy::Agent::Handoff,
38
38
  Phronomy::Agent::HandoffPolicy,
39
- Phronomy::Agent::HandoffRunner,
39
+ Phronomy::MultiAgent::HandoffRunner,
40
40
  Phronomy::MultiAgent::Orchestrator,
41
41
  Phronomy::MultiAgent::TeamCoordinator,
42
42
  Phronomy::Filter::Base,
@@ -70,7 +70,9 @@ def snapshot_entry(klass)
70
70
  "public_instance_methods" => own_methods
71
71
  }
72
72
  else
73
- instance_methods = (klass.public_instance_methods - BASELINE_INSTANCE_METHODS).sort
73
+ internal_context_methods = (klass == Phronomy::InvocationContext) ?
74
+ %i[__bind_execution __execution_scope] : []
75
+ instance_methods = (klass.public_instance_methods - BASELINE_INSTANCE_METHODS - internal_context_methods).sort
74
76
  class_methods = (klass.public_methods(false) - BASELINE_CLASS_METHODS).sort
75
77
  {
76
78
  "name" => klass.name,
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "json"
5
+ require_relative "../lib/phronomy"
6
+
7
+ # Explicit SPI 2 scope, independent of the Stable/Beta product API snapshot.
8
+ # Private dispatch and domain composition helpers are deliberately excluded.
9
+ surfaces = {
10
+ Phronomy::Persistence => Phronomy::Persistence.public_instance_methods(false).sort,
11
+ Phronomy::Storage::Backend => %i[initialize capabilities transaction resources view],
12
+ Phronomy::Storage::Backends::InMemory => %i[initialize capabilities],
13
+ Phronomy::Storage::View => %i[records streams blobs check!],
14
+ Phronomy::Storage::Resource => %i[initialize id kind attributes immutable_attributes indexes unique guard validate_attributes index_values],
15
+ Phronomy::Storage::Records => %i[resource insert read fetch replace delete scan delete_matching],
16
+ Phronomy::Storage::Streams => %i[resource append read head delete],
17
+ Phronomy::Storage::Blobs => %i[resource put_if_absent fetch exist?],
18
+ Phronomy::Storage::GuardRef => %i[initialize resource key],
19
+ Phronomy::Storage::Condition::RevisionIs => %i[initialize resource key expected],
20
+ Phronomy::Storage::Condition::StreamHeadIs => %i[initialize resource stream expected],
21
+ Phronomy::Storage::Condition::NoRows => %i[initialize resource index equals],
22
+ Phronomy::Storage::Entry::Record => %i[initialize key revision attributes record],
23
+ Phronomy::Storage::Entry::Stream => %i[initialize position id record],
24
+ Phronomy::Storage::Entry::Append => %i[initialize id record],
25
+ Phronomy::Storage::Entry::Blob => %i[initialize key bytes attributes],
26
+ Phronomy::Storage::UniqueConstraintError => %i[initialize resource constraint],
27
+ Phronomy::Storage::ConditionFailedError => %i[initialize condition]
28
+ }
29
+ snapshot = {spi_version: 2, capabilities: Phronomy::Storage::Backend::REQUIRED_CAPABILITIES,
30
+ surfaces: surfaces.to_h { |type, names| [type.name, names.sort.to_h { |name| [name, type.instance_method(name).parameters] }] }}
31
+ json = JSON.pretty_generate(snapshot) + "\n"
32
+ if ARGV.include?("--write")
33
+ File.write(File.expand_path("../spec/fixtures/storage_spi_v2_snapshot.json", __dir__), json)
34
+ else
35
+ puts json
36
+ end
@@ -190,7 +190,6 @@ module Phronomy
190
190
  def self.max_iterations: (?Integer value) -> Integer
191
191
  def self.cache_instructions: (?bool enabled) -> bool?
192
192
  def self.max_output_tokens: (?Integer value) -> Integer?
193
- def self.context_window: (?Integer value) -> Integer?
194
193
  def self.context_policy: () -> ContextPolicy
195
194
  | (ContextPolicy policy) -> ContextPolicy
196
195
  def self.agent_definition: () -> Hash[Symbol, untyped]
@@ -213,18 +212,18 @@ module Phronomy
213
212
  # Public operation methods no longer accept per-invocation event listeners
214
213
  # or event-listener blocks.
215
214
  def invoke: (untyped input, ?config: Hash[untyped, untyped], ?invocation_context: untyped) -> Hash[Symbol, untyped]
216
- def invoke_async: (untyped input, ?config: Hash[untyped, untyped], ?invocation_context: untyped) -> Task[Hash[Symbol, untyped]]
215
+ def invoke_async: (untyped input, ?config: Hash[untyped, untyped], ?invocation_context: untyped) -> TaskResult[Hash[Symbol, untyped]]
217
216
  def stream: (untyped input, ?config: Hash[untyped, untyped], ?invocation_context: untyped) -> Hash[Symbol, untyped]
218
- def stream_async: (untyped input, ?config: Hash[untyped, untyped], ?invocation_context: untyped) -> Task[Hash[Symbol, untyped]]
217
+ def stream_async: (untyped input, ?config: Hash[untyped, untyped], ?invocation_context: untyped) -> TaskResult[Hash[Symbol, untyped]]
219
218
 
220
219
  def approve: (String execution_id, approval_request_id: String, ?approved: bool, ?config: Hash[untyped, untyped]) -> untyped
221
- def approve_async: (String execution_id, approval_request_id: String, ?approved: bool, ?config: Hash[untyped, untyped]) -> Task[untyped]
220
+ def approve_async: (String execution_id, approval_request_id: String, ?approved: bool, ?config: Hash[untyped, untyped]) -> TaskResult[untyped]
222
221
 
223
222
  # Resolves a factual Recovery ambiguity. result: is meaningful for
224
223
  # :succeeded; error: is required for :failed; neither is used by
225
224
  # :not_performed.
226
225
  def resolve: (String execution_id, expected_execution_revision: Integer, subject: Hash[Symbol, untyped], outcome: Symbol, ?result: untyped, ?error: Exception) -> untyped
227
- def resolve_async: (String execution_id, expected_execution_revision: Integer, subject: Hash[Symbol, untyped], outcome: Symbol, ?result: untyped, ?error: Exception) -> Task[untyped]
226
+ def resolve_async: (String execution_id, expected_execution_revision: Integer, subject: Hash[Symbol, untyped], outcome: Symbol, ?result: untyped, ?error: Exception) -> TaskResult[untyped]
228
227
 
229
228
  def transcript: () -> Array[untyped]
230
229
  def add_knowledge: (untyped content, ?metadata: Hash[untyped, untyped]) -> self
@@ -0,0 +1,34 @@
1
+ module Phronomy
2
+ # Internal Engine contract (ADR-042); not an application extension SPI.
3
+ class ExecutionReceiver
4
+ def self.for: (EventLoop event_loop) -> instance
5
+ def self.existing_for: (Runtime runtime) -> instance?
6
+ def initialize: (event_loop: EventLoop) -> void
7
+ def __bound_to?: (EventLoop event_loop) -> bool
8
+ def idle?: () -> bool
9
+ def deliver: (untyped message) -> untyped
10
+ def shutdown: (error: Exception?) -> untyped
11
+ def session_retired: (String fsm_session_id, reason: Symbol) -> untyped
12
+
13
+ private
14
+
15
+ def synchronize: [V] () { () -> V } -> V
16
+ def admit: [V] () { () -> V } -> V
17
+ def post_message: (untyped message, ?admission: bool, ?completion: TaskResult[untyped]?) -> bool
18
+ def assert_event_loop_thread!: () -> void
19
+ end
20
+
21
+ class EventLoop
22
+ def __execution_receiver: (key: untyped) -> ExecutionReceiver?
23
+ def __register_execution_receiver: (key: untyped, receiver: ExecutionReceiver) -> ExecutionReceiver
24
+ def __synchronize_execution_state: [V] () { () -> V } -> V
25
+ def __admit_execution: [V] (ExecutionReceiver receiver) { () -> V } -> V
26
+ def __post_execution: (ExecutionReceiver receiver, untyped message, ?admission: bool, ?completion: TaskResult[untyped]?) -> bool
27
+ def __route_execution_event: (ExecutionReceiver receiver) { () -> untyped } -> bool
28
+ def __receiver_cleanup_complete?: () -> bool
29
+ end
30
+
31
+ class Runtime
32
+ def __event_loop_if_initialized: () -> EventLoop?
33
+ end
34
+ end
@@ -28,16 +28,16 @@ module Phronomy
28
28
  def clear: () -> untyped
29
29
  def size: () -> Integer
30
30
 
31
- def add_async: (id: String, embedding: Array[Float], ?metadata: Hash[untyped, untyped], ?cancellation_token: Concurrency::CancellationToken?, ?timeout: Numeric?) -> Task[untyped]
32
- def search_async: (query_embedding: Array[Float], ?k: Integer, ?cancellation_token: Concurrency::CancellationToken?, ?timeout: Numeric?) -> Task[Array[Hash[Symbol, untyped]]]
33
- def remove_async: (id: String, ?cancellation_token: Concurrency::CancellationToken?, ?timeout: Numeric?) -> Task[untyped]
34
- def clear_async: (?cancellation_token: Concurrency::CancellationToken?, ?timeout: Numeric?) -> Task[untyped]
31
+ def add_async: (id: String, embedding: Array[Float], ?metadata: Hash[untyped, untyped], ?cancellation_token: Concurrency::CancellationToken?, ?timeout: Numeric?) -> TaskResult[untyped]
32
+ def search_async: (query_embedding: Array[Float], ?k: Integer, ?cancellation_token: Concurrency::CancellationToken?, ?timeout: Numeric?) -> TaskResult[Array[Hash[Symbol, untyped]]]
33
+ def remove_async: (id: String, ?cancellation_token: Concurrency::CancellationToken?, ?timeout: Numeric?) -> TaskResult[untyped]
34
+ def clear_async: (?cancellation_token: Concurrency::CancellationToken?, ?timeout: Numeric?) -> TaskResult[untyped]
35
35
  end
36
36
 
37
37
  module Embeddings
38
38
  class Base
39
39
  def embed: (String text, ?Concurrency::CancellationToken? cancellation_token) -> Array[Float]
40
- def embed_async: (String text, ?Concurrency::CancellationToken? cancellation_token, ?timeout: Numeric?) -> Task[Array[Float]]
40
+ def embed_async: (String text, ?Concurrency::CancellationToken? cancellation_token, ?timeout: Numeric?) -> TaskResult[Array[Float]]
41
41
  end
42
42
  end
43
43
  end
@@ -25,14 +25,16 @@ module Phronomy
25
25
  def selectable_categories: () -> Array[Symbol]
26
26
  def to_h: () -> Hash[String, Hash[String, String]]
27
27
  end
28
+ end
28
29
 
30
+ module MultiAgent
29
31
  class HandoffRunner
30
32
  MAX_HANDOFFS: Integer
31
33
 
32
34
  attr_reader main_agent: ::Phronomy::Agent::Base
33
- attr_reader handoffs: Array[Handoff]
35
+ attr_reader handoffs: Array[::Phronomy::Agent::Handoff]
34
36
 
35
- def initialize: (main_agent: ::Phronomy::Agent::Base, ?handoffs: Array[Handoff]) -> void
37
+ def initialize: (main_agent: ::Phronomy::Agent::Base, ?handoffs: Array[::Phronomy::Agent::Handoff]) -> void
36
38
  def invoke: (untyped input, ?config: Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
37
39
  def result: (String execution_id) -> Hash[Symbol, untyped]
38
40
  def cancel: (String execution_id) -> Hash[Symbol, untyped]
@@ -1,13 +1,29 @@
1
1
  module Phronomy
2
2
  module MultiAgent
3
+ class SharedState
4
+ class KnowledgeStore
5
+ def self.new: () -> instance
6
+ def read_all: () -> Array[Hash[Symbol, untyped]]
7
+ def write: (agent: untyped, content: untyped, cycle: untyped) -> nil
8
+ def size: () -> Integer
9
+ end
10
+
11
+ def self.new: () -> instance
12
+ def self.member: (singleton(::Phronomy::Agent::Base) klass, ?instruction: untyped) -> Array[Hash[Symbol, untyped]]
13
+ def self.coordination: (?untyped text) -> untyped
14
+ def self.max_cycles: (?untyped value) -> Integer?
15
+ def self.timeout: (?untyped value) -> Float?
16
+ def self.terminate_when: () ?{ (KnowledgeStore) -> untyped } -> untyped
17
+ def self.aggregate: () ?{ (KnowledgeStore) -> untyped } -> untyped
18
+ def invoke: (untyped input, ?config: Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
19
+ end
20
+
3
21
  class Orchestrator < ::Phronomy::Agent::Base
4
22
  def self.subagent: (Symbol | String name, singleton(::Phronomy::Agent::Base) agent_class, ?on_error: Symbol, ?inherit_knowledge: bool) -> untyped
5
23
  def self.registered_subagents: () -> Hash[Symbol | String, untyped]
6
24
  def resume: (String execution_id, ?config: Hash[Symbol, untyped]) -> Hash[Symbol, untyped]
7
25
  def dispatch_parallel: (*Hash[Symbol, untyped] tasks, ?max_concurrency: Integer?, ?on_error: Symbol, ?timeout: Numeric?, ?cancellation_token: untyped, ?invocation_context: untyped, ?inherit_knowledge: bool) -> Array[untyped]
8
- def dispatch_parallel_async: (*Hash[Symbol, untyped] tasks, ?max_concurrency: Integer?, ?on_error: Symbol, ?timeout: Numeric?, ?cancellation_token: untyped, ?invocation_context: untyped, ?inherit_knowledge: bool) -> ::Phronomy::Task[untyped]
9
- def fan_out: (agent: singleton(::Phronomy::Agent::Base), inputs: Array[untyped], ?config: Hash[Symbol, untyped], ?max_concurrency: Integer?, ?on_error: Symbol, ?timeout: Numeric?, ?cancellation_token: untyped, ?invocation_context: untyped, ?inherit_knowledge: bool) -> Array[untyped]
10
- def fan_out_async: (agent: singleton(::Phronomy::Agent::Base), inputs: Array[untyped], ?config: Hash[Symbol, untyped], ?max_concurrency: Integer?, ?on_error: Symbol, ?timeout: Numeric?, ?cancellation_token: untyped, ?invocation_context: untyped, ?inherit_knowledge: bool) -> ::Phronomy::Task[untyped]
26
+ def dispatch_parallel_async: (*Hash[Symbol, untyped] tasks, ?max_concurrency: Integer?, ?on_error: Symbol, ?timeout: Numeric?, ?cancellation_token: untyped, ?invocation_context: untyped, ?inherit_knowledge: bool) -> ::Phronomy::TaskResult[untyped]
11
27
  end
12
28
 
13
29
  class TeamCoordinator
@@ -1,10 +1,4 @@
1
1
  module Phronomy
2
- interface _ContentRepository
3
- def put: (String bytes, canonicalization_version: untyped) -> String
4
- def fetch: (String content_id) -> String
5
- def exist?: (String content_id) -> bool
6
- end
7
-
8
2
  # Runtime/domain-facing repository interfaces.
9
3
  interface _AgentRepository
10
4
  def create: (untyped root) -> untyped
@@ -40,37 +34,6 @@ module Phronomy
40
34
  # Backend-side record repository interfaces. DurableRecord is the opaque data
41
35
  # carrier. Identity/revision/index metadata is passed separately so backends do
42
36
  # not need to interpret DurableRecord#payload.
43
- interface _BackendAgentRecordRepository
44
- def create: (agent_id: String, agent_revision: Integer, record: Persistence::DurableRecord) -> Persistence::DurableRecord
45
- def load: (String agent_id) -> Persistence::DurableRecord
46
- def save: (String agent_id, expected_revision: Integer, next_revision: Integer, record: Persistence::DurableRecord) -> Persistence::DurableRecord
47
- def delete: (String agent_id) -> untyped
48
- end
49
-
50
- interface _BackendJournalRecordRepository
51
- def append: (String agent_id, expected_position: Integer, records: Array[Persistence::DurableRecord], record_ids: Array[String]) -> Array[Persistence::DurableRecord]
52
- def read: (String agent_id, ?after: Integer?, ?limit: Integer?) -> Array[Persistence::DurableRecord]
53
- def head: (String agent_id) -> Integer
54
- def delete: (String agent_id) -> untyped
55
- end
56
-
57
- interface _BackendExecutionRecordRepository
58
- def create_active: (execution_id: String, agent_id: String, execution_revision: Integer, record: Persistence::DurableRecord) -> Persistence::DurableRecord
59
- def load: (String execution_id) -> Persistence::DurableRecord
60
- def save: (String execution_id, expected_revision: Integer, next_revision: Integer, agent_id: String, active: bool, record: Persistence::DurableRecord) -> Persistence::DurableRecord
61
- def list_active: (String agent_id) -> Array[Persistence::DurableRecord]
62
- def list: (String agent_id, ?after: String?, ?limit: Integer) -> Array[Persistence::DurableRecord]
63
- def delete: (String execution_id) -> untyped
64
- def delete_for_agent: (String agent_id) -> untyped
65
- def assert_idle!: (String agent_id) -> untyped
66
- end
67
-
68
- interface _BackendWorkflowStateRecordRepository
69
- def load: (String workflow_instance_id) -> Persistence::DurableRecord?
70
- def save: (String workflow_instance_id, expected_revision: Integer?, next_revision: Integer, record: Persistence::DurableRecord) -> Persistence::DurableRecord
71
- def delete: (String workflow_instance_id, expected_revision: Integer) -> untyped
72
- end
73
-
74
37
  interface _HandoffStateRepository
75
38
  def load: (String main_agent_id) -> untyped
76
39
  def save: (String main_agent_id, expected_revision: Integer?, state: untyped) -> untyped
@@ -95,34 +58,6 @@ module Phronomy
95
58
  def assert_idle!: (String team_id) -> untyped
96
59
  end
97
60
 
98
- interface _BackendHandoffStateRecordRepository
99
- def load: (String main_agent_id) -> Persistence::DurableRecord?
100
- def save: (String main_agent_id, expected_revision: Integer?, next_revision: Integer, active_agent_id: String, record: Persistence::DurableRecord) -> Persistence::DurableRecord
101
- def delete: (String main_agent_id, expected_revision: Integer) -> untyped
102
- end
103
-
104
- interface _BackendTeamRecordRepository
105
- def create: (team_id: String, team_revision: Integer, record: Persistence::DurableRecord) -> Persistence::DurableRecord
106
- def load: (String team_id) -> Persistence::DurableRecord
107
- def save: (String team_id, expected_revision: Integer, next_revision: Integer, record: Persistence::DurableRecord) -> Persistence::DurableRecord
108
- def delete: (String team_id) -> untyped
109
- end
110
-
111
- interface _BackendTeamExecutionRecordRepository
112
- def create_active: (team_execution_id: String, team_id: String, execution_revision: Integer, record: Persistence::DurableRecord) -> Persistence::DurableRecord
113
- def load: (String team_execution_id) -> Persistence::DurableRecord
114
- def save: (String team_execution_id, expected_revision: Integer, next_revision: Integer, team_id: String, active: bool, record: Persistence::DurableRecord) -> Persistence::DurableRecord
115
- def list_active: (String team_id) -> Array[Persistence::DurableRecord]
116
- def list: (String team_id, ?after: String?, ?limit: Integer) -> Array[Persistence::DurableRecord]
117
- def delete: (String team_execution_id) -> untyped
118
- def delete_for_team: (String team_id) -> untyped
119
- def assert_idle!: (String team_id) -> untyped
120
- end
121
-
122
- interface _BackendWatermark
123
- def assert_agent_watermark!: (agent_id: String, agent_revision: Integer, journal_position: Integer) -> true
124
- end
125
-
126
61
  interface _PersistenceTransactionView
127
62
  def contents: () -> _ContentRepository
128
63
  def agents: () -> _AgentRepository
@@ -136,36 +71,16 @@ module Phronomy
136
71
  end
137
72
 
138
73
  class Persistence
139
- class ConflictError < Phronomy::Error
140
- end
141
- class NotFoundError < Phronomy::Error
142
- end
143
- class UnsupportedBackendError < Phronomy::Error
144
- end
145
- class SerializationError < Phronomy::Error
146
- end
147
-
148
- class DurableRecord
149
- FORMAT_VERSION_PATTERN: Regexp
150
- attr_reader record_type: String
151
- attr_reader format_version: String
152
- attr_reader payload: Hash[String, untyped]
153
- def initialize: (record_type: String, format_version: String, payload: Hash[String, untyped]) -> void
154
- def copy: () -> DurableRecord
155
- end
156
-
157
74
  module Migration
158
75
  module InitialFormatMigration
159
- def self.agent_root: (Hash[untyped, untyped] hash) -> DurableRecord
160
- def self.agent_execution: (Hash[untyped, untyped] hash) -> DurableRecord
161
- def self.journal_record: (Hash[untyped, untyped] hash) -> DurableRecord
162
- def self.workflow_state: (workflow_instance_id: String, revision: Integer, snapshot: Hash[untyped, untyped]) -> DurableRecord
76
+ def self.agent_root: (Hash[untyped, untyped] hash) -> Storage::DurableRecord
77
+ def self.agent_execution: (Hash[untyped, untyped] hash) -> Storage::DurableRecord
78
+ def self.journal_record: (Hash[untyped, untyped] hash) -> Storage::DurableRecord
79
+ def self.workflow_state: (workflow_instance_id: String, revision: Integer, snapshot: Hash[untyped, untyped]) -> Storage::DurableRecord
163
80
  def self.llm_input_manifest: (Hash[untyped, untyped] hash) -> Hash[String, untyped]
164
81
  end
165
82
  end
166
83
 
167
- REQUIRED_CAPABILITIES: Hash[Symbol, bool]
168
-
169
84
  attr_reader contents: _ContentRepository
170
85
  attr_reader agents: _AgentRepository
171
86
  attr_reader journals: _JournalRepository
@@ -180,10 +95,11 @@ module Phronomy
180
95
  def team_execution_result: (String team_execution_id) -> Hash[Symbol, untyped]
181
96
  def list_team_executions: (String team_id, ?after: String?, ?limit: Integer) -> Array[untyped]
182
97
 
183
- def initialize: (contents: _ContentRepository, agents: _BackendAgentRecordRepository, journals: _BackendJournalRecordRepository, executions: _BackendExecutionRecordRepository, workflow_states: _BackendWorkflowStateRecordRepository, handoff_states: _BackendHandoffStateRecordRepository, teams: _BackendTeamRecordRepository, team_executions: _BackendTeamExecutionRecordRepository) -> void
98
+ attr_reader backend: _StorageBackend
99
+ def initialize: (backend: _StorageBackend) -> void
100
+ def self.in_memory: () -> Persistence
184
101
  def capabilities: () -> Hash[Symbol, bool]
185
102
  def transaction: () { (_PersistenceTransactionView) -> untyped } -> untyped
186
- def build_transaction_view: (contents: _ContentRepository, agents: _BackendAgentRecordRepository, journals: _BackendJournalRecordRepository, executions: _BackendExecutionRecordRepository, workflow_states: _BackendWorkflowStateRecordRepository, handoff_states: _BackendHandoffStateRecordRepository, teams: _BackendTeamRecordRepository, team_executions: _BackendTeamExecutionRecordRepository, watermark: _BackendWatermark) -> _PersistenceTransactionView
187
103
  def assert_agent_watermark!: (agent_id: String, agent_revision: Integer, journal_position: Integer) -> true
188
104
  end
189
105
  end
@@ -1,17 +1,44 @@
1
1
  module Phronomy
2
- class Task[A]
3
- def self.completed: [V] (V value, ?name: String?) -> Task[V]
4
- | (?name: String?) -> Task[nil]
5
- def self.failed: (Exception error, ?name: String?) -> Task[untyped]
2
+ class TaskResult[A]
3
+ class Outcome[V]
4
+ attr_reader index: Integer
5
+ attr_reader status: (:completed | :failed | :cancelled | :unfinished)
6
+ attr_reader value: V?
7
+ attr_reader error: Exception?
8
+ end
9
+
10
+ def self.completed: [V] (V value, ?name: String?) -> TaskResult[V]
11
+ | (?name: String?) -> TaskResult[nil]
12
+ def self.failed: (Exception error, ?name: String?) -> TaskResult[untyped]
13
+ def self.all_settled: [V] (Array[TaskResult[V]] results) -> TaskResult[Array[Outcome[V]]]
6
14
  attr_reader name: String?
7
- attr_reader parent: Task[untyped]?
15
+ attr_reader parent: TaskResult[untyped]?
8
16
 
9
17
  def status: () -> (:pending | :completed | :failed | :cancelled)
10
18
  def done?: () -> bool
11
19
  def alive?: () -> bool
12
20
  def wait_result: (?timeout: Numeric?) -> A
13
21
  def on_complete: () { (A?, Exception?) -> void } -> self
14
- def map: [B] () { (A) -> B } -> Task[B]
22
+ def map: [B] () { (A) -> B } -> TaskResult[B]
23
+ def flat_map: [B] () { (A) -> TaskResult[B] } -> TaskResult[B]
24
+ end
25
+
26
+ class Execution
27
+ attr_reader invocation_context: InvocationContext
28
+
29
+ def self.run_async: [I, V] (Array[I] inputs, ?timeout: Numeric?, ?cancellation_token: Concurrency::CancellationToken?, ?invocation_context: InvocationContext?) { (I, Execution) -> TaskResult[V] } -> TaskResult[Array[TaskResult::Outcome[V]]]
30
+ def self.run: [I, V] (Array[I] inputs, ?timeout: Numeric?, ?cancellation_token: Concurrency::CancellationToken?, ?invocation_context: InvocationContext?) { (I, Execution) -> TaskResult[V] } -> Array[TaskResult::Outcome[V]]
31
+ def observe: [V] (TaskResult[V] source_result) -> TaskResult[V]
32
+ end
33
+
34
+ class ExecutionTimeoutError < TimeoutError
35
+ attr_reader outcomes: Array[TaskResult::Outcome[untyped]]
36
+ def initialize: (?String message, outcomes: Array[TaskResult::Outcome[untyped]]) -> void
37
+ end
38
+
39
+ class ExecutionCancellationError < CancellationError
40
+ attr_reader outcomes: Array[TaskResult::Outcome[untyped]]
41
+ def initialize: (?String message, outcomes: Array[TaskResult::Outcome[untyped]]) -> void
15
42
  end
16
43
 
17
44
  module Concurrency
@@ -44,6 +71,6 @@ end
44
71
 
45
72
  module Phronomy
46
73
  module Blocking
47
- def self.call_async: [V] (?timeout: Numeric?, ?cancellation_token: Concurrency::CancellationToken?) { () -> V } -> Task[V]
74
+ def self.call_async: [V] (?timeout: Numeric?, ?cancellation_token: Concurrency::CancellationToken?, ?invocation_context: InvocationContext?) { () -> V } -> TaskResult[V]
48
75
  end
49
76
  end