phronomy 0.26.0 → 0.28.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 (557) hide show
  1. checksums.yaml +4 -4
  2. data/.mutant.yml +9 -9
  3. data/CHANGELOG.md +92 -0
  4. data/CONTRIBUTING.md +8 -8
  5. data/README.md +13 -3
  6. data/VERIFY.sh +27 -27
  7. data/benchmark/bench_agent_invoke.rb +26 -47
  8. data/benchmark/bench_context_assembler.rb +17 -15
  9. data/benchmark/bench_regression.rb +1 -1
  10. data/benchmark/bench_tool_schema.rb +1 -1
  11. data/docs/architecture/agent-chat-and-state-ownership.md +153 -0
  12. data/docs/architecture/agent-configuration-and-tool-binding.md +139 -0
  13. data/docs/architecture/agent-context.md +4 -2
  14. data/docs/architecture/agent-transition-ownership.md +13 -0
  15. data/docs/architecture/backend-groups.md +152 -0
  16. data/docs/architecture/before-llm-input.md +9 -3
  17. data/docs/architecture/context-management.md +31 -1
  18. data/docs/architecture/context-preparation-steps.md +90 -0
  19. data/docs/architecture/entry-action-and-team-wording.md +72 -0
  20. data/docs/architecture/execution-abstraction-review.md +88 -0
  21. data/docs/architecture/execution-boundaries.md +28 -0
  22. data/docs/architecture/execution-metadata-and-values.md +86 -0
  23. data/docs/architecture/generator-verifier-ownership.md +111 -0
  24. data/docs/architecture/multi-agent-handoff.md +11 -4
  25. data/docs/architecture/persistence-refactoring-plan.md +50 -0
  26. data/docs/architecture/persistence.md +45 -4
  27. data/docs/architecture/r8-unit1.md +104 -0
  28. data/docs/architecture/r8-unit10.md +72 -0
  29. data/docs/architecture/r8-unit11.md +71 -0
  30. data/docs/architecture/r8-unit12.md +67 -0
  31. data/docs/architecture/r8-unit13.md +61 -0
  32. data/docs/architecture/r8-unit14.md +72 -0
  33. data/docs/architecture/r8-unit15.md +65 -0
  34. data/docs/architecture/r8-unit16.md +54 -0
  35. data/docs/architecture/r8-unit17.md +73 -0
  36. data/docs/architecture/r8-unit18.md +92 -0
  37. data/docs/architecture/r8-unit19.md +61 -0
  38. data/docs/architecture/r8-unit2.md +55 -0
  39. data/docs/architecture/r8-unit20.md +51 -0
  40. data/docs/architecture/r8-unit21.md +79 -0
  41. data/docs/architecture/r8-unit22.md +74 -0
  42. data/docs/architecture/r8-unit23.md +81 -0
  43. data/docs/architecture/r8-unit3.md +130 -0
  44. data/docs/architecture/r8-unit4.md +79 -0
  45. data/docs/architecture/r8-unit5.md +115 -0
  46. data/docs/architecture/r8-unit6.md +71 -0
  47. data/docs/architecture/r8-unit7.md +70 -0
  48. data/docs/architecture/r8-unit8.md +78 -0
  49. data/docs/architecture/r8-unit9.md +73 -0
  50. data/docs/architecture/refactoring-closure.md +343 -0
  51. data/docs/architecture/remaining-refactoring-plan.md +390 -0
  52. data/docs/architecture/rubyllm-2-token-ownership.md +82 -0
  53. data/docs/architecture/storage-execution-boundaries.md +54 -0
  54. data/docs/architecture/tool-schema-recording-gap.md +56 -0
  55. data/docs/architecture/tracing.md +2 -2
  56. data/docs/architecture/workflow-terminal-ownership-design.md +104 -0
  57. data/docs/architecture.md +222 -0
  58. data/docs/decisions/014-unified-persistence-durable-state.md +11 -0
  59. data/docs/decisions/015-tool-public-facade-and-rbs-boundary.md +3 -3
  60. data/docs/decisions/024-event-loop-single-writer-agent-runtime.md +5 -1
  61. data/docs/decisions/025-process-local-agent-ownership-and-runtime-admission.md +13 -3
  62. data/docs/decisions/026-workflow-runtime-admission-and-durable-terminal-barrier.md +5 -1
  63. data/docs/decisions/027-llm-adapter-provider-boundary.md +2 -0
  64. data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +7 -0
  65. data/docs/decisions/031-durable-multi-agent-coordination.md +7 -0
  66. data/docs/decisions/032-storage-backend-composition.md +85 -0
  67. data/docs/decisions/033-domain-persistence-ownership.md +79 -0
  68. data/docs/decisions/034-handoff-runner-coordination-ownership.md +78 -0
  69. data/docs/decisions/035-tool-executor-capability-ownership.md +68 -0
  70. data/docs/decisions/036-context-contract-ownership.md +86 -0
  71. data/docs/decisions/037-common-definition-ownership.md +62 -0
  72. data/docs/decisions/038-responsibility-based-source-layout.md +145 -0
  73. data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +82 -0
  74. data/docs/decisions/040-configuration-default-composition.md +105 -0
  75. data/docs/decisions/041-feature-owned-identity-registries.md +100 -0
  76. data/docs/decisions/042-feature-owned-execution-state.md +106 -0
  77. data/docs/decisions/043-storage-execution-constraint-notifications.md +75 -0
  78. data/docs/decisions/044-agent-default-and-one-shot-composition.md +106 -0
  79. data/docs/decisions/045-worker-input-restriction-ownership.md +104 -0
  80. data/docs/decisions/046-agent-responsibility-layout-and-shared-records.md +110 -0
  81. data/docs/decisions/047-recovered-execution-continuation-contract.md +116 -0
  82. data/docs/decisions/048-dispatch-preparation-worker-ownership.md +120 -0
  83. data/docs/decisions/049-initial-preparation-worker-ownership.md +111 -0
  84. data/docs/decisions/050-approval-resume-snapshot-and-commit-ownership.md +98 -0
  85. data/docs/decisions/051-execution-outcome-worker-ownership.md +153 -0
  86. data/docs/decisions/052-tool-invocation-restoration-ownership.md +92 -0
  87. data/docs/decisions/053-shared-state-coordination-ownership.md +71 -0
  88. data/docs/decisions/054-workflow-terminal-save-single-owner.md +76 -0
  89. data/docs/decisions/055-terminal-observer-failure-settlement.md +60 -0
  90. data/docs/decisions/056-workflow-terminal-policy-ownership.md +87 -0
  91. data/docs/decisions/057-storage-transaction-boundaries.md +73 -0
  92. data/docs/decisions/058-neutral-storage-primitives.md +83 -0
  93. data/docs/decisions/059-backend-contracts-and-async-clients.md +74 -0
  94. data/docs/decisions/060-runtime-settings-and-application-configuration.md +86 -0
  95. data/docs/decisions/061-persistence-failure-contracts.md +89 -0
  96. data/docs/decisions/062-content-store-service.md +52 -0
  97. data/docs/decisions/063-execution-contracts-and-services.md +88 -0
  98. data/docs/decisions/064-agent-execution-and-multi-agent-responsibility.md +36 -0
  99. data/docs/decisions/065-vector-and-embeddings-common-operations.md +38 -0
  100. data/docs/decisions/066-llm-and-tool-operation-contracts.md +20 -0
  101. data/docs/decisions/067-agent-progress-and-engine-connection.md +29 -0
  102. data/docs/decisions/068-agent-tool-child-progress-and-tool-operation.md +36 -0
  103. data/docs/decisions/README.md +52 -6
  104. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_DESIGN_V2.md +6 -0
  105. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md +6 -0
  106. data/docs/execution-boundaries-preview.md +45 -0
  107. data/docs/features.md +16 -5
  108. data/docs/getting-started.md +11 -8
  109. data/docs/migrations/0.15.md +5 -0
  110. data/docs/migrations/0.22.md +2 -2
  111. data/docs/migrations/durable-semantic-coordination-v2.md +8 -0
  112. data/docs/migrations/handoff-runner-multi-agent.md +37 -0
  113. data/docs/migrations/neutral-storage-spi.md +43 -0
  114. data/docs/migrations/parallel-tool-chat-removal.md +49 -0
  115. data/docs/migrations/persistence-failure-contracts.md +67 -0
  116. data/docs/migrations/r8-unit10.md +36 -0
  117. data/docs/migrations/r8-unit11.md +33 -0
  118. data/docs/migrations/r8-unit12.md +31 -0
  119. data/docs/migrations/r8-unit13.md +36 -0
  120. data/docs/migrations/r8-unit14.md +31 -0
  121. data/docs/migrations/r8-unit15.md +31 -0
  122. data/docs/migrations/r8-unit16.md +26 -0
  123. data/docs/migrations/r8-unit17.md +28 -0
  124. data/docs/migrations/r8-unit18.md +29 -0
  125. data/docs/migrations/r8-unit19.md +28 -0
  126. data/docs/migrations/r8-unit20.md +24 -0
  127. data/docs/migrations/r8-unit21.md +28 -0
  128. data/docs/migrations/r8-unit22.md +29 -0
  129. data/docs/migrations/r8-unit23.md +20 -0
  130. data/docs/migrations/r8-unit4.md +67 -0
  131. data/docs/migrations/r8-unit5.md +75 -0
  132. data/docs/migrations/r8-unit6.md +109 -0
  133. data/docs/migrations/r8-unit7.md +31 -0
  134. data/docs/migrations/r8-unit8.md +33 -0
  135. data/docs/migrations/r8-unit9.md +28 -0
  136. data/docs/migrations/shared-state-multi-agent.md +44 -0
  137. data/docs/migrations/storage-async-client.md +103 -0
  138. data/docs/migrations/storage-backend-composition.md +117 -0
  139. data/docs/migrations/storage-transaction-boundaries.md +74 -0
  140. data/docs/migrations/vector-async-clients.md +110 -0
  141. data/docs/persistence-backends.md +190 -215
  142. data/docs/runtime-and-concurrency.md +142 -32
  143. data/lib/phronomy/agent/admission.rb +78 -0
  144. data/lib/phronomy/{agent_already_exists_error.rb → agent/agent_already_exists_error.rb} +2 -0
  145. data/lib/phronomy/{agent_busy_error.rb → agent/agent_busy_error.rb} +2 -0
  146. data/lib/phronomy/{agent_purged_error.rb → agent/agent_purged_error.rb} +2 -0
  147. data/lib/phronomy/agent/api/agent.rb +17 -0
  148. data/lib/phronomy/{execution_rehydration_required_error.rb → agent/api/execution_rehydration_required_error.rb} +2 -0
  149. data/lib/phronomy/agent/async_event_api.rb +38 -33
  150. data/lib/phronomy/agent/base.rb +106 -294
  151. data/lib/phronomy/agent/before_llm_input.rb +64 -0
  152. data/lib/phronomy/{agent.rb → agent/composition/run_once.rb} +4 -13
  153. data/lib/phronomy/agent/context_candidate_resolver.rb +2 -2
  154. data/lib/phronomy/agent/context_importer.rb +2 -2
  155. data/lib/phronomy/agent/{context_assembler.rb → context_preparation.rb} +157 -288
  156. data/lib/phronomy/agent/control_request.rb +21 -0
  157. data/lib/phronomy/agent/{agent_execution.rb → execution/agent_execution.rb} +8 -4
  158. data/lib/phronomy/agent/{agent_invocation.rb → execution/agent_invocation.rb} +32 -63
  159. data/lib/phronomy/agent/execution/approval_resume_commit.rb +108 -0
  160. data/lib/phronomy/agent/execution/dispatch_preparation.rb +195 -0
  161. data/lib/phronomy/agent/{exact_execution.rb → execution/exact_execution.rb} +43 -23
  162. data/lib/phronomy/agent/{execution_cancellation.rb → execution/execution_cancellation.rb} +8 -5
  163. data/lib/phronomy/agent/execution/execution_coordinator.rb +1744 -0
  164. data/lib/phronomy/agent/execution/execution_failure.rb +30 -0
  165. data/lib/phronomy/agent/execution/execution_metadata.rb +54 -0
  166. data/lib/phronomy/agent/execution/execution_outcome_committer.rb +288 -0
  167. data/lib/phronomy/agent/execution/execution_registry.rb +463 -0
  168. data/lib/phronomy/agent/execution/execution_session_runner.rb +113 -0
  169. data/lib/phronomy/agent/execution/initial_preparation.rb +323 -0
  170. data/lib/phronomy/agent/execution/invocation_actions.rb +368 -0
  171. data/lib/phronomy/agent/execution/invocation_transitions.rb +124 -0
  172. data/lib/phronomy/agent/execution/runtime_record_encoder.rb +210 -0
  173. data/lib/phronomy/agent/execution_change.rb +161 -0
  174. data/lib/phronomy/agent/execution_environment.rb +36 -0
  175. data/lib/phronomy/agent/execution_extension_state.rb +21 -0
  176. data/lib/phronomy/agent/execution_observation.rb +35 -0
  177. data/lib/phronomy/agent/filterable.rb +182 -0
  178. data/lib/phronomy/agent/{handoff_context.rb → handoff/transfer_context.rb} +6 -6
  179. data/lib/phronomy/agent/{handoff_projection.rb → handoff/transfer_projection.rb} +14 -33
  180. data/lib/phronomy/agent/handoff_error.rb +7 -0
  181. data/lib/phronomy/agent/{journal_projection.rb → journal/journal_projection.rb} +4 -0
  182. data/lib/phronomy/agent/{journal_record.rb → journal/journal_record.rb} +3 -3
  183. data/lib/phronomy/agent/{llm_call_record.rb → journal/llm_call_record.rb} +2 -2
  184. data/lib/phronomy/agent/knowledge_item.rb +13 -0
  185. data/lib/phronomy/agent/{agent_root.rb → lifecycle/agent_root.rb} +6 -2
  186. data/lib/phronomy/agent/lifecycle/default_persistence.rb +29 -0
  187. data/lib/phronomy/{engine/runtime/agent_ownership_registry.rb → agent/lifecycle/ownership_registry.rb} +24 -15
  188. data/lib/phronomy/agent/persistence/agent_repository.rb +69 -0
  189. data/lib/phronomy/agent/persistence/cancellation_repository.rb +51 -0
  190. data/lib/phronomy/agent/persistence/codec.rb +338 -0
  191. data/lib/phronomy/agent/persistence/execution_repository.rb +124 -0
  192. data/lib/phronomy/agent/persistence/journal_repository.rb +63 -0
  193. data/lib/phronomy/agent/persistence/records.rb +33 -0
  194. data/lib/phronomy/agent/persistence/retention_repository.rb +70 -0
  195. data/lib/phronomy/agent/persistence/storage_schema.rb +29 -0
  196. data/lib/phronomy/agent/persistence/watermark.rb +29 -0
  197. data/lib/phronomy/agent/recovery/invocation_restorer.rb +124 -0
  198. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/continuation.rb +28 -51
  199. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/installation.rb +50 -55
  200. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/resolution.rb +38 -43
  201. data/lib/phronomy/agent/{recovery_coordinator.rb → recovery/recovery_coordinator.rb} +26 -23
  202. data/lib/phronomy/agent/recovery/recovery_rules.rb +119 -0
  203. data/lib/phronomy/agent/recovery/recovery_support.rb +224 -0
  204. data/lib/phronomy/agent/reserved_execution.rb +16 -0
  205. data/lib/phronomy/agent/retention.rb +17 -0
  206. data/lib/phronomy/agent/runtime_binding/engine_environment.rb +60 -0
  207. data/lib/phronomy/agent/runtime_binding/engine_session_builder.rb +34 -0
  208. data/lib/phronomy/agent/runtime_binding/phase_machine_builder.rb +64 -0
  209. data/lib/phronomy/agent/runtime_binding/tool_session_builder.rb +34 -0
  210. data/lib/phronomy/agent/{ruby_llm_materializer.rb → runtime_input.rb} +19 -39
  211. data/lib/phronomy/agent/saved_context_reader.rb +55 -0
  212. data/lib/phronomy/agent/settings.rb +47 -0
  213. data/lib/phronomy/agent/state_writer.rb +166 -0
  214. data/lib/phronomy/agent/store.rb +253 -0
  215. data/lib/phronomy/{stream_callback_error.rb → agent/stream_callback_error.rb} +2 -0
  216. data/lib/phronomy/agent/{approval_evaluation_request.rb → tool_execution/approval_evaluation_request.rb} +1 -12
  217. data/lib/phronomy/agent/{tool_approval_request.rb → tool_execution/tool_approval_request.rb} +1 -10
  218. data/lib/phronomy/agent/tool_execution/tool_binding.rb +30 -0
  219. data/lib/phronomy/agent/{tool_invocation.rb → tool_execution/tool_invocation.rb} +114 -248
  220. data/lib/phronomy/agent/tool_execution/tool_invocation_actions.rb +107 -0
  221. data/lib/phronomy/agent/tool_execution/tool_invocation_transitions.rb +70 -0
  222. data/lib/phronomy/common/configuration_error.rb +7 -0
  223. data/lib/phronomy/common/error.rb +5 -0
  224. data/lib/phronomy/{agent → common/values}/immutable.rb +9 -1
  225. data/lib/phronomy/common/values/serializable.rb +32 -0
  226. data/lib/phronomy/configuration/runtime_settings.rb +49 -0
  227. data/lib/phronomy/content_store/storage_schema.rb +12 -0
  228. data/lib/phronomy/content_store/stored_contents.rb +44 -0
  229. data/lib/phronomy/context/assembly.rb +202 -0
  230. data/lib/phronomy/{context_budget_exceeded_error.rb → context/budget_exceeded_error.rb} +5 -1
  231. data/lib/phronomy/context/candidate.rb +51 -0
  232. data/lib/phronomy/context/constraint.rb +47 -0
  233. data/lib/phronomy/{agent → context}/context_plan.rb +2 -2
  234. data/lib/phronomy/{agent → context}/context_plan_validator.rb +3 -3
  235. data/lib/phronomy/{agent → context}/context_policy.rb +1 -1
  236. data/lib/phronomy/{agent → context}/context_policy_input.rb +7 -7
  237. data/lib/phronomy/{agent → context}/context_policy_input_builder.rb +1 -1
  238. data/lib/phronomy/context/default_policy.rb +140 -0
  239. data/lib/phronomy/context/final_budget_validator.rb +33 -0
  240. data/lib/phronomy/{agent → context}/llm_input_build_context.rb +2 -2
  241. data/lib/phronomy/{agent → context}/llm_input_manifest.rb +21 -21
  242. data/lib/phronomy/{agent → context}/llm_input_patch.rb +3 -3
  243. data/lib/phronomy/context/manifest_error.rb +10 -0
  244. data/lib/phronomy/context/prompt_template.rb +98 -0
  245. data/lib/phronomy/{vector_store → documents}/loader/base.rb +1 -1
  246. data/lib/phronomy/{vector_store → documents}/loader/csv_loader.rb +2 -2
  247. data/lib/phronomy/{vector_store → documents}/loader/markdown_loader.rb +3 -3
  248. data/lib/phronomy/{vector_store → documents}/loader/plain_text_loader.rb +2 -2
  249. data/lib/phronomy/{vector_store → documents}/splitter/base.rb +1 -1
  250. data/lib/phronomy/{vector_store → documents}/splitter/fixed_size_splitter.rb +2 -2
  251. data/lib/phronomy/{vector_store → documents}/splitter/recursive_splitter.rb +2 -2
  252. data/lib/phronomy/embeddings/async/async_client.rb +44 -0
  253. data/lib/phronomy/embeddings/backends/ruby_llm_embeddings.rb +48 -0
  254. data/lib/phronomy/embeddings/base.rb +43 -0
  255. data/lib/phronomy/embeddings/invalid_result_error.rb +11 -0
  256. data/lib/phronomy/embeddings/transport_error.rb +11 -0
  257. data/lib/phronomy/engine/concurrency/offload_pool.rb +39 -20
  258. data/lib/phronomy/engine/concurrency/pool_registry.rb +47 -12
  259. data/lib/phronomy/{diagnostics.rb → engine/diagnostics.rb} +1 -1
  260. data/lib/phronomy/engine/event_loop.rb +164 -630
  261. data/lib/phronomy/engine/execution_binding.rb +23 -0
  262. data/lib/phronomy/engine/execution_receiver_binding.rb +43 -0
  263. data/lib/phronomy/engine/fsm_protocol.rb +9 -0
  264. data/lib/phronomy/engine/fsm_session.rb +37 -30
  265. data/lib/phronomy/engine/metrics.rb +28 -0
  266. data/lib/phronomy/engine/runtime/timer_queue.rb +1 -1
  267. data/lib/phronomy/engine/runtime.rb +104 -95
  268. data/lib/phronomy/engine/terminal_decision.rb +7 -0
  269. data/lib/phronomy/execution/async_operation.rb +41 -0
  270. data/lib/phronomy/execution/backpressure_error.rb +7 -0
  271. data/lib/phronomy/{blocking.rb → execution/blocking.rb} +2 -2
  272. data/lib/phronomy/execution/cancellation_error.rb +7 -0
  273. data/lib/phronomy/{engine → execution}/concurrency/cancellation_token.rb +5 -1
  274. data/lib/phronomy/{engine → execution}/concurrency/deadline.rb +0 -17
  275. data/lib/phronomy/{engine → execution}/concurrency/physical_completion_task.rb +1 -1
  276. data/lib/phronomy/{engine → execution}/concurrency/result_collector.rb +1 -1
  277. data/lib/phronomy/{engine → execution}/concurrency/result_composition.rb +33 -12
  278. data/lib/phronomy/{engine/concurrency/subscriptions.rb → execution/concurrency/result_subscriptions.rb} +14 -18
  279. data/lib/phronomy/execution/concurrency/subscriptions.rb +25 -0
  280. data/lib/phronomy/execution/concurrency/worker_input_restricted.rb +14 -0
  281. data/lib/phronomy/execution/event_loop_reentrancy_error.rb +8 -0
  282. data/lib/phronomy/{execution.rb → execution/execution.rb} +57 -30
  283. data/lib/phronomy/{execution_cancellation_error.rb → execution/execution_cancellation_error.rb} +2 -0
  284. data/lib/phronomy/execution/execution_receiver.rb +78 -0
  285. data/lib/phronomy/execution/execution_scope_state.rb +35 -0
  286. data/lib/phronomy/{execution_timeout_error.rb → execution/execution_timeout_error.rb} +2 -0
  287. data/lib/phronomy/{invalid_async_entry_action_error.rb → execution/invalid_async_entry_action_error.rb} +2 -0
  288. data/lib/phronomy/{invalid_async_transition_action_error.rb → execution/invalid_async_transition_action_error.rb} +2 -0
  289. data/lib/phronomy/{invalid_async_workflow_action_error.rb → execution/invalid_async_workflow_action_error.rb} +2 -0
  290. data/lib/phronomy/{invocation_context.rb → execution/invocation_context.rb} +0 -15
  291. data/lib/phronomy/execution/invocation_controls.rb +73 -0
  292. data/lib/phronomy/execution/pool_shutdown_error.rb +7 -0
  293. data/lib/phronomy/execution/recursion_limit_error.rb +7 -0
  294. data/lib/phronomy/execution/runnable.rb +24 -0
  295. data/lib/phronomy/execution/runtime_shutdown_error.rb +7 -0
  296. data/lib/phronomy/execution/runtime_shutdown_reentrancy_error.rb +7 -0
  297. data/lib/phronomy/execution/scheduler_reentrancy_error.rb +9 -0
  298. data/lib/phronomy/{engine → execution}/task_result.rb +7 -3
  299. data/lib/phronomy/execution/timeout_error.rb +7 -0
  300. data/lib/phronomy/execution/wait_policy.rb +24 -0
  301. data/lib/phronomy/filter/filter_block_error.rb +14 -0
  302. data/lib/phronomy/generation/generator_verifier/agent_operation.rb +65 -0
  303. data/lib/phronomy/generation/generator_verifier/agent_result_receiver.rb +89 -0
  304. data/lib/phronomy/generation/generator_verifier/default_parser.rb +27 -0
  305. data/lib/phronomy/generation/generator_verifier/pipeline_state.rb +57 -0
  306. data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +112 -0
  307. data/lib/phronomy/generation/generator_verifier.rb +118 -0
  308. data/lib/phronomy/generation/low_confidence_error.rb +14 -0
  309. data/lib/phronomy/llm_adapter/async/async_client.rb +47 -0
  310. data/lib/phronomy/llm_adapter/authentication_error.rb +9 -0
  311. data/lib/phronomy/llm_adapter/backends/ruby_llm.rb +183 -0
  312. data/lib/phronomy/llm_adapter/base.rb +49 -68
  313. data/lib/phronomy/llm_adapter/context_length_error.rb +9 -0
  314. data/lib/phronomy/llm_adapter/invalid_result_error.rb +10 -0
  315. data/lib/phronomy/llm_adapter/message.rb +62 -0
  316. data/lib/phronomy/llm_adapter/rate_limit_error.rb +9 -0
  317. data/lib/phronomy/llm_adapter/request.rb +79 -0
  318. data/lib/phronomy/llm_adapter/response.rb +36 -0
  319. data/lib/phronomy/llm_adapter/stream_chunk.rb +18 -0
  320. data/lib/phronomy/llm_adapter/token_usage.rb +64 -0
  321. data/lib/phronomy/llm_adapter/transport_error.rb +9 -0
  322. data/lib/phronomy/llm_adapter.rb +3 -16
  323. data/lib/phronomy/llm_context_window/token_budget.rb +6 -7
  324. data/lib/phronomy/multi_agent/admission_registry.rb +15 -2
  325. data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +108 -84
  326. data/lib/phronomy/multi_agent/execution_environment.rb +29 -0
  327. data/lib/phronomy/{agent → multi_agent}/handoff.rb +2 -2
  328. data/lib/phronomy/{agent → multi_agent}/handoff_capability_factory.rb +21 -2
  329. data/lib/phronomy/multi_agent/handoff_participant.rb +79 -0
  330. data/lib/phronomy/{agent → multi_agent}/handoff_policy.rb +1 -1
  331. data/lib/phronomy/multi_agent/handoff_runner.rb +156 -0
  332. data/lib/phronomy/{agent → multi_agent}/handoff_state.rb +2 -2
  333. data/lib/phronomy/multi_agent/orchestrator.rb +61 -75
  334. data/lib/phronomy/multi_agent/persistence/codec.rb +75 -0
  335. data/lib/phronomy/multi_agent/persistence/handoff_state_repository.rb +77 -0
  336. data/lib/phronomy/multi_agent/persistence/records.rb +27 -0
  337. data/lib/phronomy/multi_agent/persistence/storage_schema.rb +21 -0
  338. data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +124 -0
  339. data/lib/phronomy/multi_agent/persistence/team_repository.rb +69 -0
  340. data/lib/phronomy/multi_agent/reserved_child_admission.rb +88 -0
  341. data/lib/phronomy/multi_agent/runtime_binding/engine_environment.rb +35 -0
  342. data/lib/phronomy/{agent → multi_agent}/shared_state.rb +56 -39
  343. data/lib/phronomy/multi_agent/store.rb +156 -0
  344. data/lib/phronomy/multi_agent/team_coordinator.rb +87 -75
  345. data/lib/phronomy/multi_agent/team_execution.rb +1 -1
  346. data/lib/phronomy/{engine/runtime → multi_agent}/team_ownership_registry.rb +5 -5
  347. data/lib/phronomy/multi_agent/team_root.rb +1 -1
  348. data/lib/phronomy/output_parser/parse_error.rb +7 -0
  349. data/lib/phronomy/persistence/content_repository.rb +26 -0
  350. data/lib/phronomy/persistence/errors.rb +16 -0
  351. data/lib/phronomy/persistence/migration/initial_format_migration.rb +21 -21
  352. data/lib/phronomy/persistence/persistence.rb +23 -0
  353. data/lib/phronomy/persistence/save_outcome.rb +24 -0
  354. data/lib/phronomy/persistence/snapshot_comparison.rb +81 -0
  355. data/lib/phronomy/persistence/storage_boundary.rb +32 -0
  356. data/lib/phronomy/persistence/transaction.rb +86 -0
  357. data/lib/phronomy/persistence_composition/storage_schema.rb +29 -0
  358. data/lib/phronomy/persistence_composition/stores.rb +34 -0
  359. data/lib/phronomy/persistence_composition/unit4_migration.rb +276 -0
  360. data/lib/phronomy/runtime_composition/agent_defaults.rb +11 -0
  361. data/lib/phronomy/runtime_composition/configuration.rb +146 -0
  362. data/lib/phronomy/runtime_composition/configuration_defaults.rb +9 -0
  363. data/lib/phronomy/runtime_composition/evaluation_defaults.rb +7 -0
  364. data/lib/phronomy/runtime_composition/execution_defaults.rb +5 -0
  365. data/lib/phronomy/runtime_composition/generation_defaults.rb +6 -0
  366. data/lib/phronomy/runtime_composition/global_configuration.rb +38 -0
  367. data/lib/phronomy/runtime_composition/global_runtime.rb +19 -0
  368. data/lib/phronomy/runtime_composition/multi_agent_defaults.rb +9 -0
  369. data/lib/phronomy/runtime_composition/workflow_defaults.rb +4 -0
  370. data/lib/phronomy/storage/async/async_client.rb +60 -0
  371. data/lib/phronomy/storage/backend.rb +107 -0
  372. data/lib/phronomy/storage/backends/in_memory.rb +157 -0
  373. data/lib/phronomy/storage/blob_conflict_error.rb +10 -0
  374. data/lib/phronomy/storage/blobs.rb +31 -0
  375. data/lib/phronomy/storage/condition.rb +25 -0
  376. data/lib/phronomy/storage/condition_failed_error.rb +16 -0
  377. data/lib/phronomy/storage/conflict_error.rb +9 -0
  378. data/lib/phronomy/{persistence → storage}/durable_record.rb +12 -12
  379. data/lib/phronomy/storage/entry.rb +33 -0
  380. data/lib/phronomy/storage/guard_ref.rb +13 -0
  381. data/lib/phronomy/storage/not_found_error.rb +9 -0
  382. data/lib/phronomy/storage/record_codec.rb +177 -0
  383. data/lib/phronomy/storage/records.rb +61 -0
  384. data/lib/phronomy/storage/resource.rb +126 -0
  385. data/lib/phronomy/storage/scope.rb +25 -0
  386. data/lib/phronomy/storage/serialization_error.rb +9 -0
  387. data/lib/phronomy/storage/streams.rb +44 -0
  388. data/lib/phronomy/storage/transaction_error.rb +10 -0
  389. data/lib/phronomy/storage/unique_constraint_error.rb +17 -0
  390. data/lib/phronomy/storage/unsupported_backend_error.rb +9 -0
  391. data/lib/phronomy/storage/validation.rb +53 -0
  392. data/lib/phronomy/storage/view.rb +131 -0
  393. data/lib/phronomy/testing/eval/metrics.rb +1 -1
  394. data/lib/phronomy/testing/eval/scorer/llm_judge.rb +19 -3
  395. data/lib/phronomy/testing/persistence_contract/a_content_store.rb +1 -1
  396. data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +2 -2
  397. data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +54 -48
  398. data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +1 -1
  399. data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +1 -1
  400. data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +5 -5
  401. data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +52 -46
  402. data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +263 -0
  403. data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +124 -0
  404. data/lib/phronomy/testing/persistence_contract.rb +4 -0
  405. data/lib/phronomy/tool/authorization.rb +77 -0
  406. data/lib/phronomy/tool/base.rb +549 -9
  407. data/lib/phronomy/tool/call_request.rb +40 -0
  408. data/lib/phronomy/{agent/tool_definition_set.rb → tool/definition_set.rb} +16 -12
  409. data/lib/phronomy/tool/operation.rb +88 -0
  410. data/lib/phronomy/tool/schema.rb +214 -0
  411. data/lib/phronomy/tool/settings.rb +31 -0
  412. data/lib/phronomy/tool/tool_error.rb +7 -0
  413. data/lib/phronomy/{agent → tool}/tool_executor.rb +10 -7
  414. data/lib/phronomy/tools/agent.rb +22 -90
  415. data/lib/phronomy/tools/mcp.rb +21 -14
  416. data/lib/phronomy/tools/vector_search.rb +4 -4
  417. data/lib/phronomy/tracing/automatic.rb +11 -6
  418. data/lib/phronomy/tracing/base.rb +1 -1
  419. data/lib/phronomy/tracing/observation.rb +37 -0
  420. data/lib/phronomy/tracing/settings.rb +36 -0
  421. data/lib/phronomy/vector_store/{async_backend.rb → async/async_client.rb} +35 -29
  422. data/lib/phronomy/vector_store/{in_memory.rb → backends/in_memory.rb} +12 -10
  423. data/lib/phronomy/vector_store/{pgvector.rb → backends/pgvector.rb} +44 -16
  424. data/lib/phronomy/vector_store/{redis_search.rb → backends/redis_search.rb} +74 -37
  425. data/lib/phronomy/vector_store/base.rb +118 -49
  426. data/lib/phronomy/vector_store/invalid_result_error.rb +12 -0
  427. data/lib/phronomy/vector_store.rb +2 -17
  428. data/lib/phronomy/version.rb +1 -1
  429. data/lib/phronomy/workflow/completion.rb +11 -0
  430. data/lib/phronomy/{workflow.rb → workflow/execution/workflow.rb} +10 -8
  431. data/lib/phronomy/workflow/execution/workflow_action_rules.rb +67 -0
  432. data/lib/phronomy/{workflow_context.rb → workflow/execution/workflow_context.rb} +5 -1
  433. data/lib/phronomy/workflow/execution/workflow_context_ownership_error.rb +7 -0
  434. data/lib/phronomy/workflow/execution/workflow_execution_environment.rb +27 -0
  435. data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +186 -0
  436. data/lib/phronomy/{workflow_runner.rb → workflow/execution/workflow_runner.rb} +145 -115
  437. data/lib/phronomy/workflow/execution/workflow_settings.rb +28 -0
  438. data/lib/phronomy/workflow/persistence/codec.rb +153 -0
  439. data/lib/phronomy/workflow/persistence/state_repository.rb +63 -0
  440. data/lib/phronomy/workflow/persistence/storage_schema.rb +13 -0
  441. data/lib/phronomy/workflow/runtime_binding/workflow_engine_environment.rb +38 -0
  442. data/lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb +156 -0
  443. data/lib/phronomy/workflow/runtime_binding/workflow_terminal_policy.rb +40 -0
  444. data/lib/phronomy.rb +97 -98
  445. data/scripts/api_snapshot.rb +36 -16
  446. data/scripts/storage_spi_snapshot.rb +36 -0
  447. data/sig/_private/phronomy/agent/settings.rbs +19 -0
  448. data/sig/_private/phronomy/configuration/runtime_settings.rbs +20 -0
  449. data/sig/_private/phronomy/llm_adapter/async/async_client.rbs +10 -0
  450. data/sig/_private/phronomy/multi_agent/store.rbs +8 -0
  451. data/sig/_private/phronomy/persistence/storage_boundary.rbs +7 -0
  452. data/sig/_private/phronomy/tool/settings.rbs +13 -0
  453. data/sig/_private/phronomy/tracing/settings.rbs +15 -0
  454. data/sig/_private/phronomy/workflow/execution/workflow_settings.rbs +13 -0
  455. data/sig/phronomy/agent/admission.rbs +19 -0
  456. data/sig/phronomy/agent/execution_contract.rbs +132 -0
  457. data/sig/phronomy/agent/execution_environment.rbs +20 -0
  458. data/sig/phronomy/agent/store.rbs +73 -0
  459. data/sig/phronomy/agent.rbs +14 -139
  460. data/sig/phronomy/common.rbs +7 -0
  461. data/sig/phronomy/content_store/contracts.rbs +13 -0
  462. data/sig/phronomy/context.rbs +152 -0
  463. data/sig/phronomy/embeddings/async/async_client.rbs +9 -0
  464. data/sig/phronomy/embeddings/base.rbs +17 -0
  465. data/sig/phronomy/engine/concurrency/timer_bindings.rbs +22 -0
  466. data/sig/phronomy/engine/control_bindings.rbs +11 -0
  467. data/sig/phronomy/engine/control_protocols.rbs +23 -0
  468. data/sig/phronomy/engine/execution_binding.rbs +6 -0
  469. data/sig/phronomy/engine/execution_receiver.rbs +15 -0
  470. data/sig/phronomy/engine/observability.rbs +13 -0
  471. data/sig/phronomy/execution/async_operation.rbs +7 -0
  472. data/sig/phronomy/execution/blocking.rbs +5 -0
  473. data/sig/phronomy/execution/concurrency/cancellation_token.rbs +13 -0
  474. data/sig/phronomy/execution/concurrency/deadline.rbs +10 -0
  475. data/sig/phronomy/execution/concurrency/result_composition.rbs +7 -0
  476. data/sig/phronomy/execution/control_protocols.rbs +18 -0
  477. data/sig/phronomy/execution/errors.rbs +7 -0
  478. data/sig/phronomy/execution/execution.rbs +41 -0
  479. data/sig/phronomy/execution/execution_receiver.rbs +40 -0
  480. data/sig/phronomy/execution/execution_scope_state.rbs +9 -0
  481. data/sig/phronomy/execution/invocation_context.rbs +18 -0
  482. data/sig/phronomy/execution/invocation_controls.rbs +13 -0
  483. data/sig/phronomy/execution/task_result.rbs +29 -0
  484. data/sig/phronomy/extensions.rbs +0 -21
  485. data/sig/phronomy/handoff.rbs +12 -4
  486. data/sig/phronomy/llm_adapter/backends/ruby_llm.rbs +6 -0
  487. data/sig/phronomy/llm_adapter/base.rbs +14 -0
  488. data/sig/phronomy/llm_adapter/usage.rbs +14 -0
  489. data/sig/phronomy/llm_adapter/values.rbs +43 -0
  490. data/sig/phronomy/llm_context_window.rbs +10 -0
  491. data/sig/phronomy/multi_agent/reserved_child_admission.rbs +8 -0
  492. data/sig/phronomy/multi_agent/store.rbs +54 -0
  493. data/sig/phronomy/multi_agent.rbs +24 -3
  494. data/sig/phronomy/persistence/errors.rbs +18 -0
  495. data/sig/phronomy/persistence/migration.rbs +13 -0
  496. data/sig/phronomy/persistence/transaction.rbs +19 -0
  497. data/sig/phronomy/persistence.rbs +3 -185
  498. data/sig/phronomy/persistence_composition.rbs +19 -0
  499. data/sig/phronomy/runtime_composition/configuration.rbs +36 -0
  500. data/sig/phronomy/storage/async/async_client.rbs +8 -0
  501. data/sig/phronomy/storage/backends/in_memory.rbs +9 -0
  502. data/sig/phronomy/storage/contracts.rbs +160 -0
  503. data/sig/phronomy/tool.rbs +58 -5
  504. data/sig/phronomy/tracing/observation.rbs +7 -0
  505. data/sig/phronomy/vector_store/async/async_client.rbs +13 -0
  506. data/sig/phronomy/vector_store/base.rbs +23 -0
  507. data/sig/phronomy/workflow/execution/workflow_execution_environment.rbs +14 -0
  508. data/sig/phronomy/workflow/runtime_binding/workflow_terminal_policy.rbs +7 -0
  509. data/sig/phronomy/workflow/store.rbs +8 -0
  510. data/sig/phronomy/workflow.rbs +1 -1
  511. data/sig/phronomy.rbs +0 -49
  512. metadata +462 -130
  513. data/lib/phronomy/agent/agent_invocation_session_builder.rb +0 -584
  514. data/lib/phronomy/agent/concerns/before_llm_input.rb +0 -66
  515. data/lib/phronomy/agent/concerns/error_translation.rb +0 -45
  516. data/lib/phronomy/agent/concerns/filterable.rb +0 -184
  517. data/lib/phronomy/agent/context/capability/base.rb +0 -507
  518. data/lib/phronomy/agent/context/instruction/prompt_template.rb +0 -102
  519. data/lib/phronomy/agent/context_parts/validators/final_budget_validator.rb +0 -37
  520. data/lib/phronomy/agent/context_policies/default.rb +0 -142
  521. data/lib/phronomy/agent/execution_coordinator.rb +0 -3151
  522. data/lib/phronomy/agent/handoff_execution_coordinator.rb +0 -143
  523. data/lib/phronomy/agent/handoff_request.rb +0 -45
  524. data/lib/phronomy/agent/handoff_runner.rb +0 -178
  525. data/lib/phronomy/agent/phase_machine_builder.rb +0 -152
  526. data/lib/phronomy/agent/provider_call_outcome.rb +0 -106
  527. data/lib/phronomy/agent/recovery_support.rb +0 -504
  528. data/lib/phronomy/agent/selection/candidate.rb +0 -53
  529. data/lib/phronomy/agent/selection/constraint.rb +0 -49
  530. data/lib/phronomy/agent/token_budget_resolver.rb +0 -70
  531. data/lib/phronomy/agent/tool_call_intercepted.rb +0 -29
  532. data/lib/phronomy/agent/tool_invocation_session_builder.rb +0 -276
  533. data/lib/phronomy/configuration.rb +0 -59
  534. data/lib/phronomy/engine/concurrency/cancellation_scope.rb +0 -124
  535. data/lib/phronomy/engine/concurrency/operation_binding.rb +0 -43
  536. data/lib/phronomy/generator_verifier.rb +0 -369
  537. data/lib/phronomy/invalid_context_budget_configuration_error.rb +0 -8
  538. data/lib/phronomy/llm_adapter/ruby_llm.rb +0 -47
  539. data/lib/phronomy/metrics.rb +0 -26
  540. data/lib/phronomy/multi_agent/parallel_tool_chat.rb +0 -116
  541. data/lib/phronomy/persistence/durable_codec.rb +0 -706
  542. data/lib/phronomy/persistence/in_memory.rb +0 -690
  543. data/lib/phronomy/persistence/repository_facades.rb +0 -535
  544. data/lib/phronomy/persistence.rb +0 -276
  545. data/lib/phronomy/recovery.rb +0 -186
  546. data/lib/phronomy/ruby_llm_patches.rb +0 -24
  547. data/lib/phronomy/runnable.rb +0 -54
  548. data/lib/phronomy/token_usage.rb +0 -85
  549. data/lib/phronomy/vector_store/embeddings/base.rb +0 -44
  550. data/lib/phronomy/vector_store/embeddings/ruby_llm_embeddings.rb +0 -47
  551. data/lib/phronomy/workflow/phase_machine_builder.rb +0 -205
  552. data/lib/phronomy/workflow_recovery.rb +0 -123
  553. data/sig/phronomy/llm_adapter.rbs +0 -11
  554. data/sig/phronomy/runtime.rbs +0 -76
  555. /data/lib/phronomy/agent/{llm_operation_result.rb → execution/llm_operation_result.rb} +0 -0
  556. /data/lib/phronomy/{canonical_json.rb → common/canonical_json.rb} +0 -0
  557. /data/lib/phronomy/{event.rb → engine/event.rb} +0 -0
