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,10 +1,15 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../../execution/concurrency/worker_input_restricted"
4
+
5
+ require "digest"
3
6
  require "securerandom"
4
7
 
5
8
  module Phronomy
6
9
  module Agent
7
10
  class ToolInvocation
11
+ include Phronomy::Concurrency::WorkerInputRestricted
12
+
8
13
  AuthorizationOutcome = Data.define(
9
14
  :tool_invocation_id, :decision, :facts, :reason, :error, :cancelled
10
15
  ) do
@@ -74,6 +79,20 @@ module Phronomy
74
79
  :origin,
75
80
  :metadata
76
81
 
82
+ # Stable identity used both before dispatch and when rebuilding facts.
83
+ # This does not construct, authorize, or execute a Tool invocation.
84
+ # @api private
85
+ def self.semantic_id(execution_id:, llm_call_id:, tool_call_id:, tool_name:)
86
+ source = [
87
+ "tool_invocation",
88
+ execution_id.to_s,
89
+ llm_call_id.to_s,
90
+ tool_call_id.to_s,
91
+ tool_name.to_s
92
+ ].join("\0")
93
+ "tool_invocation-#{Digest::SHA256.hexdigest(source)}".freeze
94
+ end
95
+
77
96
  def self.missing(
78
97
  execution_id:,
79
98
  agent:,
@@ -135,6 +154,16 @@ module Phronomy
135
154
  @phase = phase
136
155
  end
137
156
 
157
+ # Restores a newly constructed invocation from materialized saved facts.
158
+ # Recovery owns snapshot decoding and identity matching. This operation
159
+ # owns state application; it neither dispatches nor reevaluates approval.
160
+ # @api private
161
+ def restore_state!(status:, result: nil, approval_item: nil)
162
+ restore_saved_state!(status, result)
163
+ restore_approval_evidence!(approval_item)
164
+ self
165
+ end
166
+
138
167
  def handle_fsm_event(event)
139
168
  case event.type
140
169
  when :authorization_completed
@@ -167,11 +196,7 @@ module Phronomy
167
196
  def validate!
168
197
  return self if terminal?
169
198
 
170
- validated, schema_error = if @tool.respond_to?(:validate_and_coerce, true)
171
- @tool.send(:validate_and_coerce, @raw_arguments)
172
- else
173
- [@raw_arguments, nil]
174
- end
199
+ validated, schema_error = @tool.validate_arguments(@raw_arguments)
175
200
 
176
201
  if schema_error
177
202
  if @tool.class.respond_to?(:on_schema_error) && @tool.class.on_schema_error == :raise
@@ -195,27 +220,21 @@ module Phronomy
195
220
  self
196
221
  end
197
222
 
198
- def start_authorization(runtime: Phronomy::Runtime.instance, &callback)
223
+ def start_authorization(environment:, &callback)
199
224
  raise ArgumentError, "start_authorization requires a callback" unless callback
200
225
 
201
226
  command = authorization_command
202
227
  evaluator = self.class
203
228
  tool_invocation_id = @id.to_s.freeze
204
- pool = runtime.pool(
205
- :authorization,
206
- size: Phronomy.configuration.authorization_pool_size,
207
- queue_size: Phronomy.configuration.authorization_queue_size
208
- )
209
229
  timeout = @config.fetch(
210
230
  :authorization_timeout,
211
- Phronomy.configuration.authorization_timeout
231
+ Phronomy::Agent::Settings.current.authorization_timeout
212
232
  )
213
- operation = pool.submit(
233
+ operation = environment.submit_authorization(
214
234
  timeout: timeout,
215
- cancellation_token: @config[:cancellation_token],
216
- on_full: :raise
235
+ cancellation_token: @config[:cancellation_token]
217
236
  ) { evaluator.send(:evaluate_authorization_command, command) }
218
- runtime.event_loop.supervise_agent_operation(@execution_id, operation)
237
+ environment.registry.supervise_agent_operation(@execution_id, operation)
219
238
  operation.on_complete do |outcome, error|
220
239
  callback.call(
221
240
  error ? evaluator.send(:authorization_failure_result, tool_invocation_id, error) : outcome
@@ -229,7 +248,7 @@ module Phronomy
229
248
  self
230
249
  end
231
250
 
232
- def start_execution(runtime: Phronomy::Runtime.instance, &callback)
251
+ def start_execution(environment:, &callback)
233
252
  raise ArgumentError, "start_execution requires a callback" unless callback
234
253
  unless dispatchable?
235
254
  callback.call(ExecutionOutcome.new(
@@ -241,45 +260,24 @@ module Phronomy
241
260
  return self
242
261
  end
243
262
 
244
- trace_handle = nil
245
- case @tool.class.execution_mode
246
- when :cooperative, :offloaded
247
- trace_handle = Phronomy::Tracing::Automatic.start(
248
- "tool.execute",
249
- input: @arguments || @raw_arguments,
250
- agent_id: @agent.agent_id,
251
- execution_id: @execution_id,
252
- tool_invocation_id: @id,
253
- tool_call_id: @tool_call_id,
254
- tool_name: @tool_name,
255
- **@agent.send(:_build_caller_meta, @config)
256
- )
257
- operation = start_async_tool_operation(runtime)
258
- unless operation.respond_to?(:on_complete)
259
- raise Phronomy::ToolError,
260
- "Tool #{@tool.class.name}#call_async must return a completion handle"
261
- end
262
- runtime.event_loop.supervise_agent_operation(@execution_id, operation)
263
-
264
- evaluator = self.class
265
- tool_invocation_id = @id.to_s.freeze
266
- operation.on_complete do |result, error|
267
- Phronomy::Tracing::Automatic.finish(
268
- trace_handle,
269
- output: result,
270
- error: error
271
- )
272
- callback.call(
273
- evaluator.send(:build_execution_outcome, tool_invocation_id, result, error)
274
- )
275
- end
276
- else
277
- callback.call(ExecutionOutcome.new(
278
- tool_invocation_id: @id,
279
- error: Phronomy::ConfigurationError.new(
280
- "unknown Tool execution_mode: #{@tool.class.execution_mode.inspect}"
281
- )
282
- ))
263
+ trace_handle = Phronomy::Tracing::Automatic.start(
264
+ "tool.execute",
265
+ input: @arguments || @raw_arguments,
266
+ agent_id: @agent.agent_id,
267
+ execution_id: @execution_id,
268
+ tool_invocation_id: @id,
269
+ tool_call_id: @tool_call_id,
270
+ tool_name: @tool_name,
271
+ **@agent.send(:_build_caller_meta, @config)
272
+ )
273
+ operation = start_async_tool_operation(environment)
274
+ environment.registry.supervise_agent_operation(@execution_id, operation)
275
+
276
+ evaluator = self.class
277
+ tool_invocation_id = @id.to_s.freeze
278
+ operation.on_complete do |result, error|
279
+ Phronomy::Tracing::Automatic.finish(trace_handle, output: result, error: error)
280
+ callback.call(evaluator.send(:build_execution_outcome, tool_invocation_id, result, error))
283
281
  end
284
282
  self
285
283
  rescue => error
@@ -332,7 +330,7 @@ module Phronomy
332
330
  end
333
331
 
334
332
  def tool_schema
335
- @tool&.respond_to?(:params_schema) ? @tool.params_schema : {}
333
+ @tool&.respond_to?(:parameters_schema) ? @tool.parameters_schema : {}
336
334
  end
337
335
 
338
336
  def display_arguments
@@ -345,6 +343,40 @@ module Phronomy
345
343
 
346
344
  private
347
345
 
346
+ def restore_saved_state!(status, result)
347
+ case status
348
+ when :awaiting_approval
349
+ validate! unless terminal?
350
+ @final_decision = :require_approval
351
+ mark_awaiting_approval!
352
+ when :authorized
353
+ validate! unless terminal?
354
+ @final_decision = :allow
355
+ mark_authorized!
356
+ when :completed
357
+ @result = result
358
+ @status = :completed
359
+ when :rejected
360
+ mark_rejected!
361
+ when :failed
362
+ mark_framework_failed!(
363
+ Phronomy::ToolError.new("durably restored Tool preflight failure")
364
+ )
365
+ when :cancelled
366
+ mark_cancelled!
367
+ else
368
+ raise Phronomy::ExecutionRehydrationRequiredError,
369
+ "unsupported durable Tool snapshot state: #{status.inspect}"
370
+ end
371
+ end
372
+
373
+ def restore_approval_evidence!(item)
374
+ return unless item
375
+
376
+ @facts = immutable_copy(item.facts)
377
+ @authorization_reason = item.reason
378
+ end
379
+
348
380
  def authorization_command
349
381
  definition = @agent.class.agent_definition
350
382
 
@@ -354,28 +386,20 @@ module Phronomy
354
386
  agent_definition_version: Integer(definition.fetch(:version)),
355
387
  execution_id: @execution_id,
356
388
  tool_name: @tool_name.to_s.freeze,
357
- tool_schema: self.class.send(:immutable_command_copy, tool_schema),
389
+ tool_schema: Phronomy::Tool::Authorization.snapshot(tool_schema),
358
390
  tool_invocation_id: @id.to_s.freeze,
359
391
  tool_call_id: @tool_call_id&.to_s&.freeze,
360
- arguments: self.class.send(
361
- :immutable_command_copy,
392
+ arguments: Phronomy::Tool::Authorization.snapshot(
362
393
  @arguments || {}
363
394
  ),
364
- approval_policy: self.class.send(
365
- :safe_behavior_handle, @approval_policy, "approval_policy"
366
- ),
367
- approval_facts_callable: self.class.send(
368
- :safe_behavior_handle, authorization_facts_callable, "approval_facts"
369
- ),
370
- approval_requirement: self.class.send(
371
- :safe_behavior_handle, authorization_requirement, "requires_approval"
372
- ),
373
- approval_context: self.class.send(
374
- :immutable_command_copy,
395
+ approval_policy: Phronomy::Tool::Authorization.behavior(@approval_policy, "approval_policy"),
396
+ approval_facts_callable: Phronomy::Tool::Authorization.behavior(authorization_facts_callable, "approval_facts"),
397
+ approval_requirement: Phronomy::Tool::Authorization.behavior(authorization_requirement, "requires_approval"),
398
+ approval_context: Phronomy::Tool::Authorization.snapshot(
375
399
  @approval_context
376
400
  ),
377
401
  origin: @origin,
378
- metadata: self.class.send(:immutable_command_copy, @metadata)
402
+ metadata: Phronomy::Tool::Authorization.snapshot(@metadata)
379
403
  )
380
404
  end
381
405
 
@@ -393,58 +417,18 @@ module Phronomy
393
417
 
394
418
  def self.evaluate_authorization_command(command)
395
419
  request = build_authorization_request(command, facts: {}, default_decision: nil)
396
- facts = evaluate_authorization_facts(command)
397
- request = request.with(facts: facts)
398
- default_decision = evaluate_default_authorization_decision(command, request)
399
- request = request.with(default_decision: default_decision)
400
- decision = command.approval_policy ? command.approval_policy.call(request) : default_decision
401
- decision = decision.to_sym if decision.respond_to?(:to_sym)
402
-
403
- unless ApprovalEvaluationRequest::VALID_DECISIONS.include?(decision)
404
- raise Phronomy::ConfigurationError,
405
- "tool_approval_policy must return :allow, :require_approval, or :reject " \
406
- "(got #{decision.inspect})"
407
- end
408
-
409
- reason = if decision == :require_approval
410
- (command.origin == :mcp) ?
411
- "MCP Tool execution requires approval" : "Tool execution requires approval"
412
- end
420
+ decision = Phronomy::Tool::Authorization.call(
421
+ request: request, arguments: command.arguments, context: command.approval_context,
422
+ facts: command.approval_facts_callable, requirement: command.approval_requirement,
423
+ policy: command.approval_policy
424
+ )
413
425
  AuthorizationOutcome.new(
414
426
  tool_invocation_id: command.tool_invocation_id,
415
- decision: decision,
416
- facts: facts,
417
- reason: reason
427
+ decision: decision.decision, facts: decision.facts, reason: decision.reason
418
428
  )
419
429
  end
420
430
  private_class_method :evaluate_authorization_command
421
431
 
422
- def self.evaluate_authorization_facts(command)
423
- callable = command.approval_facts_callable
424
- return {} unless callable
425
-
426
- value = callable.call(command.arguments, command.approval_context)
427
- unless value.nil? || value.is_a?(Hash)
428
- raise Phronomy::ConfigurationError,
429
- "approval_facts must return a Hash or nil (got #{value.class})"
430
- end
431
- immutable_command_copy(value || {})
432
- end
433
- private_class_method :evaluate_authorization_facts
434
-
435
- def self.evaluate_default_authorization_decision(command, request)
436
- requirement = command.approval_requirement
437
- requirement = requirement.call(request) if requirement.respond_to?(:call)
438
- case requirement
439
- when true then :require_approval
440
- when false, nil then :allow
441
- else
442
- raise Phronomy::ConfigurationError,
443
- "requires_approval callable must return true or false (got #{requirement.inspect})"
444
- end
445
- end
446
- private_class_method :evaluate_default_authorization_decision
447
-
448
432
  def self.build_authorization_request(command, facts:, default_decision:)
449
433
  ApprovalEvaluationRequest.new(
450
434
  agent_id: command.agent_id,
@@ -465,116 +449,19 @@ module Phronomy
465
449
  end
466
450
  private_class_method :build_authorization_request
467
451
 
468
- def self.immutable_command_copy(value)
469
- if phronomy_managed_live_domain_object?(value)
470
- raise Phronomy::ConfigurationError,
471
- "authorization worker snapshot cannot contain Phronomy-managed live " \
472
- "domain object #{value.class}"
473
- end
474
-
475
- case value
476
- when Hash
477
- value.each_with_object({}) do |(key, item), result|
478
- result[immutable_command_copy(key)] = immutable_command_copy(item)
479
- end.freeze
480
- when Array
481
- value.map { |item| immutable_command_copy(item) }.freeze
482
- when String
483
- value.dup.freeze
484
- else
485
- # Application-defined opaque objects are permitted by ACS-11 and remain
486
- # Application-owned. A stricter general value-type protocol is deferred.
487
- value
488
- end
489
- end
490
- private_class_method :immutable_command_copy
491
-
492
- def self.phronomy_managed_live_domain_object?(value)
493
- value.is_a?(Phronomy::Agent::Base) ||
494
- value.is_a?(Phronomy::Agent::AgentRoot) ||
495
- value.is_a?(Phronomy::Agent::AgentExecution) ||
496
- value.is_a?(Phronomy::Agent::AgentInvocation) ||
497
- value.is_a?(Phronomy::Agent::ToolInvocation) ||
498
- value.is_a?(Phronomy::Agent::JournalProjection) ||
499
- value.is_a?(Phronomy::Agent::ExecutionCoordinator) ||
500
- value.is_a?(Phronomy::Agent::Context::Capability::Base) ||
501
- value.is_a?(Phronomy::Workflow) ||
502
- value.is_a?(Phronomy::WorkflowRunner) ||
503
- value.is_a?(Phronomy::WorkflowContext) ||
504
- value.is_a?(Phronomy::Runtime) ||
505
- value.is_a?(Phronomy::TaskResult) ||
506
- value.is_a?(Phronomy::EventLoop) ||
507
- value.is_a?(Phronomy::FSMSession) ||
508
- value.is_a?(Phronomy::FSMSession::EventSink) ||
509
- value.is_a?(Phronomy::Concurrency::CancellationToken) ||
510
- value.is_a?(Phronomy::Concurrency::OffloadPool)
511
- end
512
- private_class_method :phronomy_managed_live_domain_object?
513
-
514
- def self.safe_behavior_handle(value, name)
515
- return value if value.nil? || value == true || value == false
516
- if phronomy_managed_live_domain_object?(value)
517
- raise Phronomy::ConfigurationError,
518
- "#{name} must not be a Phronomy-managed live domain object (got #{value.class})"
519
- end
520
- value
521
- end
522
- private_class_method :safe_behavior_handle
523
-
524
- # Compatibility helpers for the existing private behavioral specs. These
525
- # evaluate a frozen operation command just like the worker path; they do not
526
- # reintroduce worker-side access to live mutable ToolInvocation state.
527
- def evaluate_authorization
528
- self.class.send(:evaluate_authorization_command, authorization_command)
529
- end
530
-
531
- def evaluate_facts
532
- self.class.send(:evaluate_authorization_facts, authorization_command)
533
- end
534
-
535
- def evaluate_default_decision(request)
536
- self.class.send(
537
- :evaluate_default_authorization_decision,
538
- authorization_command,
539
- request
540
- )
541
- end
542
-
543
- def build_request(facts:, default_decision:)
544
- self.class.send(
545
- :build_authorization_request,
546
- authorization_command,
547
- facts: facts,
548
- default_decision: default_decision
452
+ def start_async_tool_operation(environment)
453
+ tool_config = @tool.class.respond_to?(:__framework_owned_operation?) ?
454
+ @config.merge(phronomy_tool_invocation_id: @id, execution_id: @execution_id).freeze : @config
455
+ Phronomy::Tool::Operation.call_async(
456
+ tool: @tool,
457
+ args: @arguments,
458
+ cancellation_token: @config[:cancellation_token],
459
+ config: tool_config,
460
+ submitter: environment,
461
+ on_full: :raise
549
462
  )
550
463
  end
551
464
 
552
- def start_async_tool_operation(runtime)
553
- if uses_default_call_async?
554
- Phronomy::Agent::ToolExecutor.call_async(
555
- tool: @tool,
556
- args: @arguments,
557
- cancellation_token: @config[:cancellation_token],
558
- config: @config,
559
- runtime: runtime,
560
- on_full: :raise
561
- )
562
- else
563
- tool_config = @tool.class.respond_to?(:__framework_owned_operation?) ?
564
- @config.merge(phronomy_tool_invocation_id: @id, execution_id: @execution_id).freeze : @config
565
- @tool.call_async(
566
- @arguments,
567
- cancellation_token: @config[:cancellation_token],
568
- config: tool_config
569
- )
570
- end
571
- end
572
-
573
- def uses_default_call_async?
574
- @tool.method(:call_async).owner ==
575
- Phronomy::Agent::Context::Capability::Base
576
- end
577
-
578
465
  def self.build_execution_outcome(tool_invocation_id, result, error)
579
466
  if error
580
467
  ExecutionOutcome.new(
@@ -590,7 +477,7 @@ module Phronomy
590
477
 
591
478
  def self.authorization_failure_result(tool_invocation_id, error)
592
479
  if error.is_a?(Phronomy::TimeoutError) ||
593
- error.is_a?(Phronomy::TransportError) ||
480
+ error.is_a?(Phronomy::LLMAdapter::TransportError) ||
594
481
  error.is_a?(Phronomy::BackpressureError)
595
482
  AuthorizationOutcome.new(
596
483
  tool_invocation_id: tool_invocation_id,
@@ -608,16 +495,6 @@ module Phronomy
608
495
  end
609
496
  private_class_method :authorization_failure_result
610
497
 
611
- # Private compatibility helpers used by existing behavioral specs. Runtime
612
- # callbacks use only the pure class helpers above and captured semantic IDs.
613
- def execution_outcome(result, error)
614
- self.class.send(:build_execution_outcome, @id, result, error)
615
- end
616
-
617
- def authorization_failure_outcome(error)
618
- self.class.send(:authorization_failure_result, @id, error)
619
- end
620
-
621
498
  def authoritative_tool_outcome?(outcome)
622
499
  outcome.respond_to?(:tool_invocation_id) &&
623
500
  outcome.tool_invocation_id.to_s == @id
@@ -665,18 +542,7 @@ module Phronomy
665
542
  end
666
543
 
667
544
  def immutable_copy(value)
668
- case value
669
- when Hash
670
- value.each_with_object({}) do |(key, item), result|
671
- result[immutable_copy(key)] = immutable_copy(item)
672
- end.freeze
673
- when Array
674
- value.map { |item| immutable_copy(item) }.freeze
675
- when String
676
- value.dup.freeze
677
- else
678
- value
679
- end
545
+ Phronomy::Values::Immutable.copy(value)
680
546
  end
681
547
 
682
548
  def redact_for_display(value)
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Agent
5
+ # Agent-owned Tool child entry operations and session-local result delivery.
6
+ # Workers return outcomes; the serial owner applies live invocation state.
7
+ # @api private
8
+ class ToolInvocationActions
9
+ def self.build_entry_actions(environment, event_sink, parent_event_sink)
10
+ {
11
+ validating: [method(:validating_action)],
12
+ authorizing: [method(:authorizing_action).curry.call(environment, event_sink)],
13
+ awaiting_approval: [method(:awaiting_approval_action).curry.call(parent_event_sink)],
14
+ authorized: [method(:authorized_action).curry.call(parent_event_sink)],
15
+ queued: [method(:queued_action)],
16
+ running: [method(:running_action).curry.call(environment, event_sink)],
17
+ completed: [method(:completed_action).curry.call(parent_event_sink)],
18
+ failed: [method(:failed_action).curry.call(parent_event_sink)],
19
+ rejected: [method(:rejected_action).curry.call(parent_event_sink)],
20
+ cancelled: [method(:cancelled_action).curry.call(parent_event_sink)]
21
+ }
22
+ end
23
+
24
+ def self.validating_action(invocation)
25
+ invocation.validate!
26
+ end
27
+ private_class_method :validating_action
28
+
29
+ def self.authorizing_action(environment, event_sink, invocation)
30
+ invocation.start_authorization(environment: environment) do |outcome|
31
+ post_to_session(event_sink, :authorization_completed, outcome)
32
+ end
33
+ invocation
34
+ end
35
+ private_class_method :authorizing_action
36
+
37
+ def self.awaiting_approval_action(parent_event_sink, invocation)
38
+ invocation.mark_awaiting_approval!
39
+ notify_parent(parent_event_sink, invocation, :tool_approval_required)
40
+ invocation
41
+ end
42
+ private_class_method :awaiting_approval_action
43
+
44
+ def self.authorized_action(parent_event_sink, invocation)
45
+ invocation.mark_authorized!
46
+ notify_parent(parent_event_sink, invocation, :tool_authorized)
47
+ invocation
48
+ end
49
+ private_class_method :authorized_action
50
+
51
+ def self.queued_action(invocation)
52
+ invocation.mark_queued!
53
+ end
54
+ private_class_method :queued_action
55
+
56
+ def self.running_action(environment, event_sink, invocation)
57
+ invocation.start_execution(environment: environment) do |outcome|
58
+ post_to_session(event_sink, :execution_completed, outcome)
59
+ end
60
+ invocation.mark_running!
61
+ invocation
62
+ end
63
+ private_class_method :running_action
64
+
65
+ def self.completed_action(parent_event_sink, invocation)
66
+ notify_parent(parent_event_sink, invocation, :tool_completed)
67
+ invocation
68
+ end
69
+ private_class_method :completed_action
70
+
71
+ def self.failed_action(parent_event_sink, invocation)
72
+ notify_parent(parent_event_sink, invocation, :tool_failed)
73
+ invocation
74
+ end
75
+ private_class_method :failed_action
76
+
77
+ def self.rejected_action(parent_event_sink, invocation)
78
+ invocation.mark_rejected!
79
+ notify_parent(parent_event_sink, invocation, :tool_rejected)
80
+ invocation
81
+ end
82
+ private_class_method :rejected_action
83
+
84
+ def self.cancelled_action(parent_event_sink, invocation)
85
+ invocation.mark_cancelled!
86
+ notify_parent(parent_event_sink, invocation, :tool_cancelled)
87
+ invocation
88
+ end
89
+ private_class_method :cancelled_action
90
+
91
+ def self.post_to_session(event_sink, event_type, payload)
92
+ return if event_sink.post(event_type, payload)
93
+
94
+ Phronomy::RuntimeSettings.current.logger&.warn(
95
+ "[Phronomy] Dropped #{event_type.inspect} for " \
96
+ "FSMSession #{event_sink.fsm_session_id}"
97
+ )
98
+ end
99
+ private_class_method :post_to_session
100
+
101
+ def self.notify_parent(parent_event_sink, invocation, event_type)
102
+ parent_event_sink.post(event_type, {tool_invocation_id: invocation.id})
103
+ end
104
+ private_class_method :notify_parent
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Agent
5
+ # Agent-owned Tool child progress. Ordered guards and source admission
6
+ # share one definition; no FSM library or Runtime is needed to evaluate it.
7
+ # @api private
8
+ module ToolInvocationTransitions
9
+ ENTRY_POINT = :idle
10
+ RECURSION_LIMIT = 20
11
+
12
+ AUTO_STATE_SET = {idle: true, validating: true, queued: true}.freeze
13
+
14
+ DECLARED_STATES = %i[
15
+ idle validating authorizing awaiting_approval authorized queued running
16
+ completed failed rejected cancelled
17
+ ].freeze
18
+
19
+ WAIT_STATES = %i[awaiting_approval authorized].freeze
20
+
21
+ EXTERNAL_EVENTS = {
22
+ authorization_completed: [
23
+ {from: :authorizing, to: :cancelled, guard: ->(ctx) { ctx.cancelled? }},
24
+ {from: :authorizing, to: :failed, guard: ->(ctx) { ctx.failed? }},
25
+ {from: :authorizing, to: :rejected, guard: ->(ctx) { ctx.rejected? }},
26
+ {from: :authorizing, to: :awaiting_approval, guard: ->(ctx) { ctx.awaiting_approval? }},
27
+ {from: :authorizing, to: :authorized, guard: ->(ctx) { ctx.authorized? }}
28
+ ],
29
+ execution_completed: [
30
+ {from: :running, to: :cancelled, guard: ->(ctx) { ctx.cancelled? }},
31
+ {from: :running, to: :failed, guard: ->(ctx) { ctx.failed? }},
32
+ {from: :running, to: :completed, guard: ->(ctx) { ctx.execution_completed? }}
33
+ ],
34
+ approve: [{from: :awaiting_approval, to: :authorized, guard: nil}],
35
+ reject: [{from: :awaiting_approval, to: :rejected, guard: nil}],
36
+ dispatch: [{from: :authorized, to: :queued, guard: nil}],
37
+ cancel: [
38
+ {from: :awaiting_approval, to: :cancelled, guard: nil},
39
+ {from: :authorized, to: :cancelled, guard: nil},
40
+ {from: :queued, to: :cancelled, guard: nil},
41
+ {from: :running, to: :cancelled, guard: nil}
42
+ ]
43
+ }.transform_values { |definitions| definitions.each(&:freeze).freeze }.freeze
44
+
45
+ AUTOMATIC_EVENTS = {
46
+ state_completed: [
47
+ {from: :idle, to: :validating, guard: nil},
48
+ {from: :validating, to: :failed, guard: ->(ctx) { ctx.failed? }},
49
+ {from: :validating, to: :completed, guard: ->(ctx) { ctx.validation_completed? }},
50
+ {from: :validating, to: :authorizing, guard: ->(ctx) { ctx.validation_passed? }},
51
+ {from: :queued, to: :running, guard: nil}
52
+ ].each(&:freeze).freeze
53
+ }.freeze
54
+
55
+ EVENTS = AUTOMATIC_EVENTS.merge(EXTERNAL_EVENTS).freeze
56
+
57
+ def self.next_phase(phase, event, context)
58
+ definition = EVENTS.fetch(event.to_sym, []).find do |candidate|
59
+ candidate[:from] == phase.to_sym && allowed?(candidate, context)
60
+ end
61
+ definition&.fetch(:to)
62
+ end
63
+
64
+ def self.allowed?(definition, context)
65
+ guard = definition[:guard]
66
+ !guard || !!(context && guard.call(context))
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "error"
4
+
5
+ module Phronomy
6
+ class ConfigurationError < Error; end
7
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ class Error < StandardError; end
5
+ end