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,89 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ class GeneratorVerifier
5
+ # Converts Agent events to correlated Workflow events. PipelineState owns
6
+ # stale-result rejection and all Workflow state mutation.
7
+ class AgentResultReceiver
8
+ def initialize(draft_result_parser:, review_result_parser:)
9
+ @draft_result_parser = draft_result_parser
10
+ @review_result_parser = review_result_parser
11
+ end
12
+
13
+ def draft_listener(workflow:, workflow_instance_id:, request_id:)
14
+ agent_listener(:draft, workflow, workflow_instance_id, request_id) do |output|
15
+ draft_payload(output)
16
+ end
17
+ end
18
+
19
+ def review_listener(workflow:, workflow_instance_id:, request_id:)
20
+ agent_listener(:review, workflow, workflow_instance_id, request_id) do |output|
21
+ review_payload(output)
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def draft_payload(output)
28
+ parsed = @draft_result_parser.call(output)
29
+ {
30
+ draft: parsed[:answer].to_s,
31
+ self_score: clamp(parsed[:confidence]),
32
+ citations: normalize_citations(parsed[:citations])
33
+ }
34
+ end
35
+
36
+ def review_payload(output)
37
+ parsed = @review_result_parser.call(output)
38
+ {
39
+ review_score: clamp(parsed[:score]),
40
+ approved: parsed[:approved] == true,
41
+ feedback: parsed[:feedback].to_s
42
+ }
43
+ end
44
+
45
+ def agent_listener(phase, workflow, workflow_instance_id, request_id, &completion)
46
+ ->(agent_event) {
47
+ case agent_event.type
48
+ when :done
49
+ begin
50
+ payload = completion.call(agent_event.payload[:output])
51
+ workflow.signal(
52
+ workflow_instance_id: workflow_instance_id,
53
+ event: :"#{phase}_completed",
54
+ payload: {request_id: request_id, **payload}
55
+ )
56
+ rescue => error
57
+ signal_failure(workflow, workflow_instance_id, request_id, phase, error)
58
+ end
59
+ when :error, :timeout, :cancelled
60
+ error = agent_event.payload[:error] ||
61
+ Phronomy::Error.new("#{phase.to_s.capitalize} Agent ended with #{agent_event.type}")
62
+ signal_failure(workflow, workflow_instance_id, request_id, phase, error)
63
+ when :approval_required
64
+ error = Phronomy::Error.new("GeneratorVerifier #{phase} Agent suspended for approval")
65
+ signal_failure(workflow, workflow_instance_id, request_id, phase, error)
66
+ end
67
+ }
68
+ end
69
+
70
+ def signal_failure(workflow, workflow_instance_id, request_id, phase, error)
71
+ workflow.signal(
72
+ workflow_instance_id: workflow_instance_id,
73
+ event: :"#{phase}_failed",
74
+ payload: {request_id: request_id, error: error}
75
+ )
76
+ end
77
+
78
+ def clamp(value)
79
+ value.to_f.clamp(0.0, 1.0)
80
+ end
81
+
82
+ def normalize_citations(raw)
83
+ Array(raw).filter_map do |citation|
84
+ citation.is_a?(Hash) ? citation.transform_keys(&:to_sym) : nil
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ class GeneratorVerifier
5
+ class PipelineState
6
+ include Phronomy::WorkflowContext
7
+
8
+ field :input, type: :replace, default: -> { "" }
9
+ field :draft, type: :replace, default: -> { {} }
10
+ field :self_score, type: :replace, default: -> { 0.0 }
11
+ field :review_score, type: :replace, default: -> { 0.0 }
12
+ field :citations, type: :replace, default: -> { [] }
13
+ field :review_notes, type: :append, default: -> { [] }
14
+ field :iteration, type: :replace, default: -> { 0 }
15
+ field :approved, type: :replace, default: -> { false }
16
+ field :output, type: :replace, default: -> { {} }
17
+ field :draft_request_id, type: :replace, default: nil
18
+ field :review_request_id, type: :replace, default: nil
19
+ field :pipeline_error, type: :replace, default: nil
20
+
21
+ # Application-level event interpretation. Correlation IDs belong to this
22
+ # Pipeline rather than to the generic FSMSession.
23
+ def handle_fsm_event(event)
24
+ case event.type
25
+ when :draft_completed
26
+ return :consume unless event.payload[:request_id] == draft_request_id
27
+
28
+ self.draft = event.payload[:draft]
29
+ self.self_score = event.payload[:self_score]
30
+ self.citations = event.payload[:citations]
31
+ self.iteration = iteration + 1
32
+ self.draft_request_id = nil
33
+ self.pipeline_error = nil
34
+ when :review_completed
35
+ return :consume unless event.payload[:request_id] == review_request_id
36
+
37
+ self.review_score = event.payload[:review_score]
38
+ self.approved = event.payload[:approved]
39
+ self.review_notes = review_notes + [event.payload[:feedback]]
40
+ self.review_request_id = nil
41
+ self.pipeline_error = nil
42
+ when :draft_failed
43
+ return :consume unless event.payload[:request_id] == draft_request_id
44
+
45
+ self.pipeline_error = event.payload[:error]
46
+ self.draft_request_id = nil
47
+ when :review_failed
48
+ return :consume unless event.payload[:request_id] == review_request_id
49
+
50
+ self.pipeline_error = event.payload[:error]
51
+ self.review_request_id = nil
52
+ end
53
+ false
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "securerandom"
4
+
5
+ module Phronomy
6
+ class GeneratorVerifier
7
+ # Builds the generation/review loop; result reception does not mutate state.
8
+ class WorkflowBuilder
9
+ def initialize(
10
+ draft_agent:, review_agent:,
11
+ draft_prompt_builder:, review_prompt_builder:,
12
+ draft_result_parser:, review_result_parser:,
13
+ threshold:, max_iterations:
14
+ )
15
+ @draft_agent = draft_agent
16
+ @review_agent = review_agent
17
+ @draft_prompt_builder = draft_prompt_builder
18
+ @review_prompt_builder = review_prompt_builder
19
+ @threshold = threshold
20
+ @max_iterations = max_iterations
21
+ @result_receiver = AgentResultReceiver.new(
22
+ draft_result_parser: draft_result_parser,
23
+ review_result_parser: review_result_parser
24
+ )
25
+ end
26
+
27
+ def build
28
+ # The entry closures see the Workflow assigned after definition.
29
+ workflow = nil
30
+ draft_entry = ->(state) { start_draft(state, workflow) }
31
+ review_entry = ->(state) { start_review(state, workflow) }
32
+ finalize_entry = method(:finalize)
33
+ failed_entry = method(:fail_pipeline)
34
+ ready_to_finalize = method(:ready_to_finalize?)
35
+
36
+ workflow = Phronomy::Workflow.define(PipelineState) do
37
+ initial :draft
38
+
39
+ state :draft
40
+ state :review
41
+ state :finalize
42
+ state :failed
43
+
44
+ entry :draft, draft_entry
45
+ entry :review, review_entry
46
+ entry :finalize, finalize_entry
47
+ entry :failed, failed_entry
48
+
49
+ transition from: :draft, on: :draft_completed, to: :review
50
+ transition from: :draft, on: :draft_failed, to: :failed
51
+ transition from: :review,
52
+ on: :review_completed,
53
+ guard: ready_to_finalize,
54
+ to: :finalize
55
+ transition from: :review, on: :review_completed, to: :draft
56
+ transition from: :review, on: :review_failed, to: :failed
57
+
58
+ transition from: :finalize, to: :__finish__
59
+ end
60
+ end
61
+
62
+ private
63
+
64
+ def start_draft(state, workflow)
65
+ request_id = SecureRandom.uuid
66
+ next_state = state.merge(draft_request_id: request_id)
67
+ feedback = next_state.review_notes.last
68
+ prompt = @draft_prompt_builder.call(next_state.input, feedback)
69
+ listener = @result_receiver.draft_listener(
70
+ workflow: workflow,
71
+ workflow_instance_id: next_state.workflow_instance_id,
72
+ request_id: request_id
73
+ )
74
+ @draft_agent.send(:__invoke_async_with_event_sink, prompt, on_event: listener)
75
+ next_state
76
+ end
77
+
78
+ def start_review(state, workflow)
79
+ request_id = SecureRandom.uuid
80
+ next_state = state.merge(review_request_id: request_id)
81
+ prompt = @review_prompt_builder.call(
82
+ next_state.input, next_state.draft, next_state.citations
83
+ )
84
+ listener = @result_receiver.review_listener(
85
+ workflow: workflow,
86
+ workflow_instance_id: next_state.workflow_instance_id,
87
+ request_id: request_id
88
+ )
89
+ @review_agent.send(:__invoke_async_with_event_sink, prompt, on_event: listener)
90
+ next_state
91
+ end
92
+
93
+ def ready_to_finalize?(state)
94
+ confidence = [state.self_score || 0.0, state.review_score || 0.0].min
95
+ (confidence >= @threshold && state.approved) ||
96
+ state.iteration >= @max_iterations
97
+ end
98
+
99
+ def finalize(state)
100
+ state.output = state.draft
101
+ state
102
+ end
103
+
104
+ def fail_pipeline(state)
105
+ raise(
106
+ state.pipeline_error ||
107
+ Phronomy::Error.new("GeneratorVerifier Agent operation failed")
108
+ )
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,118 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ # Implements the Generator-Verifier multi-agent coordination pattern.
5
+ #
6
+ # Agent completion is integrated through application-defined Workflow events;
7
+ # Workflow entry actions do not return or await Agent Tasks.
8
+ class GeneratorVerifier
9
+ DEFAULT_CONFIDENCE_THRESHOLD = 0.7
10
+ DEFAULT_MAX_ITERATIONS = 3
11
+
12
+ Result = Struct.new(
13
+ :output,
14
+ :confidence,
15
+ :citations,
16
+ :iterations,
17
+ :review_notes,
18
+ :trusted
19
+ ) do
20
+ alias_method :trusted?, :trusted
21
+ end
22
+
23
+ private_constant :PipelineState, :WorkflowBuilder, :AgentResultReceiver
24
+
25
+ def initialize(
26
+ draft_agent:,
27
+ review_agent:,
28
+ draft_prompt_builder:,
29
+ review_prompt_builder:,
30
+ draft_result_parser: nil,
31
+ review_result_parser: nil,
32
+ confidence_threshold: DEFAULT_CONFIDENCE_THRESHOLD,
33
+ max_iterations: DEFAULT_MAX_ITERATIONS,
34
+ raise_if_untrusted: false
35
+ )
36
+ @draft_agent_class = draft_agent
37
+ @review_agent_class = review_agent
38
+ @draft_prompt_builder = draft_prompt_builder
39
+ @review_prompt_builder = review_prompt_builder
40
+ @draft_result_parser =
41
+ draft_result_parser || method(:default_parse_draft)
42
+ @review_result_parser =
43
+ review_result_parser || method(:default_parse_review)
44
+ @threshold = confidence_threshold.to_f
45
+ @max_iterations = max_iterations.to_i
46
+ @raise_if_untrusted = raise_if_untrusted
47
+ @compiled_workflow = nil
48
+ end
49
+
50
+ def invoke(input, config: {})
51
+ state = compiled_workflow.invoke({input: input}, config: config)
52
+ confidence = combined_confidence(state)
53
+ trusted = confidence >= @threshold
54
+ result = Result.new(
55
+ output: state.output || state.draft.to_s,
56
+ confidence: confidence,
57
+ citations: state.citations,
58
+ iterations: state.iteration,
59
+ review_notes: state.review_notes,
60
+ trusted: trusted
61
+ )
62
+ if @raise_if_untrusted && !trusted
63
+ raise LowConfidenceError.new(result)
64
+ end
65
+ result
66
+ end
67
+
68
+ private
69
+
70
+ def combined_confidence(state)
71
+ [
72
+ (state.self_score || 0.0).to_f,
73
+ (state.review_score || 0.0).to_f
74
+ ].min
75
+ end
76
+
77
+ def compiled_workflow
78
+ @compiled_workflow ||= build_workflow
79
+ end
80
+
81
+ def build_workflow
82
+ WorkflowBuilder.new(
83
+ draft_agent: @draft_agent_class.new,
84
+ review_agent: @review_agent_class.new,
85
+ draft_prompt_builder: @draft_prompt_builder,
86
+ review_prompt_builder: @review_prompt_builder,
87
+ draft_result_parser: @draft_result_parser,
88
+ review_result_parser: @review_result_parser,
89
+ threshold: @threshold,
90
+ max_iterations: @max_iterations
91
+ ).build
92
+ end
93
+
94
+ def default_parse_draft(text)
95
+ json_parser.parse(text)
96
+ rescue Phronomy::ParseError
97
+ {
98
+ answer: text.to_s,
99
+ confidence: 0.0,
100
+ citations: []
101
+ }
102
+ end
103
+
104
+ def default_parse_review(text)
105
+ json_parser.parse(text)
106
+ rescue Phronomy::ParseError
107
+ {
108
+ approved: false,
109
+ score: 0.0,
110
+ feedback: "Review output could not be parsed: #{text}"
111
+ }
112
+ end
113
+
114
+ def json_parser
115
+ @json_parser ||= Phronomy::OutputParser::JsonParser.new
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../common/error"
4
+
5
+ module Phronomy
6
+ class LowConfidenceError < Error
7
+ attr_reader :result
8
+
9
+ def initialize(result)
10
+ @result = result
11
+ super("Answer confidence #{result.confidence} is below the required threshold")
12
+ end
13
+ end
14
+ end
@@ -2,20 +2,19 @@
2
2
 
