phronomy 0.26.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 (304) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +40 -0
  3. data/README.md +4 -0
  4. data/VERIFY.sh +27 -27
  5. data/benchmark/bench_agent_invoke.rb +26 -22
  6. data/benchmark/bench_context_assembler.rb +4 -5
  7. data/docs/architecture/agent-chat-and-state-ownership.md +147 -0
  8. data/docs/architecture/agent-configuration-and-tool-binding.md +139 -0
  9. data/docs/architecture/agent-context.md +4 -2
  10. data/docs/architecture/agent-transition-ownership.md +89 -0
  11. data/docs/architecture/before-llm-input.md +6 -0
  12. data/docs/architecture/context-management.md +30 -0
  13. data/docs/architecture/context-preparation-steps.md +90 -0
  14. data/docs/architecture/entry-action-and-team-wording.md +72 -0
  15. data/docs/architecture/execution-metadata-and-values.md +80 -0
  16. data/docs/architecture/generator-verifier-ownership.md +111 -0
  17. data/docs/architecture/multi-agent-handoff.md +8 -2
  18. data/docs/architecture/persistence-refactoring-plan.md +50 -0
  19. data/docs/architecture/persistence.md +27 -4
  20. data/docs/architecture/refactoring-closure.md +334 -0
  21. data/docs/architecture/remaining-refactoring-plan.md +374 -0
  22. data/docs/architecture/rubyllm-2-token-ownership.md +82 -0
  23. data/docs/architecture/tool-schema-recording-gap.md +50 -0
  24. data/docs/architecture/tracing.md +1 -1
  25. data/docs/architecture/workflow-terminal-ownership-design.md +104 -0
  26. data/docs/architecture.md +209 -0
  27. data/docs/decisions/014-unified-persistence-durable-state.md +11 -0
  28. data/docs/decisions/024-event-loop-single-writer-agent-runtime.md +5 -1
  29. data/docs/decisions/025-process-local-agent-ownership-and-runtime-admission.md +13 -3
  30. data/docs/decisions/026-workflow-runtime-admission-and-durable-terminal-barrier.md +5 -1
  31. data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +5 -0
  32. data/docs/decisions/031-durable-multi-agent-coordination.md +5 -0
  33. data/docs/decisions/032-storage-backend-composition.md +80 -0
  34. data/docs/decisions/033-domain-persistence-ownership.md +79 -0
  35. data/docs/decisions/034-handoff-runner-coordination-ownership.md +76 -0
  36. data/docs/decisions/035-tool-executor-capability-ownership.md +68 -0
  37. data/docs/decisions/036-context-contract-ownership.md +86 -0
  38. data/docs/decisions/037-common-definition-ownership.md +62 -0
  39. data/docs/decisions/038-responsibility-based-source-layout.md +131 -0
  40. data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +74 -0
  41. data/docs/decisions/040-configuration-default-composition.md +94 -0
  42. data/docs/decisions/041-feature-owned-identity-registries.md +100 -0
  43. data/docs/decisions/042-feature-owned-execution-state.md +106 -0
  44. data/docs/decisions/043-storage-execution-constraint-notifications.md +75 -0
  45. data/docs/decisions/044-agent-default-and-one-shot-composition.md +106 -0
  46. data/docs/decisions/045-worker-input-restriction-ownership.md +100 -0
  47. data/docs/decisions/046-agent-responsibility-layout-and-shared-records.md +110 -0
  48. data/docs/decisions/047-recovered-execution-continuation-contract.md +116 -0
  49. data/docs/decisions/048-dispatch-preparation-worker-ownership.md +120 -0
  50. data/docs/decisions/049-initial-preparation-worker-ownership.md +111 -0
  51. data/docs/decisions/050-approval-resume-snapshot-and-commit-ownership.md +98 -0
  52. data/docs/decisions/051-execution-outcome-worker-ownership.md +151 -0
  53. data/docs/decisions/052-tool-invocation-restoration-ownership.md +92 -0
  54. data/docs/decisions/053-shared-state-coordination-ownership.md +71 -0
  55. data/docs/decisions/054-workflow-terminal-save-single-owner.md +76 -0
  56. data/docs/decisions/055-terminal-observer-failure-settlement.md +60 -0
  57. data/docs/decisions/056-workflow-terminal-policy-ownership.md +87 -0
  58. data/docs/decisions/057-storage-transaction-boundaries.md +73 -0
  59. data/docs/decisions/058-neutral-storage-primitives.md +78 -0
  60. data/docs/decisions/README.md +35 -6
  61. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_DESIGN_V2.md +6 -0
  62. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md +6 -0
  63. data/docs/features.md +9 -3
  64. data/docs/getting-started.md +4 -4
  65. data/docs/migrations/0.15.md +5 -0
  66. data/docs/migrations/durable-semantic-coordination-v2.md +6 -0
  67. data/docs/migrations/handoff-runner-multi-agent.md +35 -0
  68. data/docs/migrations/neutral-storage-spi.md +43 -0
  69. data/docs/migrations/parallel-tool-chat-removal.md +49 -0
  70. data/docs/migrations/shared-state-multi-agent.md +44 -0
  71. data/docs/migrations/storage-backend-composition.md +117 -0
  72. data/docs/migrations/storage-transaction-boundaries.md +74 -0
  73. data/docs/persistence-backends.md +162 -204
  74. data/docs/runtime-and-concurrency.md +117 -32
  75. data/lib/phronomy/agent/api/agent.rb +17 -0
  76. data/lib/phronomy/agent/async_event_api.rb +2 -2
  77. data/lib/phronomy/agent/base.rb +51 -241
  78. data/lib/phronomy/{agent.rb → agent/composition/run_once.rb} +4 -13
  79. data/lib/phronomy/agent/context/capability/base.rb +67 -26
  80. data/lib/phronomy/agent/context/capability/tool_executor.rb +62 -0
  81. data/lib/phronomy/agent/{context_assembler.rb → context_assembly/context_assembler.rb} +143 -88
  82. data/lib/phronomy/agent/{context_importer.rb → context_assembly/context_importer.rb} +2 -2
  83. data/lib/phronomy/agent/{ruby_llm_materializer.rb → context_assembly/ruby_llm_materializer.rb} +4 -7
  84. data/lib/phronomy/agent/context_assembly/runtime_chat_builder.rb +36 -0
  85. data/lib/phronomy/agent/context_assembly/saved_context_reader.rb +53 -0
  86. data/lib/phronomy/agent/context_assembly/state_writer.rb +165 -0
  87. data/lib/phronomy/agent/context_assembly/token_budget_resolver.rb +22 -0
  88. data/lib/phronomy/agent/{context_plan.rb → context_contract/context_plan.rb} +1 -1
  89. data/lib/phronomy/agent/{context_policy_input.rb → context_contract/context_policy_input.rb} +6 -6
  90. data/lib/phronomy/agent/{llm_input_build_context.rb → context_contract/llm_input_build_context.rb} +1 -1
  91. data/lib/phronomy/agent/{llm_input_manifest.rb → context_contract/llm_input_manifest.rb} +20 -20
  92. data/lib/phronomy/agent/{llm_input_patch.rb → context_contract/llm_input_patch.rb} +2 -2
  93. data/lib/phronomy/agent/{agent_execution.rb → execution/agent_execution.rb} +8 -4
  94. data/lib/phronomy/agent/{agent_invocation.rb → execution/agent_invocation.rb} +8 -13
  95. data/lib/phronomy/agent/{agent_invocation_session_builder.rb → execution/agent_invocation_session_builder.rb} +19 -95
  96. data/lib/phronomy/agent/execution/approval_resume_commit.rb +108 -0
  97. data/lib/phronomy/agent/execution/dispatch_preparation.rb +305 -0
  98. data/lib/phronomy/agent/{exact_execution.rb → execution/exact_execution.rb} +9 -10
  99. data/lib/phronomy/agent/{execution_cancellation.rb → execution/execution_cancellation.rb} +2 -3
  100. data/lib/phronomy/agent/execution/execution_coordinator.rb +1925 -0
  101. data/lib/phronomy/agent/execution/execution_failure.rb +30 -0
  102. data/lib/phronomy/agent/execution/execution_metadata.rb +53 -0
  103. data/lib/phronomy/agent/execution/execution_outcome_committer.rb +344 -0
  104. data/lib/phronomy/agent/execution/execution_registry.rb +459 -0
  105. data/lib/phronomy/agent/execution/execution_session_runner.rb +118 -0
  106. data/lib/phronomy/agent/execution/initial_preparation.rb +421 -0
  107. data/lib/phronomy/agent/execution/invocation_transitions.rb +86 -0
  108. data/lib/phronomy/agent/{phase_machine_builder.rb → execution/phase_machine_builder.rb} +20 -71
  109. data/lib/phronomy/agent/{provider_call_outcome.rb → execution/provider_call_outcome.rb} +9 -9
  110. data/lib/phronomy/agent/execution/runtime_record_encoder.rb +210 -0
  111. data/lib/phronomy/agent/{handoff_context.rb → handoff/handoff_context.rb} +2 -2
  112. data/lib/phronomy/agent/handoff/handoff_execution_coordinator.rb +15 -0
  113. data/lib/phronomy/agent/handoff/handoff_outcome_committer.rb +131 -0
  114. data/lib/phronomy/agent/{handoff_state.rb → handoff/handoff_state.rb} +1 -1
  115. data/lib/phronomy/agent/{journal_projection.rb → journal/journal_projection.rb} +4 -0
  116. data/lib/phronomy/agent/{journal_record.rb → journal/journal_record.rb} +3 -3
  117. data/lib/phronomy/agent/{llm_call_record.rb → journal/llm_call_record.rb} +2 -2
  118. data/lib/phronomy/agent/{agent_root.rb → lifecycle/agent_root.rb} +6 -2
  119. data/lib/phronomy/agent/lifecycle/default_persistence.rb +29 -0
  120. data/lib/phronomy/{engine/runtime/agent_ownership_registry.rb → agent/lifecycle/ownership_registry.rb} +24 -10
  121. data/lib/phronomy/{agent_already_exists_error.rb → agent/lifecycle_contract/agent_already_exists_error.rb} +2 -0
  122. data/lib/phronomy/{agent_busy_error.rb → agent/lifecycle_contract/agent_busy_error.rb} +2 -0
  123. data/lib/phronomy/{agent_purged_error.rb → agent/lifecycle_contract/agent_purged_error.rb} +2 -0
  124. data/lib/phronomy/agent/lifecycle_contract/handoff_error.rb +7 -0
  125. data/lib/phronomy/{stream_callback_error.rb → agent/lifecycle_contract/stream_callback_error.rb} +2 -0
  126. data/lib/phronomy/agent/persistence/agent_repository.rb +61 -0
  127. data/lib/phronomy/agent/persistence/codec.rb +358 -0
  128. data/lib/phronomy/agent/persistence/execution_repository.rb +108 -0
  129. data/lib/phronomy/agent/persistence/handoff_state_repository.rb +58 -0
  130. data/lib/phronomy/agent/persistence/journal_repository.rb +54 -0
  131. data/lib/phronomy/agent/persistence/queries.rb +61 -0
  132. data/lib/phronomy/agent/persistence/storage_schema.rb +24 -0
  133. data/lib/phronomy/agent/persistence/watermark.rb +27 -0
  134. data/lib/phronomy/agent/recovery/invocation_restorer.rb +132 -0
  135. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/continuation.rb +21 -45
  136. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/installation.rb +32 -37
  137. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/resolution.rb +37 -40
  138. data/lib/phronomy/agent/{recovery_coordinator.rb → recovery/recovery_coordinator.rb} +6 -10
  139. data/lib/phronomy/agent/recovery/recovery_support.rb +227 -0
  140. data/lib/phronomy/agent/selection/candidate.rb +1 -1
  141. data/lib/phronomy/agent/{approval_evaluation_request.rb → tool_execution/approval_evaluation_request.rb} +1 -12
  142. data/lib/phronomy/agent/{tool_approval_request.rb → tool_execution/tool_approval_request.rb} +1 -10
  143. data/lib/phronomy/agent/tool_execution/tool_binding.rb +90 -0
  144. data/lib/phronomy/agent/{tool_call_intercepted.rb → tool_execution/tool_call_intercepted.rb} +2 -2
  145. data/lib/phronomy/agent/{tool_definition_set.rb → tool_execution/tool_definition_set.rb} +9 -4
  146. data/lib/phronomy/agent/{tool_invocation.rb → tool_execution/tool_invocation.rb} +69 -34
  147. data/lib/phronomy/common/configuration_error.rb +7 -0
  148. data/lib/phronomy/common/error.rb +5 -0
  149. data/lib/phronomy/{agent → common/values}/immutable.rb +9 -1
  150. data/lib/phronomy/common/values/serializable.rb +32 -0
  151. data/lib/phronomy/{configuration.rb → configuration/configuration.rb} +14 -5
  152. data/lib/phronomy/configuration/global_configuration.rb +26 -0
  153. data/lib/phronomy/content_store/storage_schema.rb +11 -0
  154. data/lib/phronomy/content_store/stored_contents.rb +43 -0
  155. data/lib/phronomy/engine/backpressure_error.rb +7 -0
  156. data/lib/phronomy/{blocking.rb → engine/blocking.rb} +1 -1
  157. data/lib/phronomy/engine/cancellation_error.rb +7 -0
  158. data/lib/phronomy/engine/concurrency/cancellation_token.rb +4 -0
  159. data/lib/phronomy/engine/concurrency/offload_pool.rb +4 -0
  160. data/lib/phronomy/engine/concurrency/operation_binding.rb +1 -1
  161. data/lib/phronomy/engine/concurrency/worker_input_restricted.rb +14 -0
  162. data/lib/phronomy/engine/event_loop.rb +158 -624
  163. data/lib/phronomy/engine/event_loop_reentrancy_error.rb +8 -0
  164. data/lib/phronomy/{execution.rb → engine/execution.rb} +0 -7
  165. data/lib/phronomy/{execution_cancellation_error.rb → engine/execution_cancellation_error.rb} +2 -0
  166. data/lib/phronomy/engine/execution_receiver.rb +65 -0
  167. data/lib/phronomy/{execution_timeout_error.rb → engine/execution_timeout_error.rb} +2 -0
  168. data/lib/phronomy/engine/fsm_protocol.rb +14 -0
  169. data/lib/phronomy/engine/fsm_session.rb +35 -28
  170. data/lib/phronomy/{invalid_async_entry_action_error.rb → engine/invalid_async_entry_action_error.rb} +2 -0
  171. data/lib/phronomy/{invalid_async_transition_action_error.rb → engine/invalid_async_transition_action_error.rb} +2 -0
  172. data/lib/phronomy/{invalid_async_workflow_action_error.rb → engine/invalid_async_workflow_action_error.rb} +2 -0
  173. data/lib/phronomy/engine/pool_shutdown_error.rb +7 -0
  174. data/lib/phronomy/engine/recursion_limit_error.rb +7 -0
  175. data/lib/phronomy/engine/runtime.rb +80 -84
  176. data/lib/phronomy/engine/runtime_shutdown_error.rb +7 -0
  177. data/lib/phronomy/engine/runtime_shutdown_reentrancy_error.rb +7 -0
  178. data/lib/phronomy/engine/scheduler_reentrancy_error.rb +9 -0
  179. data/lib/phronomy/engine/task_result.rb +4 -0
  180. data/lib/phronomy/engine/timeout_error.rb +7 -0
  181. data/lib/phronomy/filter/contract/filter_block_error.rb +14 -0
  182. data/lib/phronomy/generation/generator_verifier/agent_result_receiver.rb +89 -0
  183. data/lib/phronomy/generation/generator_verifier/pipeline_state.rb +57 -0
  184. data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +112 -0
  185. data/lib/phronomy/generation/generator_verifier.rb +118 -0
  186. data/lib/phronomy/generation/low_confidence_error.rb +14 -0
  187. data/lib/phronomy/llm_context_window/token_budget.rb +6 -7
  188. data/lib/phronomy/llm_contract/authentication_error.rb +7 -0
  189. data/lib/phronomy/{context_budget_exceeded_error.rb → llm_contract/context_budget_exceeded_error.rb} +2 -0
  190. data/lib/phronomy/llm_contract/context_length_error.rb +7 -0
  191. data/lib/phronomy/llm_contract/rate_limit_error.rb +7 -0
  192. data/lib/phronomy/{token_usage.rb → llm_contract/token_usage.rb} +2 -2
  193. data/lib/phronomy/llm_contract/transport_error.rb +7 -0
  194. data/lib/phronomy/multi_agent/admission_registry.rb +22 -2
  195. data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +5 -5
  196. data/lib/phronomy/{agent → multi_agent}/handoff_runner.rb +18 -17
  197. data/lib/phronomy/multi_agent/orchestrator.rb +2 -2
  198. data/lib/phronomy/multi_agent/persistence/codec.rb +55 -0
  199. data/lib/phronomy/multi_agent/persistence/queries.rb +30 -0
  200. data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +108 -0
  201. data/lib/phronomy/multi_agent/persistence/team_repository.rb +61 -0
  202. data/lib/phronomy/{agent → multi_agent}/shared_state.rb +56 -39
  203. data/lib/phronomy/multi_agent/storage_contract/team_storage_schema.rb +15 -0
  204. data/lib/phronomy/multi_agent/team_coordinator.rb +21 -18
  205. data/lib/phronomy/multi_agent/team_execution.rb +1 -1
  206. data/lib/phronomy/{engine/runtime → multi_agent}/team_ownership_registry.rb +14 -4
  207. data/lib/phronomy/multi_agent/team_root.rb +1 -1
  208. data/lib/phronomy/output_parser/contract/parse_error.rb +7 -0
  209. data/lib/phronomy/persistence/api/persistence.rb +140 -0
  210. data/lib/phronomy/persistence/migration/initial_format_migration.rb +19 -19
  211. data/lib/phronomy/persistence_composition/repositories.rb +77 -0
  212. data/lib/phronomy/persistence_composition/storage_schema.rb +24 -0
  213. data/lib/phronomy/{execution_rehydration_required_error.rb → recovery/execution_rehydration_required_error.rb} +2 -0
  214. data/lib/phronomy/{recovery.rb → recovery/recovery.rb} +1 -1
  215. data/lib/phronomy/runtime_composition/agent_defaults.rb +7 -0
  216. data/lib/phronomy/runtime_composition/configuration_defaults.rb +9 -0
  217. data/lib/phronomy/runtime_composition/global_runtime.rb +19 -0
  218. data/lib/phronomy/storage/backend.rb +101 -0
  219. data/lib/phronomy/storage/backends/in_memory.rb +157 -0
  220. data/lib/phronomy/storage/blob_conflict_error.rb +10 -0
  221. data/lib/phronomy/storage/blobs.rb +31 -0
  222. data/lib/phronomy/storage/condition.rb +25 -0
  223. data/lib/phronomy/storage/condition_failed_error.rb +16 -0
  224. data/lib/phronomy/storage/conflict_error.rb +9 -0
  225. data/lib/phronomy/{persistence → storage}/durable_record.rb +12 -12
  226. data/lib/phronomy/storage/entry.rb +33 -0
  227. data/lib/phronomy/storage/guard_ref.rb +13 -0
  228. data/lib/phronomy/storage/not_found_error.rb +9 -0
  229. data/lib/phronomy/storage/record_codec.rb +177 -0
  230. data/lib/phronomy/storage/records.rb +61 -0
  231. data/lib/phronomy/storage/resource.rb +126 -0
  232. data/lib/phronomy/storage/scope.rb +25 -0
  233. data/lib/phronomy/storage/serialization_error.rb +9 -0
  234. data/lib/phronomy/storage/streams.rb +44 -0
  235. data/lib/phronomy/storage/transaction_error.rb +10 -0
  236. data/lib/phronomy/storage/unique_constraint_error.rb +17 -0
  237. data/lib/phronomy/storage/unsupported_backend_error.rb +9 -0
  238. data/lib/phronomy/storage/validation.rb +53 -0
  239. data/lib/phronomy/storage/view.rb +131 -0
  240. data/lib/phronomy/testing/persistence_contract/a_content_store.rb +1 -1
  241. data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +4 -4
  242. data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +10 -7
  243. data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +2 -2
  244. data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +6 -6
  245. data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +6 -6
  246. data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +8 -8
  247. data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +263 -0
  248. data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +123 -0
  249. data/lib/phronomy/testing/persistence_contract.rb +4 -0
  250. data/lib/phronomy/tool/contract/tool_error.rb +7 -0
  251. data/lib/phronomy/tools/agent.rb +1 -1
  252. data/lib/phronomy/version.rb +1 -1
  253. data/lib/phronomy/{workflow.rb → workflow/execution/workflow.rb} +8 -6
  254. data/lib/phronomy/{workflow_context.rb → workflow/execution/workflow_context.rb} +4 -0
  255. data/lib/phronomy/workflow/execution/workflow_context_ownership_error.rb +7 -0
  256. data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +188 -0
  257. data/lib/phronomy/{workflow_runner.rb → workflow/execution/workflow_runner.rb} +122 -71
  258. data/lib/phronomy/workflow/execution/workflow_terminal_policy.rb +40 -0
  259. data/lib/phronomy/workflow/persistence/codec.rb +153 -0
  260. data/lib/phronomy/workflow/persistence/state_repository.rb +57 -0
  261. data/lib/phronomy/workflow/phase_machine_builder.rb +2 -2
  262. data/lib/phronomy/workflow/storage_contract/workflow_storage_schema.rb +9 -0
  263. data/lib/phronomy.rb +52 -94
  264. data/scripts/api_snapshot.rb +1 -1
  265. data/scripts/storage_spi_snapshot.rb +36 -0
  266. data/sig/phronomy/agent.rbs +0 -1
  267. data/sig/phronomy/execution_receiver.rbs +34 -0
  268. data/sig/phronomy/handoff.rbs +4 -2
  269. data/sig/phronomy/multi_agent.rbs +18 -0
  270. data/sig/phronomy/persistence.rbs +7 -91
  271. data/sig/phronomy/storage.rbs +174 -0
  272. data/sig/phronomy/tool.rbs +10 -1
  273. metadata +230 -99
  274. data/lib/phronomy/agent/execution_coordinator.rb +0 -3151
  275. data/lib/phronomy/agent/handoff_execution_coordinator.rb +0 -143
  276. data/lib/phronomy/agent/recovery_support.rb +0 -504
  277. data/lib/phronomy/agent/token_budget_resolver.rb +0 -70
  278. data/lib/phronomy/agent/tool_executor.rb +0 -55
  279. data/lib/phronomy/generator_verifier.rb +0 -369
  280. data/lib/phronomy/invalid_context_budget_configuration_error.rb +0 -8
  281. data/lib/phronomy/multi_agent/parallel_tool_chat.rb +0 -116
  282. data/lib/phronomy/persistence/durable_codec.rb +0 -706
  283. data/lib/phronomy/persistence/in_memory.rb +0 -690
  284. data/lib/phronomy/persistence/repository_facades.rb +0 -535
  285. data/lib/phronomy/persistence.rb +0 -276
  286. data/lib/phronomy/ruby_llm_patches.rb +0 -24
  287. data/lib/phronomy/workflow_recovery.rb +0 -123
  288. /data/lib/phronomy/agent/{context_candidate_resolver.rb → context_assembly/context_candidate_resolver.rb} +0 -0
  289. /data/lib/phronomy/agent/{context_policy_input_builder.rb → context_assembly/context_policy_input_builder.rb} +0 -0
  290. /data/lib/phronomy/agent/{context_plan_validator.rb → context_contract/context_plan_validator.rb} +0 -0
  291. /data/lib/phronomy/agent/{context_policy.rb → context_contract/context_policy.rb} +0 -0
  292. /data/lib/phronomy/agent/{llm_operation_result.rb → execution/llm_operation_result.rb} +0 -0
  293. /data/lib/phronomy/agent/{handoff.rb → handoff/handoff.rb} +0 -0
  294. /data/lib/phronomy/agent/{handoff_capability_factory.rb → handoff/handoff_capability_factory.rb} +0 -0
  295. /data/lib/phronomy/agent/{handoff_policy.rb → handoff/handoff_policy.rb} +0 -0
  296. /data/lib/phronomy/agent/{handoff_projection.rb → handoff/handoff_projection.rb} +0 -0
  297. /data/lib/phronomy/agent/{handoff_request.rb → handoff/handoff_request.rb} +0 -0
  298. /data/lib/phronomy/agent/{tool_invocation_session_builder.rb → tool_execution/tool_invocation_session_builder.rb} +0 -0
  299. /data/lib/phronomy/{canonical_json.rb → common/canonical_json.rb} +0 -0
  300. /data/lib/phronomy/{diagnostics.rb → engine/diagnostics.rb} +0 -0
  301. /data/lib/phronomy/{event.rb → engine/event.rb} +0 -0
  302. /data/lib/phronomy/{invocation_context.rb → engine/invocation_context.rb} +0 -0
  303. /data/lib/phronomy/{metrics.rb → engine/metrics.rb} +0 -0
  304. /data/lib/phronomy/{runnable.rb → engine/runnable.rb} +0 -0
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../common/error"
4
+
5
+ module Phronomy
6
+ # Raised when a synchronous API would block the EventLoop control thread.
7
+ class EventLoopReentrancyError < Error; end
8
+ end
@@ -60,13 +60,6 @@ module Phronomy
60
60
  end