@@ -0,0 +1,224 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Agent
5
+ module RecoverySupport
6
+ module_function
7
+
8
+ def canonical_copy(value)
9
+ Phronomy::Values::Serializable.convert(value,
10
+ unsupported_message: "Recovery value is not canonically serializable")
11
+ end
12
+
13
+ def tool_calls_from_outcome(outcome)
14
+ Array(outcome.tool_calls)
15
+ end
16
+
17
+ def normalize_provider_outcome(value)
18
+ return value if value.is_a?(Phronomy::LLMAdapter::Response)
19
+
20
+ if value.is_a?(Hash)
21
+ return Phronomy::LLMAdapter::Response.from_h(value)
22
+ end
23
+
24
+ raise ArgumentError,
25
+ "LLM Recovery :succeeded requires an LLMAdapter::Response or its canonical Hash"
26
+ end
27
+
28
+ def resolution_failure(error)
29
+ {
30
+ "class" => error.class.name.to_s,
31
+ "message" => error.message.to_s
32
+ }.freeze
33
+ end
34
+
35
+ def error_from_failure(failure)
36
+ hash = failure.to_h { |key, value| [key.to_s, value] }
37
+ Phronomy::Error.new(
38
+ "#{hash.fetch("class", "Error")}: #{hash.fetch("message", "Recovery-resolved failure")}"
39
+ )
40
+ end
41
+
42
+ def event_payload(execution, descriptor)
43
+ {
44
+ execution_id: execution.execution_id,
45
+ execution_revision: execution.execution_revision,
46
+ reason: descriptor.fetch(:reason),
47
+ subject: Phronomy::Agent::RecoveryRules.normalize_subject(
48
+ descriptor.fetch(:subject)
49
+ ),
50
+ allowed_outcomes: Array(
51
+ descriptor.fetch(:allowed_outcomes)
52
+ ).map(&:to_sym).freeze,
53
+ facts: Phronomy::Values::Immutable.copy(
54
+ descriptor.fetch(:facts, {})
55
+ )
56
+ }.freeze
57
+ end
58
+
59
+ def pending_llm_descriptor(execution)
60
+ llm_call_id = execution.metadata[ExecutionMetadata::PENDING_LLM_ID_KEY]
61
+ return unless llm_call_id
62
+
63
+ {
64
+ reason: :outcome_unknown,
65
+ subject: {
66
+ type: :llm_call,
67
+ llm_call_id: llm_call_id
68
+ },
69
+ allowed_outcomes: Phronomy::Agent::RecoveryRules::OUTCOMES,
70
+ facts: {
71
+ manifest_ref: execution.metadata["manifest_ref"],
72
+ call_sequence: execution.llm_calls.length + 1
73
+ }.compact.freeze
74
+ }.freeze
75
+ end
76
+
77
+ def recovery_hash(execution)
78
+ value = execution.metadata[ExecutionMetadata::RECOVERY_METADATA_KEY]
79
+ value.is_a?(Hash) ? value : nil
80
+ end
81
+
82
+ def current_tool_descriptor(execution)
83
+ recovery = recovery_hash(execution)
84
+ return unless recovery
85
+
86
+ subjects = Array(recovery["subjects"] || recovery[:subjects])
87
+ current = subjects.find do |entry|
88
+ hash = entry.to_h { |key, value| [key.to_s, value] }
89
+ hash.fetch("state", "unresolved") == "unresolved"
90
+ end
91
+ return unless current
92
+
93
+ hash = current.to_h { |key, value| [key.to_s, value] }
94
+ {
95
+ reason: (recovery["reason"] || recovery[:reason] || "outcome_unknown").to_sym,
96
+ subject: {
97
+ type: :tool_invocation,
98
+ tool_invocation_id: hash.fetch("tool_invocation_id")
99
+ },
100
+ allowed_outcomes: Array(
101
+ recovery["allowed_outcomes"] ||
102
+ recovery[:allowed_outcomes] ||
103
+ Phronomy::Agent::RecoveryRules::OUTCOMES
104
+ ).map(&:to_sym),
105
+ facts: {
106
+ tool_call_id: hash["tool_call_id"],
107
+ tool_name: hash["tool_name"],
108
+ arguments: hash["arguments"],
109
+ llm_call_id: hash["llm_call_id"]
110
+ }.compact
111
+ }.freeze
112
+ end
113
+
114
+ def pending_tool_subjects(execution)
115
+ Array(execution.metadata[ExecutionMetadata::TOOL_BATCH_METADATA_KEY]).filter_map do |entry|
116
+ hash = entry.to_h { |key, value| [key.to_s, value] }
117
+ next unless %w[authorized awaiting_approval].include?(
118
+ hash.fetch("status")
119
+ )
120
+
121
+ {
122
+ "tool_invocation_id" => hash.fetch("tool_invocation_id"),
123
+ "llm_call_id" => hash["llm_call_id"],
124
+ "tool_call_id" => hash.fetch("tool_call_id"),
125
+ "tool_name" => hash.fetch("tool_name"),
126
+ "arguments" => canonical_copy(
127
+ hash["arguments"] || hash["raw_arguments"] || {}
128
+ ),
129
+ "state" => "unresolved"
130
+ }.compact.freeze
131
+ end.freeze
132
+ end
133
+
134
+ def pending_tool_descriptor(execution)
135
+ subjects = pending_tool_subjects(execution)
136
+ return if subjects.empty?
137
+
138
+ first = subjects.first
139
+ {
140
+ reason: :outcome_unknown,
141
+ subject: {
142
+ type: :tool_invocation,
143
+ tool_invocation_id: first.fetch("tool_invocation_id")
144
+ },
145
+ allowed_outcomes: Phronomy::Agent::RecoveryRules::OUTCOMES,
146
+ facts: {
147
+ tool_call_id: first["tool_call_id"],
148
+ tool_name: first["tool_name"],
149
+ arguments: first["arguments"],
150
+ llm_call_id: first["llm_call_id"]
151
+ }.compact
152
+ }.freeze
153
+ end
154
+
155
+ def recovery_descriptor(execution)
156
+ case execution.phase.to_sym
157
+ when :calling_llm
158
+ pending_llm_descriptor(execution)
159
+ when :resuming
160
+ approved = execution.approval_request &&
161
+ (
162
+ execution.approval_request["approved"] ||
163
+ execution.approval_request[:approved]
164
+ )
165
+ approved ? pending_tool_descriptor(execution) : nil
166
+ when :dispatching_tools
167
+ pending_tool_descriptor(execution)
168
+ when :recovery_tools
169
+ current_tool_descriptor(execution)
170
+ end
171
+ end
172
+
173
+ def build_tool_subjects(execution, llm_call_id, outcome)
174
+ tool_calls_from_outcome(outcome).map do |call|
175
+ call_hash = canonical_copy(call)
176
+ tool_call_id = call_hash.fetch("id").to_s
177
+ tool_name = call_hash.fetch("name").to_s
178
+ {
179
+ "tool_invocation_id" => ToolInvocation.semantic_id(
180
+ execution_id: execution.execution_id,
181
+ llm_call_id: llm_call_id,
182
+ tool_call_id: tool_call_id,
183
+ tool_name: tool_name
184
+ ),
185
+ "llm_call_id" => llm_call_id.to_s,
186
+ "tool_call_id" => tool_call_id,
187
+ "tool_name" => tool_name,
188
+ "arguments" => canonical_copy(call_hash.fetch("arguments", {})),
189
+ "state" => "unresolved"
190
+ }.freeze
191
+ end.freeze
192
+ end
193
+
194
+ def build_recovery_hash(subjects, reason: :outcome_unknown, allowed_outcomes: Phronomy::Agent::RecoveryRules::OUTCOMES)
195
+ {
196
+ "version" => ExecutionMetadata::CONTRACT_VERSION,
197
+ "reason" => reason.to_s,
198
+ "allowed_outcomes" => Array(allowed_outcomes).map(&:to_s),
199
+ "subjects" => Array(subjects).map { |entry| canonical_copy(entry) }
200
+ }.freeze
201
+ end
202
+
203
+ def update_recovery_subject(recovery, tool_invocation_id:, state:, outcome:, result_ref: nil)
204
+ copy = canonical_copy(recovery)
205
+ copy["subjects"] = Array(copy.fetch("subjects")).map do |entry|
206
+ next entry unless entry.fetch("tool_invocation_id").to_s == tool_invocation_id.to_s
207
+
208
+ entry.merge(
209
+ "state" => state.to_s,
210
+ "outcome" => outcome.to_s,
211
+ "result_ref" => result_ref
212
+ ).compact
213
+ end
214
+ copy.freeze
215
+ end
216
+
217
+ def unresolved_subjects(recovery)
218
+ Array(recovery.fetch("subjects")).select do |entry|
219
+ entry.fetch("state", "unresolved") == "unresolved"
220
+ end
221
+ end
222
+ end
223
+ end
224
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Agent
5
+ # Exact identity reserved by a coordinating domain. Correlation is immutable;
6
+ # mutable routing fences belong to the coordinator, never to this identity.
7
+ # @api public
8
+ ReservedExecution = Data.define(:agent_id, :execution_id, :correlation) do
9
+ def initialize(agent_id:, execution_id:, correlation: nil)
10
+ raise ArgumentError, "reserved identities must not be empty" if agent_id.to_s.empty? || execution_id.to_s.empty?
11
+ super(agent_id: agent_id.to_s.freeze, execution_id: execution_id.to_s.freeze,
12
+ correlation: Phronomy::Values::Immutable.copy(correlation))
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Agent
5
+ # A durable reference prevents whole-Agent purge. The referencing domain
6
+ # decides when its opaque owner key can be released.
7
+ # @api public
8
+ Retention = Data.define(:agent_id, :execution_id, :owner_key) do
9
+ def initialize(agent_id:, owner_key:, execution_id: nil)
10
+ raise ArgumentError, "retention identities must not be empty" if agent_id.to_s.empty? || owner_key.to_s.empty?
11
+ super(agent_id: agent_id.to_s.freeze, execution_id: execution_id&.to_s&.freeze, owner_key: owner_key.to_s.freeze)
12
+ end
13
+
14
+ def to_h = {"agent_id" => agent_id, "execution_id" => execution_id, "owner_key" => owner_key}.freeze
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Agent
5
+ # Adapts the concrete Engine lifecycle and sessions to Agent's connection.
6
+ # Construction alone starts no Runtime resources and creates no registry.
7
+ # @api private
8
+ class EngineEnvironment < ExecutionEnvironment
9
+ def initialize(runtime: Phronomy::Runtime.instance)
10
+ @runtime = runtime
11
+ end
12
+
13
+ def registry = ExecutionRegistry.for(@runtime.event_loop)
14
+ def existing_registry = ExecutionRegistry.existing_for(@runtime)
15
+
16
+ def ownership
17
+ existing_ownership || @runtime.__register_shutdown_participant(
18
+ key: OwnershipRegistry, participant: OwnershipRegistry.new(environment: self)
19
+ )
20
+ end
21
+
22
+ def existing_ownership = @runtime.__shutdown_participant(key: OwnershipRegistry)
23
+ def executing? = @runtime.event_loop.current?
24
+ def session_phase(id) = @runtime.event_loop.fsm_session_state(id)
25
+
26
+ def submit(**options, &operation)
27
+ Phronomy::Execution.submit(runtime: @runtime, **options, &operation)
28
+ end
29
+
30
+ # Pool sizing is an execution resource choice, separate from Agent's
31
+ # authorization decision and timeout selection. Read at submission time.
32
+ def submit_authorization(timeout:, cancellation_token:, &operation)
33
+ submit(pool_name: :authorization,
34
+ size: Phronomy::RuntimeSettings.current.authorization_pool_size,
35
+ queue_size: Phronomy::RuntimeSettings.current.authorization_queue_size,
36
+ timeout: timeout, cancellation_token: cancellation_token,
37
+ on_full: :raise, &operation)
38
+ end
39
+
40
+ def build_llm_client(adapter:)
41
+ Phronomy::LLMAdapter::AsyncClient.new(adapter: adapter, submitter: self)
42
+ end
43
+
44
+ def build_agent_session(invocation:, resume_event: nil, resume_phase: nil)
45
+ EngineSessionBuilder.build(invocation: invocation, environment: self,
46
+ event_loop: @runtime.event_loop, resume_event: resume_event, resume_phase: resume_phase)
47
+ end
48
+
49
+ def build_tool_session(invocation:, parent_sink:, resume_event: nil, resume_phase: nil)
50
+ ToolSessionBuilder.build(invocation: invocation, environment: self,
51
+ event_loop: @runtime.event_loop, parent_event_sink: parent_sink,
52
+ resume_event: resume_event, resume_phase: resume_phase)
53
+ end
54
+
55
+ def register_session(session, completion:)
56
+ @runtime.event_loop.register(session, completion: completion)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Agent
5
+ # Engine-only construction. State vocabulary, ordered guards and entry
6
+ # operations are supplied by the Agent framework, not defined here.
7
+ # @api private
8
+ class EngineSessionBuilder
9
+ def self.build(invocation:, environment:, event_loop:, resume_event: nil, resume_phase: nil)
10
+ event_sink = Phronomy::FSMSession::EventSink.new(event_loop: event_loop)
11
+ invocation.bind_event_sink!(event_sink)
12
+ actions = InvocationActions.build_entry_actions(invocation.agent,
13
+ environment, mode: invocation.mode, event_sink: event_sink)
14
+ phase_machine = PhaseMachineBuilder.new(entry_actions: actions).build
15
+
16
+ Phronomy::FSMSession.new(
17
+ context: invocation,
18
+ event_sink: event_sink,
19
+ entry_point: InvocationTransitions::ENTRY_POINT,
20
+ phase_machine_class: phase_machine,
21
+ entry_actions: {},
22
+ auto_state_set: InvocationTransitions::AUTO_STATE_SET,
23
+ declared_states: InvocationTransitions::DECLARED_STATES,
24
+ wait_state_names: InvocationTransitions::WAIT_STATES,
25
+ external_events: InvocationTransitions::EXTERNAL_EVENTS,
26
+ recursion_limit: InvocationTransitions.recursion_limit(invocation.agent.class.max_iterations),
27
+ event_loop: event_loop,
28
+ resume_event: resume_event,
29
+ resume_phase: resume_phase
30
+ )
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "state_machines"
4
+
5
+ module Phronomy
6
+ module Agent
7
+ class PhaseMachineBuilder
8
+ def initialize(entry_actions: {}, transitions: InvocationTransitions, operation_name: "Agent")
9
+ @entry_actions = entry_actions
10
+ @transitions = transitions
11
+ @operation_name = operation_name
12
+ end
13
+
14
+ def build
15
+ entry_actions = @entry_actions
16
+ callback_builder = method(:build_entry_callback)
17
+ transitions = @transitions
18
+ declared_states = transitions::DECLARED_STATES
19
+ entry_point = transitions::ENTRY_POINT
20
+
21
+ Class.new do
22
+ attr_accessor :context, :current_event
23
+
24
+ state_machine :phase, initial: entry_point do
25
+ declared_states.each { |state_name| state state_name }
26
+
27
+ transitions::EVENTS.each do |event_name, definitions|
28
+ event event_name do
29
+ definitions.each do |definition|
30
+ transition definition[:from] => definition[:to],
31
+ :if => ->(machine) {
32
+ transitions.allowed?(definition, machine.context)
33
+ }
34
+ end
35
+ end
36
+ end
37
+
38
+ entry_actions.each do |state_name, callables|
39
+ callables.each do |callable|
40
+ after_transition(
41
+ to: state_name,
42
+ do: callback_builder.call(callable, state_name)
43
+ )
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def build_entry_callback(callable, state_name)
53
+ ->(machine) {
54
+ result = callable.call(machine.context)
55
+ if result.is_a?(Phronomy::TaskResult)
56
+ raise Phronomy::InvalidAsyncEntryActionError,
57
+ "#{@operation_name} entry action for #{state_name.inspect} returned Phronomy::TaskResult"
58
+ end
59
+ machine.context = result if result.respond_to?(:set_graph_metadata)
60
+ }
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Agent
5
+ # Binds Agent Tool child progress to a concrete FSM session. Session routing
6
+ # is fresh for each incarnation and never replaces a durable Tool identity.
7
+ # @api private
8
+ class ToolSessionBuilder
9
+ def self.build(invocation:, environment:, event_loop:, parent_event_sink:,
10
+ resume_event: nil, resume_phase: nil)
11
+ event_sink = Phronomy::FSMSession::EventSink.new(event_loop: event_loop)
12
+ actions = ToolInvocationActions.build_entry_actions(environment, event_sink, parent_event_sink)
13
+ phase_machine = PhaseMachineBuilder.new(entry_actions: actions,
14
+ transitions: ToolInvocationTransitions, operation_name: "Tool").build
15
+
16
+ Phronomy::FSMSession.new(
17
+ context: invocation,
18
+ event_sink: event_sink,
19
+ entry_point: ToolInvocationTransitions::ENTRY_POINT,
20
+ phase_machine_class: phase_machine,
21
+ entry_actions: {},
22
+ auto_state_set: ToolInvocationTransitions::AUTO_STATE_SET,
23
+ declared_states: ToolInvocationTransitions::DECLARED_STATES,
24
+ wait_state_names: ToolInvocationTransitions::WAIT_STATES,
25
+ external_events: ToolInvocationTransitions::EXTERNAL_EVENTS,
26
+ recursion_limit: ToolInvocationTransitions::RECURSION_LIMIT,
27
+ event_loop: event_loop,
28
+ resume_event: resume_event,
29
+ resume_phase: resume_phase
30
+ )
31
+ end
32
+ end
33
+ end
34
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Phronomy
4
4
  module Agent