3
3
  module Phronomy
4
4
  module LlmContextWindow
5
- # Immutable arithmetic value for one resolved model context budget.
6
- # Model-registry lookup belongs to Agent::TokenBudgetResolver.
5
+ # Immutable input-budget arithmetic. RubyLLM owns model capabilities.
7
6
  class TokenBudget
8
- attr_reader :context_window, :max_output_tokens
7
+ attr_reader :max_input_tokens
9
8
 
10
9
  # @api private
11
- def initialize(context_window:, max_output_tokens:)
12
- @context_window = Integer(context_window)
13
- @max_output_tokens = Integer(max_output_tokens)
10
+ def initialize(max_input_tokens:)
11
+ @max_input_tokens = Integer(max_input_tokens)
12
+ raise ArgumentError, "max_input_tokens must be positive" unless @max_input_tokens.positive?
14
13
  end
15
14
 
16
15
  # @api private
17
16
  def effective_input_limit
18
- [@context_window - @max_output_tokens, 0].max
17
+ @max_input_tokens
19
18
  end
20
19
 
21
20
  # @api private
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "transport_error"
4
+
5
+ module Phronomy
6
+ class AuthenticationError < TransportError; end
7
+ end
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../common/error"
4
+
3
5
  module Phronomy
4
6
  # Raised when mandatory context content (system instructions, tool definitions,