61
61
  private_class_method :__run_async
62
62
 
63
- # Common admission binding for Agent and synchronous-work adapters.
64
- # @api private
65
- def self.__operation_binding(invocation_context:, cancellation_token:)
66
- Concurrency::OperationBinding.new(invocation_context: invocation_context,
67
- cancellation_token: cancellation_token)
68
- end
69
-
70
63
  # A scoped observation copy. The source, its controls and its physical work
71
64
  # keep their original ownership. Bind before adding this run's map/flat_map.
72
65
  # @param source_result [TaskResult]
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "cancellation_error"
4
+
3
5
  module Phronomy
4
6
  class ExecutionCancellationError < CancellationError
5
7
  attr_reader :outcomes
@@ -0,0 +1,65 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ # Internal lifecycle/delivery contract for feature-owned execution state.
5
+ # Receivers are retained by one EventLoop, not an application callback SPI.
6
+ # Normal mutation and delivery run only on that loop. idle? runs with its
7
+ # lifecycle lock held; it must not call Runtime or acquire that lock again.
8
+ # shutdown runs on a failing loop, or exclusively after a clean loop joins.
9
+ # @api private
10
+ class ExecutionReceiver
11
+ def self.for(event_loop)
12
+ event_loop.__execution_receiver(key: self) ||
13
+ event_loop.__register_execution_receiver(key: self, receiver: new(event_loop: event_loop))
14
+ end
15
+
16
+ # Lookup only: neither a loop nor a receiver is created during inspection.
17
+ def self.existing_for(runtime)
18
+ runtime.__event_loop_if_initialized&.__execution_receiver(key: self)
19
+ end
20
+
21
+ def initialize(event_loop:)
22
+ @event_loop = event_loop
23
+ end
24
+
25
+ def __bound_to?(event_loop)
26
+ @event_loop.equal?(event_loop)
27
+ end
28
+
29
+ def idle?
30
+ raise NotImplementedError
31
+ end
32
+
33
+ def deliver(message)
34
+ raise NotImplementedError
35
+ end
36
+
37
+ def shutdown(error:)
38
+ raise NotImplementedError
39
+ end
40
+
41
+ def session_retired(fsm_session_id, reason:)
42
+ end
43
+
44
+ private
45
+
46
+ def synchronize(&block)
47
+ @event_loop.__synchronize_execution_state(&block)
48
+ end
49
+
50
+ def admit(&block)
51
+ @event_loop.__admit_execution(self, &block)
52
+ end
53
+
54
+ def post_message(message, admission: false, completion: nil)
55
+ @event_loop.__post_execution(self, message, admission: admission, completion: completion)
56
+ end
57
+
58
+ def assert_event_loop_thread!
59
+ return if @event_loop.current?
60
+
61
+ raise Phronomy::Error,
62
+ "Phronomy-managed live execution state may only be mutated on EventLoop"
63
+ end
64
+ end
65
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "timeout_error"
4
+
3
5
  module Phronomy
