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
@@ -1,11 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../../execution/concurrency/worker_input_restricted"
4
+
3
5
  require "securerandom"
4
6
  require "time"
5
7
 
6
8
  module Phronomy
7
9
  module Agent
8
10
  class AgentExecution
11
+ include Phronomy::Concurrency::WorkerInputRestricted
12
+
9
13
  ACTIVE_STATUSES = %i[preparing active suspended].freeze
10
14
  TERMINAL_STATUSES = %i[completed handed_off failed cancelled rejected blocked].freeze
11
15
  TRANSITIONS = {
@@ -55,13 +59,13 @@ module Phronomy
55
59
  ATTRIBUTES.each do |name|
56
60
  value = attributes.fetch(name)
57
61
  value = value.to_sym if %i[status phase].include?(name)
58
- instance_variable_set("@#{name}", Immutable.copy(value))
62
+ instance_variable_set("@#{name}", Phronomy::Values::Immutable.copy(value))
59
63
  end
60
64
  raise ArgumentError, "unknown execution status: #{status.inspect}" unless TRANSITIONS.key?(status)
61
65
  raise ArgumentError, "execution_revision must be non-negative" if execution_revision.negative?
62
- Immutable.validate_canonical_json!(metadata, label: "Execution metadata")
66
+ Phronomy::Values::Immutable.validate_canonical_json!(metadata, label: "Execution metadata")
63
67
  if approval_request
64
- Immutable.validate_canonical_json!(approval_request, label: "Approval request")
68
+ Phronomy::Values::Immutable.validate_canonical_json!(approval_request, label: "Approval request")
65
69
  end
66
70
  freeze
67
71
  end
@@ -86,7 +90,7 @@ module Phronomy
86
90
  end
87
91
 
88
92
  # Current semantic payload representation. Persistence format identity and
89
- # compatibility validation are owned by Persistence::DurableCodec.
93
+ # compatibility validation are owned by Agent::Persistence::Codec.
90
94
  # @api public
91
95
  def to_h
92
96
  ATTRIBUTES.to_h do |name|
@@ -1,22 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../../execution/concurrency/worker_input_restricted"
4
+
3
5
  require "securerandom"
4
6
  require "time"
5
7
 
6
8
  module Phronomy
7
9
  module Agent
8
10
  class AgentInvocation
11
+ include Phronomy::Concurrency::WorkerInputRestricted
12
+
9
13
  # execution_id is the logical Agent execution parent. Concrete Runtime
10
14
  # routing identity belongs only to the owning FSMSession incarnation.
11
- TOOL_EVENT_TYPES = %i[
12
- tool_authorized
13
- tool_approval_required
14
- tool_completed
15
- tool_failed
16
- tool_rejected
17
- tool_cancelled
18
- ].freeze
19
-
20
15
  LLM_EVENT_TYPES = %i[llm_completed llm_failed].freeze
21
16
  CALLBACK_FAILED_EVENTS = %i[application_callback_failed].freeze
22
17
  SETUP_FAILED_EVENTS = %i[llm_setup_failed tool_setup_failed].freeze
@@ -38,7 +33,8 @@ module Phronomy
38
33
  end
39
34
 
40
35
  attr_accessor :input,
41
- :chat,
36
+ :messages,
37
+ :tools,
42
38
  :output,
43
39
  :usage,
44
40
  :input_blocked,
@@ -90,7 +86,8 @@ module Phronomy
90
86
  @event_sink = nil
91
87
  @mode = (mode || :invoke).to_sym
92
88
 
93
- @chat = nil
89
+ @messages = []
90
+ @tools = {}.freeze
94
91
  @output = nil
95
92
  @usage = nil
96
93
  @input_blocked = false
@@ -144,17 +141,6 @@ module Phronomy
144
141
  end
145
142
 
146
143
  durable_id = llm_call_id
147
- if durable_id.nil? &&
148
- Phronomy::Runtime.instance.event_loop.current?
149
- state =
150
- Phronomy::Runtime.instance.event_loop.agent_execution_state(
151
- execution_id
152
- )
153
- durable_id = state&.execution&.metadata&.fetch(
154
- RecoverySupport::PENDING_LLM_ID_KEY,
155
- nil
156
- )
157
- end
158
144
  unless durable_id
159
145
  raise Phronomy::ExecutionRehydrationRequiredError,
160
146
  "Provider Call semantic identity was not durably established before dispatch"
@@ -209,7 +195,7 @@ module Phronomy
209
195
  return true
210
196
  end
211
197
 
212
- return false unless TOOL_EVENT_TYPES.include?(event.type)
198
+ return false unless InvocationTransitions::TOOL_EVENTS.include?(event.type)
213
199
 
214
200
  invocation = tool_invocation(event.payload&.fetch(:tool_invocation_id, nil))
215
201
  return true unless invocation
@@ -284,7 +270,7 @@ module Phronomy
284
270
 
285
271
  @user_message_sent = true
286
272
  @output = response.content
287
- @usage = Phronomy::TokenUsage.from_tokens(response.tokens)
273
+ @usage = response.usage
288
274
  @pending_tool_calls = []
289
275
  self
290
276
  end
@@ -326,7 +312,7 @@ module Phronomy
326
312
  def record_tool_results!
327
313
  @tool_invocations.each do |invocation|
328
314
  tool_content = invocation.result.to_s
329
- @chat.add_message(
315
+ @messages << Phronomy::LLMAdapter::Message.new(
330
316
  role: :tool,
331
317
  content: tool_content,
332
318
  tool_call_id: invocation.tool_call_id
@@ -435,7 +421,7 @@ module Phronomy
435
421
  private
436
422
 
437
423
  def handoff_matches_for(calls)
438
- bindings = Array(@config[:phronomy_handoff_bindings])
424
+ bindings = Array(@config[:phronomy_control_bindings])
439
425
  return [] if bindings.empty?
440
426
 
441
427
  by_name = bindings.to_h { |binding| [binding.tool_name.to_s, binding] }
@@ -446,25 +432,15 @@ module Phronomy
446
432
  end
447
433
 
448
434
  def build_handoff_request(tool_call, binding, llm_call_id:)
449
- handoff = binding.handoff
450
- unless handoff.source_agent.equal?(@agent)
451
- raise Phronomy::HandoffError,
452
- "Handoff capability is not bound to the active Source Agent"
435
+ unless binding.source_agent_id == @agent.agent_id
436
+ raise Phronomy::HandoffError, "Control capability is not bound to the active Source Agent"
453
437
  end
454
-
455
- args = tool_call.respond_to?(:arguments) ? tool_call.arguments : {}
456
- args = (args || {}).to_h.transform_keys(&:to_sym)
457
- selection = handoff.policy.selectable_categories.each_with_object({}) do |category, result|
458
- key = :"include_#{category}"
459
- result[category] = args[key] if args.key?(key)
438
+ request = binding.request(arguments: tool_call.arguments || {}, llm_call_id: llm_call_id,
439
+ tool_call_id: tool_call.respond_to?(:id) ? tool_call.id : nil)
440
+ unless request.is_a?(ControlRequest)
441
+ raise ArgumentError, "Control capability must return Agent::ControlRequest"
460
442
  end
461
- Phronomy::Agent::HandoffRequest.new(
462
- handoff: handoff,
463
- responsibility: args.fetch(:responsibility),
464
- selection_intent: selection,
465
- llm_call_id: llm_call_id,
466
- tool_call_id: tool_call.respond_to?(:id) ? tool_call.id : nil
467
- )
443
+ request
468
444
  end
469
445
 
470
446
  def apply_llm_event(event)
@@ -481,7 +457,7 @@ module Phronomy
481
457
  call = @current_llm_call
482
458
  @llm_results << {
483
459
  llm_call_id: call.fetch(:llm_call_id),
484
- response: canonical_response_for_llm_result(result),
460
+ response: result.response,
485
461
  error: result.error,
486
462
  streaming: result.streaming,
487
463
  manifest_ref: call.fetch(:manifest_ref),
@@ -495,16 +471,17 @@ module Phronomy
495
471
  end
496
472
 
497
473
  if result.error
498
- if result.error.is_a?(ToolCallIntercepted)
499
- accept_tool_calls!(
500
- result.error.tool_calls,
501
- llm_call_id: result.llm_call_id
502
- )
474
+ @error = result.error
475
+ elsif result.response.is_a?(Phronomy::LLMAdapter::Response)
476
+ @messages << result.response
477
+ if result.response.tool_call?
478
+ @usage = result.response.usage
479
+ accept_tool_calls!(result.response.tool_calls, llm_call_id: result.llm_call_id)
503
480
  else
504
- @error = result.error
481
+ apply_llm_response!(result.response)
505
482
  end
506
483
  else
507
- apply_llm_response!(result.response)
484
+ @error = Phronomy::LLMAdapter::InvalidResultError.new("LLM operation completed without a Response")
508
485
  end
509
486
  true
510
487
  end
@@ -515,7 +492,7 @@ module Phronomy
515
492
  end
516
493
 
517
494
  def warn_stale_llm_result(llm_call_id)
518
- Phronomy.configuration.logger&.warn(
495
+ Phronomy::RuntimeSettings.current.logger&.warn(
519
496
  "[Phronomy] Dropped stale Provider result: " \
520
497
  "execution_id=#{@execution_id} expected_llm_call_id=#{current_llm_call_id.inspect} " \
521
498
  "actual_llm_call_id=#{llm_call_id.inspect}"
@@ -524,14 +501,6 @@ module Phronomy
524
501
  nil
525
502
  end
526
503
 
527
- def canonical_response_for_llm_result(result)
528
- if result.error.is_a?(ToolCallIntercepted)
529
- result.error.assistant_outcome || ProviderCallOutcome.capture(result.response)
530
- else
531
- ProviderCallOutcome.capture(result.response)
532
- end
533
- end
534
-
535
504
  # Canonical runtime recording is independent of Application callback health.
536
505
  # Once an event is observed it is appended even after a listener has failed.
537
506
  def deliver_event(event)
@@ -540,7 +509,7 @@ module Phronomy
540
509
  # listener receives the notification event separately.
541
510
  @runtime_events << StreamEvent.new(
542
511
  type: event.type,
543
- payload: Immutable.copy(event.payload)
512
+ payload: Phronomy::Values::Immutable.copy(event.payload)
544
513
  )
545
514
  listener = @callback_failure ? nil : @event_listener
546
515
  return unless listener
@@ -560,13 +529,13 @@ module Phronomy
560
529
  if @event_sink
561
530
  accepted = @event_sink.post(:application_callback_failed, {failure: failure})
562
531
  unless accepted
563
- Phronomy.configuration.logger&.warn(
532
+ Phronomy::RuntimeSettings.current.logger&.warn(
564
533
  "[Phronomy] Callback failure recorded but could not notify " \
565
534
  "FSMSession #{@event_sink.fsm_session_id}: execution_id=#{@execution_id}"
566
535
  )
567
536
  end
568
537
  end
569
- Phronomy.configuration.logger&.warn(
538
+ Phronomy::RuntimeSettings.current.logger&.warn(
570
539
  "[Phronomy] Application event listener failed: " \
571
540
  "#{failure.error.class}: #{failure.error.message}"
572
541
  )
@@ -0,0 +1,108 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../execution/concurrency/worker_input_restricted"
4
+
5
+ module Phronomy
6
+ module Agent
7
+ # Persists one approval decision with its captured recovery facts.
8
+ # Admission, live state and session resumption stay with the execution owner.
9
+ # @api private
10
+ class ApprovalResumeCommit
11
+ include Phronomy::Concurrency::WorkerInputRestricted
12
+
13
+ Command = Data.define(
14
+ :execution_id, :expected_execution_revision,
15
+ :root, :execution, :approval_request_id, :approved, :tool_batch_snapshot
16
+ )
17
+ Result = Data.define(:execution, :root)
18
+
19
+ # @api private
20
+ def initialize(agent_id:, persistence:)
21
+ @agent_id = agent_id
22
+ @persistence = persistence
23
+ end
24
+
25
+ # @api private
26
+ def commit(operation)
27
+ request = validated_approval_request(operation)
28
+ execution = stage_recovery_snapshot(operation)
29
+ persist_decision(execution, operation.root, request, operation.approved)
30
+ end
31
+
32
+ private
33
+
34
+ def validated_approval_request(operation)
35
+ request = operation.execution.approval_request || {}
36
+ request_id = request["id"] || request[:id]
37
+ unless request_id.to_s == operation.approval_request_id
38
+ raise ArgumentError,
39
+ "approval request does not match execution #{operation.execution.execution_id}"
40
+ end
41
+ request
42
+ end
43
+
44
+ def stage_recovery_snapshot(operation)
45
+ return operation.execution unless operation.tool_batch_snapshot
46
+
47
+ ExecutionMetadata.with_values(
48
+ operation.execution,
49
+ ExecutionMetadata::TOOL_BATCH_METADATA_KEY => operation.tool_batch_snapshot
50
+ )
51
+ end
52
+
53
+ def persist_decision(execution, root, request, approved)
54
+ updated = next_root = nil
55
+ @persistence.transaction do |tx|
56
+ decision = record_decision(tx, execution, root, request, approved)
57
+ updated = save_resumed_execution(tx, execution, request, approved, decision)
58
+ next_root = save_resumed_root(tx, root)
59
+ end
60
+ Result.new(execution: updated, root: next_root)
61
+ end
62
+
63
+ def record_decision(tx, execution, root, request, approved)
64
+ decision_ref = tx.contents.put_json(
65
+ "approval_request_id" => (request["id"] || request[:id]).to_s,
66
+ "approved" => approved
67
+ )
68
+ JournalRecord.new(
69
+ agent_id: @agent_id,
70
+ execution_id: execution.execution_id,
71
+ kind: :approval_decided,
72
+ channel: :approval,
73
+ content_ref: decision_ref,
74
+ context_generation: root.transcript_generation,
75
+ context_candidate: false
76
+ )
77
+ end
78
+
79
+ def save_resumed_execution(tx, current, request, approved, decision)
80
+ updated = current.with(
81
+ status: :active,
82
+ phase: :resuming,
83
+ working_records: current.working_records + [decision],
84
+ approval_request: request.merge("approved" => approved)
85
+ )
86
+ tx.executions.save(
87
+ current.execution_id,
88
+ expected_revision: current.execution_revision,
89
+ execution: updated
90
+ )
91
+ updated
92
+ end
93
+
94
+ def save_resumed_root(tx, current)
95
+ updated = current.with(
96
+ agent_revision: current.agent_revision + 1,
97
+ lifecycle_status: :active
98
+ )
99
+ tx.agents.save(
100
+ current.agent_id,
101
+ expected_revision: current.agent_revision,
102
+ root: updated
103
+ )
104
+ updated
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,195 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../../execution/concurrency/worker_input_restricted"
4
+
5
+ module Phronomy
6
+ module Agent
7
+ # Commits the operation-specific prerequisites for Provider/Tool dispatch.
8
+ # The Agent supplies existing context/hook services, never live-state writes.
9
+ # Inputs and results belong to this worker boundary; delivery stays with the
10
+ # execution owner. No operation-local state is retained between calls.
11
+ # @api private
12
+ class DispatchPreparation
13
+ include Phronomy::Concurrency::WorkerInputRestricted
14
+
15
+ ProviderCommand = Data.define(
16
+ :execution_id, :fsm_session_id, :expected_execution_revision,
17
+ :root, :journal_records, :execution, :base_manifest,
18
+ :invocation_config, :runtime_snapshot, :streaming,
19
+ :pending_llm_call_id, :pending_llm_started_at
20
+ )
21
+ ToolCommand = Data.define(
22
+ :execution_id, :fsm_session_id, :expected_execution_revision,
23
+ :root, :execution, :runtime_snapshot, :tool_batch_snapshot, :invocation_config
24
+ )
25
+ ProviderResult = Data.define(:execution, :runtime_projection, :error)
26
+ ToolResult = Data.define(:execution)
27
+ # @api private
28
+ def initialize(agent:, persistence:)
29
+ @agent = agent
30
+ @persistence = persistence
31
+ end
32
+
33
+ # @api private
34
+ def prepare_provider(operation)
35
+ staged = stage_provider_call(operation)
36
+ recorded = encode_provider_records(operation, staged)
37
+ assembler, prepared = prepare_provider_context(operation, recorded)
38
+ committed, manifest, manifest_ref = commit_provider_preparation(
39
+ operation, recorded, assembler, prepared
40
+ )
41
+ materialize_provider_result(committed.execution, manifest, manifest_ref)
42
+ end
43
+
44
+ # @api private
45
+ def prepare_tools(operation)
46
+ updated = @persistence.transaction do |tx|
47
+ assert_local_durable_base!(tx, operation.root)
48
+ build_tool_dispatch_execution(operation, tx)
49
+ end
50
+ change = ExecutionChange.new(agent: @agent, current: operation.execution,
51
+ root: operation.root, kind: :dispatch, updated: updated, config: operation.invocation_config)
52
+ ToolResult.new(execution: change.perform)
53
+ end
54
+
55
+ private
56
+
57
+ def stage_provider_call(operation)
58
+ metadata = operation.execution.metadata.dup
59
+ metadata.delete(ExecutionMetadata::TOOL_BATCH_METADATA_KEY)
60
+ metadata.delete(ExecutionMetadata::RECOVERY_METADATA_KEY)
61
+ metadata[ExecutionMetadata::PENDING_LLM_ID_KEY] = operation.pending_llm_call_id
62
+ metadata[ExecutionMetadata::PENDING_LLM_STARTED_AT_KEY] = operation.pending_llm_started_at
63
+ metadata[ExecutionMetadata::CONTRACT_VERSION_KEY] = ExecutionMetadata::CONTRACT_VERSION
64
+ operation.execution.with(
65
+ execution_revision: operation.execution.execution_revision,
66
+ metadata: metadata
67
+ )
68
+ end
69
+
70
+ def encode_provider_records(operation, staged)
71
+ encoded_records = call_records = nil
72
+ # Content-addressed records may be added here; Execution is not advanced.
73
+ @persistence.transaction do |tx|
74
+ assert_local_durable_base!(tx, operation.root)
75
+ encoded_records, call_records = RuntimeRecordEncoder.encode(
76
+ staged,
77
+ agent_id: @agent.agent_id,
78
+ tx: tx,
79
+ snapshot: operation.runtime_snapshot,
80
+ context_candidate: true,
81
+ agent_root: operation.root
82
+ )
83
+ end
84
+ staged.with(
85
+ execution_revision: staged.execution_revision,
86
+ phase: :preparing_llm_call,
87
+ working_records: staged.working_records + encoded_records,
88
+ llm_calls: staged.llm_calls + call_records
89
+ )
90
+ end
91
+
92
+ def prepare_provider_context(operation, recorded)
93
+ patch = @agent.send(
94
+ :run_before_llm_input_hooks,
95
+ call_sequence: recorded.llm_calls.length + 1,
96
+ config: operation.invocation_config
97
+ )
98
+ assembler = ContextPreparation.new(
99
+ agent: @agent,
100
+ persistence: @persistence,
101
+ journal_records: operation.journal_records
102
+ )
103
+ prepared = assembler.prepare_followup(
104
+ base_manifest: operation.base_manifest,
105
+ agent_root: operation.root,
106
+ execution: recorded,
107
+ config: operation.invocation_config,
108
+ patch: patch
109
+ )
110
+ @agent.send(
111
+ :check_cancellation!,
112
+ operation.invocation_config,
113
+ "invocation cancelled after context policy"
114
+ )
115
+ [assembler, prepared]
116
+ end
117
+
118
+ def commit_provider_preparation(operation, recorded, assembler, prepared)
119
+ manifest, manifest_ref = @persistence.transaction do |tx|
120
+ assert_local_durable_base!(tx, operation.root)
121
+ Phronomy::Context::Assembly.new.store(prepared, contents: tx.contents)
122
+ end
123
+ updated = execution_with_provider_manifest(recorded, manifest_ref)
124
+ change = ExecutionChange.new(agent: @agent, current: operation.execution,
125
+ root: operation.root, kind: :dispatch, updated: updated, config: operation.invocation_config)
126
+ intended = ProviderResult.new(execution: change.perform, runtime_projection: nil, error: nil)
127
+ [intended, manifest, manifest_ref]
128
+ end
129
+
130
+ def execution_with_provider_manifest(recorded, manifest_ref)
131
+ refs = Array(recorded.metadata["manifest_refs"]) + [manifest_ref]
132
+ recorded.with(
133
+ phase: :calling_llm,
134
+ metadata: recorded.metadata.merge(
135
+ "manifest_ref" => manifest_ref,
136
+ "manifest_refs" => refs
137
+ )
138
+ )
139
+ end
140
+
141
+ def materialize_provider_result(execution, manifest, manifest_ref)
142
+ projection = materialization_error = nil
143
+ begin
144
+ projection = RuntimeInput.new(
145
+ agent: @agent, persistence: @persistence
146
+ ).materialize(manifest: manifest, manifest_ref: manifest_ref)
147
+ rescue => error
148
+ materialization_error = error
149
+ end
150
+ ProviderResult.new(
151
+ execution: execution, runtime_projection: projection,
152
+ error: materialization_error
153
+ )
154
+ end
155
+
156
+ def build_tool_dispatch_execution(operation, tx)
157
+ encoded_records, call_records = RuntimeRecordEncoder.encode(
158
+ operation.execution,
159
+ agent_id: @agent.agent_id,
160
+ tx: tx,
161
+ snapshot: operation.runtime_snapshot,
162
+ context_candidate: true,
163
+ agent_root: operation.root
164
+ )
165
+ operation.execution.with(
166
+ phase: :dispatching_tools,
167
+ working_records: operation.execution.working_records + encoded_records,
168
+ llm_calls: operation.execution.llm_calls + call_records,
169
+ metadata: tool_dispatch_metadata(operation)
170
+ )
171
+ end
172
+
173
+ def tool_dispatch_metadata(operation)
174
+ metadata = operation.execution.metadata.dup
175
+ metadata.delete(ExecutionMetadata::PENDING_LLM_ID_KEY)
176
+ metadata.delete(ExecutionMetadata::PENDING_LLM_STARTED_AT_KEY)
177
+ metadata.delete(ExecutionMetadata::RECOVERY_METADATA_KEY)
178
+ metadata.delete("framework_calls_pending")
179
+ metadata[ExecutionMetadata::TOOL_BATCH_METADATA_KEY] =
180
+ Phronomy::Values::Serializable.convert(operation.tool_batch_snapshot,
181
+ unsupported_message: "Recovery value is not canonically serializable")
182
+ metadata[ExecutionMetadata::CONTRACT_VERSION_KEY] = ExecutionMetadata::CONTRACT_VERSION
183
+ metadata
184
+ end
185
+
186
+ def assert_local_durable_base!(tx, root)
187
+ tx.assert_agent_watermark!(
188
+ agent_id: root.agent_id,
189
+ agent_revision: root.agent_revision,
190
+ journal_position: root.journal_position
191
+ )
192
+ end
193
+ end
194
+ end
195
+ end