5
7
  # current input) exhausts the model's context window, leaving no room for
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../common/error"
4
+
5
+ module Phronomy
6
+ class ContextLengthError < Error; end
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "transport_error"
4
+
5
+ module Phronomy
6
+ class RateLimitError < TransportError; end
7
+ end
@@ -43,8 +43,8 @@ module Phronomy
43
43
  new(
44
44
  input: tokens.input || 0,
45
45
  output: tokens.output || 0,
46
- cached: tokens.cached || 0,
47
- cache_creation: tokens.cache_creation || 0
46
+ cached: tokens.cache_read || 0,
47
+ cache_creation: tokens.cache_write || 0
48
48
  )
49
49
  end
50
50
 
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../common/error"
4
+
5
+ module Phronomy
6
+ class TransportError < Error; end
7
+ end
@@ -2,18 +2,32 @@
2
2
 
3
3
  module Phronomy
4
4
  module MultiAgent
5
- # Runtime-local admission for one user-facing coordination turn per main Agent.
6
- # This owns exclusion only; active-agent mutation still belongs to EventLoop.
5
+ # Runtime-local admission for one synchronous coordination call per owner
6
+ # (a Handoff main Agent or TeamCoordinator). This does not own durable runs
7
+ # or active-agent mutation; those remain in their existing domains.
8
+ # @api private
7
9
  class AdmissionRegistry