4
6
  class ExecutionTimeoutError < TimeoutError
5
7
  attr_reader :outcomes
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ # Shared terminal-state vocabulary and domain decisions for FSM execution.
5
+ # This protocol does not depend on a particular runner or session instance.
6
+ # @api private
7
+ module FSMProtocol
8
+ FINISH = :__end__
9
+
10
+ # A domain policy permits completion, reports failure, or retires a session
11
+ # without settling its caller. It never carries live session authority.
12
+ TerminalDecision = Data.define(:action, :error)
13
+ end
14
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "concurrency/worker_input_restricted"
4
+
3
5
  require "securerandom"
4
6
 
5
7
  module Phronomy
@@ -9,6 +11,8 @@ module Phronomy
9
11
  # FSMSession owns FSM execution only; it does not own external TaskResult handles,
10
12
  # activity tokens, callback correlation, or domain-specific stale-event policy.
11
13
  class FSMSession
14
+ include Phronomy::Concurrency::WorkerInputRestricted
15
+
12
16
  class IdentityReservation
13
17
  attr_reader :fsm_session_id
14
18
 
@@ -30,6 +34,8 @@ module Phronomy
30
34
  private_constant :IdentityReservation
31
35
 
32
36
  class EventSink
37
+ include Phronomy::Concurrency::WorkerInputRestricted
38
+
33
39
  attr_reader :fsm_session_id