5
- class RubyLLMMaterializer
5
+ class RuntimeInput
6
6
  RuntimeProjection = Data.define(
7
7
  :system, :messages, :tool_classes, :ask_message,
8
8
  :model_config, :manifest, :manifest_ref
@@ -43,7 +43,7 @@ module Phronomy
43
43
  messages: materialize_message_segments(message_segments).freeze,
44
44
  tool_classes: tool_set.runtime_tools,
45
45
  ask_message: ask_message,
46
- model_config: Immutable.copy(model_config),
46
+ model_config: Phronomy::Values::Immutable.copy(model_config),
47
47
  manifest: manifest,
48
48
  manifest_ref: manifest_ref
49
49
  )
@@ -63,7 +63,7 @@ module Phronomy
63
63
  private
64
64
 
65
65
  def segment_from_record(record, position: 0)
66
- LLMInputManifest::Segment.new(
66
+ Phronomy::Context::LLMInputManifest::Segment.new(
67
67
  position: position,
68
68
  category: record.kind,
69
69
  role: record.role,
@@ -88,7 +88,7 @@ module Phronomy
88
88
  end
89
89
 
90
90
  def verify_adapter!(manifest)
91
- actual = Phronomy.configuration.llm_adapter.class.name
91
+ actual = Phronomy::Agent::Settings.current.llm_adapter.class.name
92
92
  return if manifest.adapter_name.nil? || manifest.adapter_name == actual
93
93
 
94
94
  raise Phronomy::ConfigurationError,
@@ -97,19 +97,16 @@ module Phronomy
97
97
 
98
98
  def verify_tool_definitions!(manifest)
99
99
  unless manifest.tool_definitions_ref
100
- return ToolDefinitionSet.build(
101
- @agent,
102
- additional_tools: @additional_tools
103
- )
100
+ return @agent.send(:tool_definition_set, additional_tools: @additional_tools)
104
101
  end
105
102
 
106
103
  expected_definitions = fetch_json(manifest.tool_definitions_ref)
107
104
  additional = @additional_tools
108
105
  if additional.empty?
109
- additional = Array(@agent.__coordination_config[:phronomy_handoff_bindings]).map(&:tool_class)
106
+ additional = Array(@agent.__execution_wiring[:phronomy_control_bindings]).map(&:tool_class)
110
107
  end
111
108
 
112
- ToolDefinitionSet.build(@agent, additional_tools: additional)
109
+ @agent.send(:tool_definition_set, additional_tools: additional)
113
110
  .select_definitions(expected_definitions)
114
111
  end
115
112
 
@@ -130,7 +127,7 @@ module Phronomy
130
127
  when :tool_result
131
128
  raise ArgumentError, "raw Tool result is not an LLM message"
132
129
  else
133
- RubyLLM::Message.new(
130
+ Phronomy::LLMAdapter::Message.new(
134
131
  role: segment.role,
135
132
  content: @persistence.contents.fetch_text(segment.content_ref),
136
133
  tool_call_id: segment.tool_call_id
@@ -151,42 +148,25 @@ module Phronomy
151
148
  end
152
149
 
153
150
  tool_calls = materialize_tool_calls(payload["tool_calls"])
154
- message = RubyLLM::Message.new(
151
+ Phronomy::LLMAdapter::Message.new(
155
152
  role: role,
156
- content: initial_content_for(role, payload.fetch("content", nil), tool_calls),
157
- tool_calls: tool_calls.empty? ? nil : tool_calls,
153
+ content: payload.fetch("content", nil),
154
+ tool_calls: tool_calls,
158
155
  tool_call_id: payload["tool_call_id"],
159
- model_id: payload["model_id"]
156
+ metadata: {"model_id" => payload["model_id"]}.compact
160
157
  )
161
-
162
- message.content = payload["content"] if payload.key?("content")
163
- message
164
- end
165
-
166
- def initial_content_for(role, content, tool_calls)
167
- return "" if role == :assistant && content.nil? && !tool_calls.empty?
168
-
169
- content.nil? ? "" : content
170
158
  end
171
159
 
172
160
  def materialize_tool_calls(payloads)
173
- Array(payloads).each_with_object({}) do |payload, result|
174
- tool_call_id = payload.fetch("id").to_s
175
- if result.key?(tool_call_id)
176
- raise ArgumentError, "duplicate Tool Call in assistant message: #{tool_call_id}"
177
- end
178
- result[tool_call_id] = materialize_tool_call(payload)
161
+ Array(payloads).map do |payload|
162
+ # Read the pre-unit6 durable format explicitly; SDK objects are never
163
+ # reconstituted during recovery. New records use opaque metadata.
164
+ metadata = payload.fetch("metadata", {}).dup
165
+ metadata["thought_signature"] = payload["thought_signature"] if payload["thought_signature"]
166
+ Phronomy::Tool::CallRequest.new(id: payload.fetch("id"), name: payload.fetch("name"),
167
+ arguments: payload.fetch("arguments", {}), metadata: metadata)
179
168
  end
180
169
  end
181
-
182
- def materialize_tool_call(payload)
183
- RubyLLM::ToolCall.new(
184
- id: payload.fetch("id").to_s,
185
- name: payload.fetch("name"),
186
- arguments: payload.fetch("arguments", {}),
187
- thought_signature: payload["thought_signature"]
188
- )
189
- end
190
170
  end
191
171
  end
192
172
  end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Agent
5
+ # Reads saved manifests and Provider output for continuation preparation.
6
+ # Callers perform these reads before applying live state on EventLoop.
7
+ # @api private
8
+ module SavedContextReader
9
+ module_function
10
+
11
+ def manifest_from_ref(agent, ref)
12
+ Phronomy::Persistence::StorageBoundary.call do
13
+ raw = agent.persistence.contents.fetch_json(ref)
14
+ Phronomy::Context::LLMInputManifest.from_h(raw)
15
+ end
16
+ end
17
+
18
+ def materialize_projection(agent, manifest_ref)
19
+ manifest = manifest_from_ref(agent, manifest_ref)
20
+ projection = Phronomy::Agent::RuntimeInput.new(
21
+ agent: agent,
22
+ persistence: agent.persistence
23
+ ).materialize(manifest: manifest, manifest_ref: manifest_ref)
24
+ [manifest, projection]
25
+ end
26
+
27
+ def latest_assistant_record(execution, llm_call_id: nil)
28
+ Array(execution.working_records).reverse.find do |record|
29
+ record.kind.to_sym == :assistant_message &&
30
+ (llm_call_id.nil? || record.llm_call_id.to_s == llm_call_id.to_s)
31
+ end
32
+ end
33
+
34
+ def provider_output_and_usage(agent, execution)
35
+ assistant = latest_assistant_record(execution)
36
+ payload = assistant ? agent.persistence.contents.fetch_json(assistant.content_ref) : {}
37
+ output = payload["content"]
38
+
39
+ call = execution.llm_calls.last
40
+ usage_hash = if call&.usage_ref
41
+ agent.persistence.contents.fetch_json(call.usage_ref)
42
+ else
43
+ {}
44
+ end
45
+ usage = Phronomy::LLMAdapter::TokenUsage.new(
46
+ input: usage_hash["input"] || usage_hash[:input] || usage_hash["input_tokens"] || usage_hash[:input_tokens],
47
+ output: usage_hash["output"] || usage_hash[:output] || usage_hash["output_tokens"] || usage_hash[:output_tokens],
48
+ cached: usage_hash["cached"] || usage_hash[:cached] || usage_hash["cache_read_tokens"] || usage_hash[:cache_read_tokens] || usage_hash["cached_tokens"] || usage_hash[:cached_tokens],
49
+ cache_creation: usage_hash["cache_creation"] || usage_hash[:cache_creation] || usage_hash["cache_write_tokens"] || usage_hash[:cache_write_tokens] || usage_hash["cache_creation_tokens"] || usage_hash[:cache_creation_tokens]
50
+ )
51
+ [output, usage]
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../common/configuration_error"
4
+
5
+ module Phronomy
6
+ module Agent
7
+ # Agent-owned option values and policy validation. Composition supplies
8
+ # concrete components and default values; execution reads at existing sites.
9
+ # @api private
10
+ class Settings
11
+ class << self
12
+ # Bind at boot without reading settings or starting runtime resources.
13
+ def install_provider(&provider)
14
+ raise ArgumentError, "settings provider requires a block" unless provider
15
+ @provider = provider
16
+ nil
17
+ end
18
+
19
+ # Read only when requested; no cache or change notification is involved.
20
+ def current
21
+ @provider.call
22
+ end
23
+ end
24
+
25
+ STREAM_CALLBACK_ERROR_POLICIES = %i[report fail_task].freeze
26
+ private_constant :STREAM_CALLBACK_ERROR_POLICIES
27
+
28
+ attr_accessor :default_model, :before_llm_input, :agent_store, :llm_adapter, :authorization_timeout
29
+ attr_reader :stream_callback_error_policy
30
+
31
+ def initialize(llm_adapter:, authorization_timeout:, stream_callback_error_policy:)
32
+ @llm_adapter = llm_adapter
33
+ @authorization_timeout = authorization_timeout
34
+ self.stream_callback_error_policy = stream_callback_error_policy
35
+ end
36
+
37
+ def stream_callback_error_policy=(value)
38
+ unless STREAM_CALLBACK_ERROR_POLICIES.include?(value)
39
+ allowed = STREAM_CALLBACK_ERROR_POLICIES.map(&:inspect).join(", ")
40
+ raise Phronomy::ConfigurationError,
41
+ "stream_callback_error_policy must be one of: #{allowed}"
42
+ end
43
+ @stream_callback_error_policy = value
44
+ end
45
+ end
46
+ end
47
+ end