10
+ # Construction has no side effects. Runtime returns the registered instance
11
+ # when callers concurrently supply candidates for the same key.
12
+ # @api private
13
+ def self.for(runtime)
14
+ runtime.__register_shutdown_participant(key: self, participant: new)
15
+ end
16
+
8
17
  def initialize
9
18
  @mutex = Mutex.new
10
19
  @cond = ConditionVariable.new
11
20
  @owners = {}
21
+ @draining = false
12
22
  end
13
23
 
14
24
  def admit!(coordinator)
15
25
  key = coordinator.object_id
16
26
  @mutex.synchronize do
27
+ if @draining
28
+ raise Phronomy::RuntimeShutdownError,
29
+ "Runtime is shutting down or failed; new Multi-Agent turns are not accepted"
30
+ end
17
31
  if @owners.key?(key)
18
32
  raise Phronomy::HandoffError,
19
33
  "a Multi-Agent turn is already active for this main Agent instance"
@@ -36,6 +50,12 @@ module Phronomy
36
50
  @mutex.synchronize { @owners.empty? }
37
51
  end
38
52
 
53
+ # Close admission atomically with respect to admit!, without waiting for
54
+ # admitted callers or calling back into Runtime. Repeated closure is safe.
55
+ def begin_draining
56
+ @mutex.synchronize { @draining = true }
57
+ end
58
+
39
59
  def wait_until_idle(deadline)