34
40
 
35
41
  def initialize(event_loop:)
@@ -60,8 +66,6 @@ module Phronomy
60
66
  end
61
67
  end
62
68
 
63
- FINISH = WorkflowRunner::FINISH
64
-
65
69
  attr_reader :id, :context, :event_sink
66
70
 
67
71
  # Returns the live current Workflow phase. During an active FSM transition
@@ -95,7 +99,7 @@ module Phronomy
95
99
  context_metadata: {},
96
100
  event_sink: nil,
97
101
  identity_reservation: nil,
98
- terminal_barrier: nil
102
+ terminal_policy: nil
99
103
  )
100
104
  @id = if identity_reservation
101
105
  unless identity_reservation.is_a?(IdentityReservation)
@@ -123,7 +127,7 @@ module Phronomy
123
127
  @resume_event = resume_event
124
128
  @resume_phase = resume_phase
125
129
  @stable_observer = stable_observer
126
- @terminal_barrier = terminal_barrier
130
+ @terminal_policy = terminal_policy
127
131
  @terminal_lifecycle_state = :running
128
132
  @pending_terminal_type = nil
129
133
  @pending_terminal_notify_stable = false
@@ -160,14 +164,13 @@ module Phronomy
160
164
  def handle(event)
161
165
  return if @done
