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
@@ -1,70 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phronomy
4
- module Agent
5
- # Resolves a TokenBudget from the effective model configuration that will
6
- # actually be materialized for one LLM call.
7
- class TokenBudgetResolver
8
- def initialize(agent:)
9
- @agent = agent
10
- end
11
-
12
- def resolve(model_config)
13
- config = stringify_keys(model_config)
14
- model_name = config["model"]
15
- explicit_window = integer_or_nil(config["context_window"])
16
- explicit_reserve = integer_or_nil(config["max_output_tokens"])
17
-
18
- if explicit_window
19
- reserve = explicit_reserve || configured_default_reserve
20
- return build_budget(explicit_window, reserve, model_name)
21
- end
22
-
23
- return nil unless model_name
24
-
25
- model = RubyLLM.models.find(model_name)
26
- return nil unless model
27
-
28
- context_window = model.context_window.to_i
29
- registry_max = model.max_output_tokens.to_i
30
- reserve = explicit_reserve || configured_default_reserve
31
- reserve ||= registry_max if registry_max.positive? && registry_max < context_window
32
- build_budget(context_window, reserve, model_name)
33
- rescue RubyLLM::ModelNotFoundError
34
- nil
35
- end
36
-
37
- private
38
-
39
- def build_budget(context_window, reserve, model_name)
40
- unless reserve&.positive? && reserve < context_window
41
- raise Phronomy::InvalidContextBudgetConfigurationError,
42
- "Cannot determine a valid output reserve for model #{model_name.inspect}; " \
43
- "set max_output_tokens or Phronomy.configuration.default_output_reserve"
44
- end
45
-
46
- # Mandatory Manifest content is accounted separately by ContextAssembler
47
- # and the final budget validator. TokenBudget reserves output capacity only.
48
- Phronomy::LlmContextWindow::TokenBudget.new(
49
- context_window: context_window,
50
- max_output_tokens: reserve
51
- )
52
- end
53
-
54
- def configured_default_reserve
55
- integer_or_nil(Phronomy.configuration.default_output_reserve)
56
- end
57
-
58
- def integer_or_nil(value)
59
- return nil if value.nil?
60
- Integer(value)
61
- end
62
-
63
- def stringify_keys(hash)
64
- hash.to_h.each_with_object({}) do |(key, value), result|
65
- result[key.to_s] = value
66
- end
67
- end
68
- end
69
- end
70
- end
@@ -1,55 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phronomy
4
- module Agent
5
- # Routes Tool work according to the Tool execution contract.
6
- #
7
- # Both execution modes return {Phronomy::TaskResult}; only the execution mechanism
8
- # differs.
9
- #
10
- # :cooperative Tool calls execute inline and must return quickly. call_async
11
- # wraps their result in an already-settled TaskResult and never consumes an
12
- # OffloadPool worker.
13
- #
14
- # :offloaded Tool calls route synchronous work through OffloadPool, whose
15
- # caller-facing completion handle is also a TaskResult. Phronomy does not distinguish
16
- # whether the reason is blocking I/O, CPU-bound work, or another long
17
- # synchronous operation.
18
- module ToolExecutor
19
- def self.call_async(
20
- tool:,
21
- args:,
22
- cancellation_token: nil,
23
- config: {},
24
- runtime: nil,
25
- on_full: :raise
26
- )
27
- mode = tool.class.execution_mode
28
-
29
- case mode
30
- when :cooperative
31
- task = Phronomy::TaskResult.deferred(name: "tool-#{tool.name}")
32
- begin
33
- task.complete(
34
- tool.call(args, cancellation_token: cancellation_token)
35
- )
36
- rescue => error
37
- task.fail(error)
38
- end
39
- task
40
- when :offloaded
41
- runtime ||= Phronomy::Runtime.instance
42
- runtime.offload.submit(
43
- cancellation_token: cancellation_token,
44
- on_full: on_full
45
- ) do
46
- tool.call(args, cancellation_token: cancellation_token)
47
- end
48
- else
49
- raise Phronomy::ConfigurationError,
50
- "unknown Tool execution_mode: #{mode.inspect}"
51
- end
52
- end
53
- end
54
- end
55
- end
@@ -1,369 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "securerandom"
4
-
5
- module Phronomy
6
- # Implements the Generator-Verifier multi-agent coordination pattern.
7
- #
8
- # Agent completion is integrated through application-defined Workflow events;
9
- # Workflow entry actions do not return or await Agent Tasks.
10
- class GeneratorVerifier
11
- DEFAULT_CONFIDENCE_THRESHOLD = 0.7
12
- DEFAULT_MAX_ITERATIONS = 3
13
-
14
- Result = Struct.new(
15
- :output,
16
- :confidence,
17
- :citations,
18
- :iterations,
19
- :review_notes,
20
- :trusted
21
- ) do
22
- alias_method :trusted?, :trusted
23
- end
24
-
25
- class PipelineState
26
- include Phronomy::WorkflowContext
27
-
28
- field :input, type: :replace, default: -> { "" }
29
- field :draft, type: :replace, default: -> { {} }
30
- field :self_score, type: :replace, default: -> { 0.0 }
31
- field :review_score, type: :replace, default: -> { 0.0 }
32
- field :citations, type: :replace, default: -> { [] }
33
- field :review_notes, type: :append, default: -> { [] }
34
- field :iteration, type: :replace, default: -> { 0 }
35
- field :approved, type: :replace, default: -> { false }
36
- field :output, type: :replace, default: -> { {} }
37
- field :draft_request_id, type: :replace, default: nil
38
- field :review_request_id, type: :replace, default: nil
39
- field :pipeline_error, type: :replace, default: nil
40
-
41
- # Application-level event interpretation. Correlation IDs belong to this
42
- # Pipeline rather than to the generic FSMSession.
43
- def handle_fsm_event(event)
44
- case event.type
45
- when :draft_completed
46
- return :consume unless event.payload[:request_id] == draft_request_id
47
-
48
- self.draft = event.payload[:draft]
49
- self.self_score = event.payload[:self_score]
50
- self.citations = event.payload[:citations]
51
- self.iteration = iteration + 1
52
- self.draft_request_id = nil
53
- self.pipeline_error = nil
54
- when :review_completed
55
- return :consume unless event.payload[:request_id] == review_request_id
56
-
57
- self.review_score = event.payload[:review_score]
58
- self.approved = event.payload[:approved]
59
- self.review_notes = review_notes + [event.payload[:feedback]]
60
- self.review_request_id = nil
61
- self.pipeline_error = nil
62
- when :draft_failed
63
- return :consume unless event.payload[:request_id] == draft_request_id
64
-
65
- self.pipeline_error = event.payload[:error]
66
- self.draft_request_id = nil
67
- when :review_failed
68
- return :consume unless event.payload[:request_id] == review_request_id
69
-
70
- self.pipeline_error = event.payload[:error]
71
- self.review_request_id = nil
72
- end
73
- false
74
- end
75
- end
76
-
77
- private_constant :PipelineState
78
-
79
- def initialize(
80
- draft_agent:,
81
- review_agent:,
82
- draft_prompt_builder:,
83
- review_prompt_builder:,
84
- draft_result_parser: nil,
85
- review_result_parser: nil,
86
- confidence_threshold: DEFAULT_CONFIDENCE_THRESHOLD,
87
- max_iterations: DEFAULT_MAX_ITERATIONS,
88
- raise_if_untrusted: false
89
- )
90
- @draft_agent_class = draft_agent
91
- @review_agent_class = review_agent
92
- @draft_prompt_builder = draft_prompt_builder
93
- @review_prompt_builder = review_prompt_builder
94
- @draft_result_parser =
95
- draft_result_parser || method(:default_parse_draft)
96
- @review_result_parser =
97
- review_result_parser || method(:default_parse_review)
98
- @threshold = confidence_threshold.to_f
99
- @max_iterations = max_iterations.to_i
100
- @raise_if_untrusted = raise_if_untrusted
101
- @compiled_workflow = nil
102
- end
103
-
104
- def invoke(input, config: {})
105
- state = compiled_workflow.invoke({input: input}, config: config)
106
- confidence = combined_confidence(state)
107
- trusted = confidence >= @threshold
108
- result = Result.new(
109
- output: state.output || state.draft.to_s,
110
- confidence: confidence,
111
- citations: state.citations,
112
- iterations: state.iteration,
113
- review_notes: state.review_notes,
114
- trusted: trusted
115
- )
116
- if @raise_if_untrusted && !trusted
117
- raise LowConfidenceError.new(result)
118
- end
119
- result
120
- end
121
-
122
- private
123
-
124
- def combined_confidence(state)
125
- [
126
- (state.self_score || 0.0).to_f,
127
- (state.review_score || 0.0).to_f
128
- ].min
129
- end
130
-
131
- def compiled_workflow
132
- @compiled_workflow ||= build_workflow
133
- end
134
-
135
- def build_workflow
136
- draft_agent = @draft_agent_class.new
137
- review_agent = @review_agent_class.new
138
- threshold = @threshold
139
- max_iterations = @max_iterations
140
- draft_prompt_builder = @draft_prompt_builder
141
- review_prompt_builder = @review_prompt_builder
142
- draft_result_parser = @draft_result_parser
143
- review_result_parser = @review_result_parser
144
- pipeline = self
145
- workflow = nil
146
-
147
- # workflow is assigned after define so closures captured by reference see the result.
148
- workflow = Phronomy::Workflow.define(PipelineState) do
149
- initial :draft
150
-
151
- state :draft
152
- state :review
153
- state :finalize
154
- state :failed
155
-
156
- entry :draft, ->(state) {
157
- request_id = SecureRandom.uuid
158
- next_state = state.merge(draft_request_id: request_id)
159
- feedback = next_state.review_notes.last
160
- prompt = draft_prompt_builder.call(next_state.input, feedback)
161
-
162
- draft_agent.send(:__invoke_async_with_event_sink,
163
- prompt,
164
- on_event: ->(agent_event) {
165
- case agent_event.type
166
- when :done
167
- begin
168
- parsed = draft_result_parser.call(
169
- agent_event.payload[:output]
170
- )
171
- workflow.signal(
172
- workflow_instance_id: next_state.workflow_instance_id,
173
- event: :draft_completed,
174
- payload: {
175
- request_id: request_id,
176
- draft: parsed[:answer].to_s,
177
- self_score: pipeline.__send__(
178
- :clamp,
179
- parsed[:confidence]
180
- ),
181
- citations: pipeline.__send__(
182
- :normalize_citations,
183
- parsed[:citations]
184
- )
185
- }
186
- )
187
- rescue => error
188
- workflow.signal(
189
- workflow_instance_id: next_state.workflow_instance_id,
190
- event: :draft_failed,
191
- payload: {
192
- request_id: request_id,
193
- error: error
194
- }
195
- )
196
- end
197
- when :error, :timeout, :cancelled
198
- workflow.signal(
199
- workflow_instance_id: next_state.workflow_instance_id,
200
- event: :draft_failed,
201
- payload: {
202
- request_id: request_id,
203
- error:
204
- agent_event.payload[:error] ||
205
- Phronomy::Error.new(
206
- "Draft Agent ended with #{agent_event.type}"
207
- )
208
- }
209
- )
210
- when :approval_required
211
- workflow.signal(
212
- workflow_instance_id: next_state.workflow_instance_id,
213
- event: :draft_failed,
214
- payload: {
215
- request_id: request_id,
216
- error: Phronomy::Error.new(
217
- "GeneratorVerifier draft Agent suspended for approval"
218
- )
219
- }
220
- )
221
- end
222
- })
223
-
224
- next_state
225
- }
226
-
227
- entry :review, ->(state) {
228
- request_id = SecureRandom.uuid
229
- next_state = state.merge(review_request_id: request_id)
230
- prompt = review_prompt_builder.call(
231
- next_state.input,
232
- next_state.draft,
233
- next_state.citations
234
- )
235
-
236
- review_agent.send(:__invoke_async_with_event_sink,
237
- prompt,
238
- on_event: ->(agent_event) {
239
- case agent_event.type
240
- when :done
241
- begin
242
- parsed = review_result_parser.call(
243
- agent_event.payload[:output]
244
- )
245
- workflow.signal(
246
- workflow_instance_id: next_state.workflow_instance_id,
247
- event: :review_completed,
248
- payload: {
249
- request_id: request_id,
250
- review_score: pipeline.__send__(
251
- :clamp,
252
- parsed[:score]
253
- ),
254
- approved: parsed[:approved] == true,
255
- feedback: parsed[:feedback].to_s
256
- }
257
- )
258
- rescue => error
259
- workflow.signal(
260
- workflow_instance_id: next_state.workflow_instance_id,
261
- event: :review_failed,
262
- payload: {
263
- request_id: request_id,
264
- error: error
265
- }
266
- )
267
- end
268
- when :error, :timeout, :cancelled
269
- workflow.signal(
270
- workflow_instance_id: next_state.workflow_instance_id,
271
- event: :review_failed,
272
- payload: {
273
- request_id: request_id,
274
- error:
275
- agent_event.payload[:error] ||
276
- Phronomy::Error.new(
277
- "Review Agent ended with #{agent_event.type}"
278
- )
279
- }
280
- )
281
- when :approval_required
282
- workflow.signal(
283
- workflow_instance_id: next_state.workflow_instance_id,
284
- event: :review_failed,
285
- payload: {
286
- request_id: request_id,
287
- error: Phronomy::Error.new(
288
- "GeneratorVerifier review Agent suspended for approval"
289
- )
290
- }
291
- )
292
- end
293
- })
294
-
295
- next_state
296
- }
297
-
298
- entry :finalize, ->(state) {
299
- state.output = state.draft
300
- state
301
- }
302
-
303
- entry :failed, ->(state) {
304
- raise(
305
- state.pipeline_error ||
306
- Phronomy::Error.new("GeneratorVerifier Agent operation failed")
307
- )
308
- }
309
-
310
- transition from: :draft, on: :draft_completed, to: :review
311
- transition from: :draft, on: :draft_failed, to: :failed
312
-
313
- transition from: :review,
314
- on: :review_completed,
315
- guard: ->(state) {
316
- confidence = [
317
- state.self_score || 0.0,
318
- state.review_score || 0.0
319
- ].min
320
- (confidence >= threshold && state.approved) ||
321
- state.iteration >= max_iterations
322
- },
323
- to: :finalize
324
- transition from: :review,
325
- on: :review_completed,
326
- to: :draft
327
- transition from: :review,
328
- on: :review_failed,
329
- to: :failed
330
-
331
- transition from: :finalize, to: :__finish__
332
- end
333
- end
334
-
335
- def default_parse_draft(text)
336
- json_parser.parse(text)
337
- rescue Phronomy::ParseError
338
- {
339
- answer: text.to_s,
340
- confidence: 0.0,
341
- citations: []
342
- }
343
- end
344
-
345
- def default_parse_review(text)
346
- json_parser.parse(text)
347
- rescue Phronomy::ParseError
348
- {
349
- approved: false,
350
- score: 0.0,
351
- feedback: "Review output could not be parsed: #{text}"
352
- }
353
- end
354
-
355
- def json_parser
356
- @json_parser ||= Phronomy::OutputParser::JsonParser.new
357
- end
358
-
359
- def clamp(value)
360
- value.to_f.clamp(0.0, 1.0)
361
- end
362
-
363
- def normalize_citations(raw)
364
- Array(raw).filter_map do |citation|
365
- citation.is_a?(Hash) ? citation.transform_keys(&:to_sym) : nil
366
- end
367
- end
368
- end
369
- end
@@ -1,8 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phronomy
4
- # Raised when output token reserve cannot be determined at budget construction
5
- # time (e.g. no explicit max_output_tokens set and registry value equals the
6
- # context window).
7
- class InvalidContextBudgetConfigurationError < Error; end
8
- end
@@ -1,116 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phronomy
4
- module MultiAgent
5
- # RubyLLM::Chat subclass supporting concurrent direct Tool execution.
6
- #
7
- # AgentInvocation installs +on_tool_call_batch+ and intercepts the whole batch
8
- # before this class dispatches anything. The direct-chat fallback dispatches
9
- # the complete batch concurrently; process protection remains the responsibility
10
- # of Runtime's bounded executors and queues.
11
- # @api private
12
- class ParallelToolChat < RubyLLM::Chat
13
- def initialize(cancellation_token: nil, **opts)
14
- super(**opts)
15
- @cancellation_token = cancellation_token
16
- end
17
-
18
- attr_writer :cancellation_token
19
-
20
- # Registers an Agent-owned batch interceptor. The callback must return
21
- # quickly or raise ToolCallIntercepted; it must not execute Tool bodies.
22
- def on_tool_call_batch(&block)
23
- @on[:tool_call_batch] = block
24
- self
25
- end
26
-
27
- private
28
-
29
- def handle_tool_calls(response, &block)
30
- tool_calls = response.tool_calls.values
31
- return super if tool_calls.size <= 1
32
-
33
- if @on[:tool_call_batch]
34
- tool_calls.each { run_callbacks(:before_message, :new_message) }
35
- @on[:tool_call_batch].call(tool_calls)
36
- return
37
- end
38
-
39
- # Direct ParallelToolChat fallback. Agent execution never reaches this
40
- # branch because AgentInvocation installs the batch interceptor first.
41
- # RubyLLM >= 1.15 additive callbacks are dispatched together with their
42
- # legacy equivalents, matching RubyLLM::Chat semantics.
43
- tool_calls.each do |tool_call|
44
- run_callbacks(:before_message, :new_message)
45
- run_callbacks(:before_tool_call, :tool_call, tool_call)
46
- end
47
-
48
- cancellation_token = @cancellation_token
49
- if cancellation_token&.cancelled?
50
- raise Phronomy::CancellationError,
51
- "invocation cancelled before tool execution"
52
- end
53
-
54
- dispatched = tool_calls.map do |tool_call|
55
- tool = tools[tool_call.name.to_sym]
56
- unless tool
57
- next {
58
- tool_call: tool_call,
59
- awaitable: nil,
60
- result: {
61
- error: "Model tried to call unavailable tool `#{tool_call.name}`. " \
62
- "Available tools: #{tools.keys.to_json}."
63
- }
64
- }
65
- end
66
-
67
- awaitable = Phronomy::Agent::ToolExecutor.call_async(
68
- tool: tool,
69
- args: tool_call.arguments,
70
- cancellation_token: cancellation_token
71
- )
72
- {tool_call: tool_call, awaitable: awaitable, result: nil}
73
- end
74
-
75
- tool_results = dispatched.map do |item|
76
- result = item[:awaitable] ? item[:awaitable].wait_result : item[:result]
77
- {tool_call: item[:tool_call], result: result}
78
- end
79
-
80
- halt_result = nil
81
- tool_results.each do |item|
82
- result = item[:result]
83
- run_callbacks(:after_tool_result, :tool_result, result)
84
- tool_payload = result.is_a?(RubyLLM::Tool::Halt) ? result.content : result
85
- content = content_like?(tool_payload) ? tool_payload : tool_payload.to_s
86
- message = add_message(
87
- role: :tool,
88
- content: content,
89
- tool_call_id: item[:tool_call].id
90
- )
91
- run_callbacks(:after_message, :end_message, message)
92
- halt_result = result if result.is_a?(RubyLLM::Tool::Halt)
93
- end
94
-
95
- reset_tool_choice if forced_tool_choice?
96
- halt_result || complete(&block)
97
- end
98
-
99
- def execute_tool(tool_call)
100
- tool = tools[tool_call.name.to_sym]
101
- unless tool
102
- return {
103
- error: "Model tried to call unavailable tool `#{tool_call.name}`. " \
104
- "Available tools: #{tools.keys.to_json}."
105
- }
106
- end
107
-
108
- Phronomy::Agent::ToolExecutor.call_async(
109
- tool: tool,
110
- args: tool_call.arguments,
111
- cancellation_token: @cancellation_token
112
- ).wait_result
113
- end
114
- end
115
- end
116
- end