40
60
  @mutex.synchronize do
41
61
  until @owners.empty?
@@ -16,13 +16,13 @@ module Phronomy
16
16
  children.each do |child|
17
17
  begin
18
18
  exact = tx.executions.load(child.fetch("execution_id"))
19
- rescue Phronomy::Persistence::NotFoundError
19
+ rescue Phronomy::Storage::NotFoundError
20
20
  next
21
21
  end
22
- raise Phronomy::Persistence::ConflictError, "Child owner mismatch" unless exact.agent_id == child.fetch("agent_id")
22
+ raise Phronomy::Storage::ConflictError, "Child owner mismatch" unless exact.agent_id == child.fetch("agent_id")
23
23
  child.merge!("state" => exact.status.to_s, "result_ref" => exact.result_ref, "error_ref" => exact.error_ref)
24
24
  end
25
- Array(execution.metadata[Phronomy::Agent::RecoverySupport::TOOL_BATCH_METADATA_KEY]).each do |tool|
25
+ Array(execution.metadata[Phronomy::Agent::ExecutionMetadata::TOOL_BATCH_METADATA_KEY]).each do |tool|
26
26
  registration = parent.class.registered_subagents.find { |name, _| "dispatch_to_#{name}" == tool.fetch("tool_name") }
27
27
  next unless registration
28
28
  next if children.any? { |child| child.fetch("slot") == tool.fetch("tool_invocation_id") }
@@ -50,7 +50,7 @@ module Phronomy
50
50
  completion = Phronomy::TaskResult.deferred(name: "durable-subagent:#{tool_invocation_id}")
51
51
  preparation = runtime.offload.submit(on_full: :raise) do
52
52
  current = parent.persistence.executions.load(parent_execution_id)
53
- raise Phronomy::Persistence::ConflictError, "Parent owner mismatch" unless current.agent_id == parent.agent_id
53
+ raise Phronomy::Storage::ConflictError, "Parent owner mismatch" unless current.agent_id == parent.agent_id
54
54
  snapshot = parent.persistence.contents.fetch_json(current.metadata.fetch(KEY))
55
55
  child = snapshot.fetch("children").find { |entry| entry.fetch("slot") == tool_invocation_id }
56
56
  raise Phronomy::ExecutionRehydrationRequiredError, "Missing reserved child slot" unless child
@@ -65,7 +65,7 @@ module Phronomy
65
65
  begin
66
66
  parent.persistence.agents.load(id)
67
67
  exists = true
68
- rescue Phronomy::Persistence::NotFoundError
68
+ rescue Phronomy::Storage::NotFoundError
69
69
  exists = false
70
70
  end
71
71
  listener = parent.send(:_phronomy_event_listener)