162
166
 
163
- if event.type == :workflow_terminal_persistence_result
164
- handle_terminal_persistence_result(event.payload)
167
+ if @terminal_policy&.handles?(event)
168
+ handle_terminal_event(event)
165
169
  return
166
170
  end
167
171
 
168
- # Once terminal persistence begins, ordinary Workflow events no longer
169
- # have result authority. Only the persistence completion for this concrete
170
- # FSMSession can advance its terminal lifecycle.
172
+ # Once a terminal decision is pending, ordinary events cannot advance
173
+ # the state machine. Only the injected policy can permit terminalization.
171
174
  return unless @terminal_lifecycle_state == :running
172
175
 
173
176
  context_disposition = apply_context_event(event)
@@ -261,7 +264,7 @@ module Phronomy
261
264
  end
262
265
 
263
266
  def advance_or_halt
264
- return finish! if @current_state == FINISH
267
+ return finish! if @current_state == FSMProtocol::FINISH
265
268
 
266
269
  # A wait state is the logical end of this Workflow execution segment. Its
267
270
  # public stable-state notification is therefore part of terminalization
@@ -324,7 +327,7 @@ module Phronomy
324
327
  def finish!(notify_stable: false)
325
328
  request_terminal!(
326
329
  :finished,
327
- phase: :__end__,
330
+ phase: FSMProtocol::FINISH,
328
331
  notify_stable: notify_stable
329
332
  )
330
333
  end
@@ -339,13 +342,13 @@ module Phronomy
339
342
  apply_context_metadata!(phase: phase)
340
343
  @pending_terminal_type = terminal_type
341
344
  @pending_terminal_notify_stable = notify_stable
342
- unless @terminal_barrier
345
+ unless @terminal_policy
343
346
  complete_terminal!(terminal_type)
344
347
  return
345
348
  end
346
349
 
347
- @terminal_lifecycle_state = :persisting_terminal
348
- @terminal_barrier.call(
350
+ @terminal_lifecycle_state = :awaiting_terminal
351
+ @terminal_policy.start(
349
352
  terminal_type: terminal_type,
350
353
  context: @ctx,
351
354
  event_sink: @event_sink
@@ -354,31 +357,35 @@ module Phronomy
354
357
  finish_with_error(error)
355
358
  end
356
359
 
357
- def handle_terminal_persistence_result(result)
358
- return unless @terminal_lifecycle_state == :persisting_terminal
360
+ def handle_terminal_event(event)
361
+ return unless @terminal_lifecycle_state == :awaiting_terminal
359
362
 
360
- case result.outcome
361
- when :success
363
+ decision = @terminal_policy.decision_for(event)
364
+ case decision.action
365
+ when :complete
362
366
  complete_terminal!(@pending_terminal_type)
363
- when :known_failure
364
- finish_with_error(
365
- result.error || Phronomy::Error.new("Workflow terminal persistence failed")
366
- )
367
- when :outcome_unknown
368
- @done = true
369
- @terminal_lifecycle_state = :recovery_required
370
- post_recovery_required_event(result.error)
367
+ when :fail
368
+ finish_with_error(decision.error)
369
+ when :retire
370
+ retire_without_result!(decision.error)
371
371
  else
372
372
  raise Phronomy::Error,
373
- "unknown Workflow terminal persistence outcome: #{result.outcome.inspect}"
373
+ "unknown FSM terminal decision: #{decision.action.inspect}"
374
374
  end
375
375
  end
376
376
 
377
- def complete_terminal!(terminal_type)
377
+ def retire_without_result!(error)
378
378
  @done = true
379
+ @terminal_lifecycle_state = :retired
380
+ post_recovery_required_event(error)
381
+ end
382
+
383
+ def complete_terminal!(terminal_type)
379
384
  @terminal_lifecycle_state =
380
385
  (terminal_type == :halted) ? :halted : :completed
386
+ # Keep the error path open until the observer returns successfully.
381
387
  notify_stable_state! if @pending_terminal_notify_stable
388
+ @done = true
382
389
  post_terminal_event(terminal_type, @ctx)
383
390
  end
384
391
 
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "invalid_async_workflow_action_error"
4
+
3
5
  module Phronomy
4
6
  # Raised when a synchronous FSM entry action returns Phronomy::TaskResult.
5
7
  #
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "invalid_async_workflow_action_error"
4
+
3
5
  module Phronomy
4
6
  # Raised when a synchronous Workflow transition action returns Phronomy::TaskResult.
5
7
  #
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../common/error"
4
+
3
5
  module Phronomy
4
6
  # Base error for synchronous Workflow callbacks that return Phronomy::TaskResult.
5
7
  #
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../common/error"
4
+
5
+ module Phronomy
6
+ class PoolShutdownError < Error; end
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../common/error"
4
+
5
+ module Phronomy
6
+ class RecursionLimitError < Error; end
7
+ end
@@ -1,13 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "concurrency/worker_input_restricted"
4
+
3
5
  require_relative "runtime/timer_queue"
4
6
  require_relative "runtime/shutdown_result"
5
7
  require_relative "runtime/timer_service"
6
- require_relative "runtime/agent_ownership_registry"
7
- require_relative "runtime/team_ownership_registry"
8
8
 
9
9
  module Phronomy
10
10
  class Runtime
11
+ include Phronomy::Concurrency::WorkerInputRestricted
12
+
11
13
  @instance_mutex = Mutex.new
12
14
 
13
15
  class << self
@@ -64,9 +66,8 @@ module Phronomy
64
66
  @pool_registry = Phronomy::Concurrency::PoolRegistry.new(
65
67
  timer_queue_provider: -> { timer_queue }
66
68
  )
67
- @multi_agent_admissions = Phronomy::MultiAgent::AdmissionRegistry.new
68
- @agent_ownership_registry = AgentOwnershipRegistry.new(runtime: self)
69
- @team_ownership_registry = TeamOwnershipRegistry.new
69
+ @shutdown_participants = {}
70
+ @shutdown_participant_error = nil
70
71
  @lifecycle_mutex = Mutex.new
71
72
  @shutdown_mutex = Mutex.new
72
73
  @state = :running
@@ -103,83 +104,40 @@ module Phronomy
103
104
  @timer_service.timer_queue
104
105
  end
105
106
 
106
- # Returns only an immutable process-local routing/ownership view. Mutable
107
- # execution state remains inside EventLoop and is never exposed to callers.
108
- # @api private
109
- def __agent_execution_owner(execution_id)
110
- loop_instance = @lifecycle_mutex.synchronize { @event_loop }
111
- loop_instance&.agent_execution_owner(execution_id)
112
- end
113
-
114
- # @api private
115
- def __team_owner(id, klass:, create:, persistence:, &block)
116
- @team_ownership_registry.fetch(id, klass: klass, create: create, persistence: persistence, &block)
117
- end
118
-
119
- # @api private
120
- def __get_team(id, klass:)
121
- @team_ownership_registry.get(id.to_s, klass: klass)
122
- end
123
-
124
- # @api private
125
- def __create_agent(agent_id, expected_class:, &block)
126
- @agent_ownership_registry.create(agent_id, expected_class: expected_class, &block)
127
- end
128
-
129
- # @api private
130
- def __load_agent(agent_id, expected_class:, &block)
131
- @agent_ownership_registry.load(agent_id, expected_class: expected_class, &block)
132
- end
133
-
134
- # @api private
135
- def __get_agent(agent_id, expected_class:)
136
- @agent_ownership_registry.get(agent_id, expected_class: expected_class)
137
- end
138
-
139
- # @api private
140
- def __agent_owned?(agent)
141
- @agent_ownership_registry.owned?(agent)
142
- end
143
-
144
- # @api private
145
- def __begin_agent_purge(agent)
146
- @agent_ownership_registry.begin_purge(agent)
147
- end
148
-
149
- # @api private
150
- def __complete_agent_purge(agent, token)
151
- @agent_ownership_registry.complete_purge(agent, token)
152
- end
153
-
154
- # @api private
155
- def __abort_agent_purge(agent, token)
156
- @agent_ownership_registry.abort_purge(agent, token)
157
- end
158
-
107
+ # Lookup only; inspection must not create an EventLoop during shutdown.
159
108
  # @api private
160
- def __leave_agent_purge_uncertain(agent, token)
161
- @agent_ownership_registry.leave_purge_uncertain(agent, token)
109
+ def __event_loop_if_initialized
110
+ @lifecycle_mutex.synchronize { @event_loop }
162
111
  end
163
112
 
113
+ # Shares one internal participant per key for this Runtime's lifetime.
114
+ # begin_draining must be idempotent, nonblocking and must not call Runtime:
115
+ # admission closes under the same lifecycle lock as registration.
116
+ # wait_until_idle receives an absolute monotonic deadline and returns a Boolean.
117
+ # Optional after_runtime_shutdown runs outside the lifecycle lock only after
118
+ # all waits succeed, EventLoop stops, and pools/timer finish shutdown. It must
119
+ # be idempotent, short, and perform no I/O. Exceptions mark cleanup incomplete.
120
+ # This is a shutdown contract, not an application extension point.
164
121
  # @api private
165
- def __agent_execution_admitted?(agent_id)
166
- loop_instance = @lifecycle_mutex.synchronize { @event_loop }
167
- loop_instance&.agent_execution_admitted?(agent_id) || false
168
- end
122
+ def __register_shutdown_participant(key:, participant:)
123
+ unless participant.respond_to?(:begin_draining) && participant.respond_to?(:wait_until_idle)
124
+ raise ArgumentError, "shutdown participant must implement begin_draining and wait_until_idle"
125
+ end
169
126
 
170
- # @api private
171
- def __admit_multi_agent(coordinator)
172
- current_state = @lifecycle_mutex.synchronize { @state }
173
- unless current_state == :running
174
- raise Phronomy::RuntimeShutdownError,
175
- "Runtime is #{current_state}; new Multi-Agent turns are not accepted"
127
+ @lifecycle_mutex.synchronize do
128
+ unless @state == :running
129
+ raise Phronomy::RuntimeShutdownError,
130
+ "Runtime is #{@state}; shutdown participants cannot be registered"
131
+ end
132
+ @shutdown_participants[key] ||= participant
176
133
  end
177
- @multi_agent_admissions.admit!(coordinator)
178
134
  end
179
135
 
136
+ # Lookup only: never registers a participant or reopens its admission gate.
137
+ # Retained participants remain available during and after shutdown.
180
138
  # @api private
181
- def __release_multi_agent(coordinator)
182
- @multi_agent_admissions.release!(coordinator)
139
+ def __shutdown_participant(key:)
140
+ @lifecycle_mutex.synchronize { @shutdown_participants[key] }
183
141
  end
184
142
 
185
143
  def event_loop
@@ -214,6 +172,7 @@ module Phronomy
214
172
 
215
173
  @failure ||= error
216
174
  @state = :failed
175
+ begin_draining_participants
217
176
  end
218
177
  end
219
178
 
@@ -232,17 +191,14 @@ module Phronomy
232
191
  return @shutdown_result if @shutdown_result
233
192
 
234
193
  drain_deadline = monotonic_now + timeout
235
- loop_instance = @lifecycle_mutex.synchronize do
194
+ loop_instance, participants = @lifecycle_mutex.synchronize do
236
195
  @state = :draining unless @state == :failed
237
- @event_loop
196
+ begin_draining_participants
197
+ [@event_loop, @shutdown_participants.values]
238
198
  end
239
199
  loop_instance&.begin_draining
240
- @agent_ownership_registry.begin_draining
241
- @team_ownership_registry.begin_draining
242
200
 
243
- admission_idle = @multi_agent_admissions.wait_until_idle(drain_deadline)
244
- agent_ownership_stable = @agent_ownership_registry.wait_until_stable(drain_deadline)
245
- team_ownership_stable = @team_ownership_registry.wait_until_stable(drain_deadline)
201
+ participants_idle = wait_for_participants(participants, drain_deadline)
246
202
  loop_idle = !loop_instance || loop_instance.wait_until_idle(drain_deadline)
247
203
 
248
204
  @lifecycle_mutex.synchronize do
@@ -257,11 +213,15 @@ module Phronomy
257
213
  end
258
214
 
259
215
  subsystem_error = shutdown_pools_and_timer
260
- cleanup_complete = admission_idle && agent_ownership_stable && team_ownership_stable && loop_idle &&
216
+ participant_error = @lifecycle_mutex.synchronize { @shutdown_participant_error }
217
+ cleanup_complete = participants_idle && participant_error.nil? &&
218
+ loop_idle &&
261
219
  (!loop_instance || !loop_instance.thread_alive?) &&
262
220
  event_loop_status != :cancel_timeout &&
221
+ (!loop_instance || loop_instance.__receiver_cleanup_complete?) &&
263
222
  subsystem_error.nil?
264
223
 
224
+ cleanup_complete = finalize_participants(participants) if cleanup_complete
265
225
  failure = @lifecycle_mutex.synchronize { @failure } || subsystem_error
266
226
  runtime_outcome = if failure || event_loop_status == :failed
267
227
  :failed
@@ -269,9 +229,6 @@ module Phronomy
269
229
  :terminated
270
230
  end
271
231
 
272
- @agent_ownership_registry.shutdown! if cleanup_complete
273
- @team_ownership_registry.shutdown! if cleanup_complete
274
-
275
232
  result = ShutdownResult.new(
276
233
  runtime_outcome: runtime_outcome,
277
234
  cleanup_status: cleanup_complete ? :complete : :incomplete,
@@ -290,6 +247,45 @@ module Phronomy
290
247
 
291
248
  private
292
249
 
250
+ # Called with the lifecycle lock held; participants only close their gates.
251
+ def begin_draining_participants
252
+ @shutdown_participants.each_value do |participant|
253
+ participant.begin_draining
254
+ rescue => error
255
+ record_participant_failure(error)
256
+ end
257
+ end
258
+
259
+ # All gates are already closed. Wait without holding the lifecycle lock and
260
+ # give every participant the same deadline, even if an earlier wait fails.
261
+ def wait_for_participants(participants, deadline)
262
+ participants.map do |participant|
263
+ participant.wait_until_idle(deadline) == true
264
+ rescue => error
265
+ @lifecycle_mutex.synchronize { record_participant_failure(error) }
266
+ false
267
+ end.all?
268
+ end
269
+
270
+ # Finalize every participant even if one fails. Earlier releases are not
271
+ # rolled back, and a failure prevents replacement of the default Runtime.
272
+ def finalize_participants(participants)
273
+ participants.map do |participant|
274
+ participant.after_runtime_shutdown if participant.respond_to?(:after_runtime_shutdown)
275
+ true
276
+ rescue => error
277
+ @lifecycle_mutex.synchronize { record_participant_failure(error) }
278
+ false
279
+ end.all?
280
+ end
281
+
282
+ # Called with the lifecycle lock held. Failed hooks make cleanup uncertain.
283
+ def record_participant_failure(error)
284
+ @shutdown_participant_error ||= error
285
+ @failure ||= error
286
+ @state = :failed
287
+ end
288
+
293
289
  def ensure_accepting_work!
294
290
  current_state = @lifecycle_mutex.synchronize { @state }
295
291
  return if %i[running draining].include?(current_state)
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../common/error"
4
+
5
+ module Phronomy
6
+ class RuntimeShutdownError < Error; end
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "runtime_shutdown_error"
4
+
5
+ module Phronomy
6
+ class RuntimeShutdownReentrancyError < RuntimeShutdownError; end
7
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "event_loop_reentrancy_error"
4
+
5
+ module Phronomy
6
+ # Backward-compatible error class name for callers that still rescue the old
7
+ # scheduler-oriented exception. New code should use EventLoopReentrancyError.
8
+ class SchedulerReentrancyError < EventLoopReentrancyError; end
9
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "concurrency/worker_input_restricted"
4
+
3
5
  module Phronomy
4
6
  # A thread-free asynchronous completion handle.
5
7
  #
@@ -13,6 +15,8 @@ module Phronomy
13
15
  # is supplied through the CancellationToken accepted by the API that created
14
16
  # the TaskResult.
15
17
  class TaskResult
18
+ include Phronomy::Concurrency::WorkerInputRestricted
19
+
16
20
  STATES = %i[pending completed failed cancelled].freeze
17
21
  TERMINAL_STATES = %i[completed failed cancelled].freeze
18
22
  private_constant :TERMINAL_STATES
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../common/error"
4
+
5
+ module Phronomy
6
+ class TimeoutError < Error; end
7
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../common/error"
4
+
5
+ module Phronomy
6
+ class FilterBlockError < Error
7
+ attr_reader :filter
8
+
9
+ def initialize(message, filter: nil)
10
+ super(message)
11
+ @filter = filter
12
+ end
13
+ end
14
+ end