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,9 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../execution/concurrency/worker_input_restricted"
4
+
3
5
  require "securerandom"
4
- require_relative "concerns/filterable"
5
- require_relative "concerns/before_llm_input"
6
- require_relative "concerns/error_translation"
6
+ require_relative "filterable"
7
+ require_relative "before_llm_input"
7
8
 
8
9
  module Phronomy
9
10
  module Agent
@@ -31,10 +32,11 @@ module Phronomy
31
32
  # max_iterations 15
32
33
  # end
33
34
  class Base
35
+ include Phronomy::Concurrency::WorkerInputRestricted
36
+
34
37
  include Phronomy::Runnable
35
- include Concerns::Filterable
36
- include Concerns::BeforeLLMInput
37
- include Concerns::ErrorTranslation
38
+ include Filterable
39
+ include BeforeLLMInput
38
40
 
39
41
  APPROVAL_CONFIGURATION_INIT_MUTEX = Mutex.new
40
42
  private_constant :APPROVAL_CONFIGURATION_INIT_MUTEX
@@ -44,7 +46,7 @@ module Phronomy
44
46
  if name
45
47
  @model = name
46
48
  else
47
- @model || Phronomy.configuration.default_model
49
+ @model || Phronomy::Agent::Settings.current.default_model
48
50
  end
49
51
  end
50
52
 
@@ -119,30 +121,24 @@ module Phronomy
119
121
  if val.nil?
120
122
  @max_output_tokens
121
123
  else
122
- @max_output_tokens = val.to_i
123
- end
124
- end
125
-
126
- def context_window(val = nil)
127
- if val.nil?
128
- @context_window
129
- else
130
- @context_window = val.to_i
124
+ value = Integer(val)
125
+ raise ArgumentError, "max_output_tokens must be positive" unless value.positive?
126
+ @max_output_tokens = value
131
127
  end
132
128
  end
133
129
 
134
- # Binds one Application-constructed ContextPolicy instance to this
130
+ # Binds one Application-constructed Phronomy::Context::ContextPolicy instance to this
135
131
  # Agent class. Policy binding is Application code/runtime wiring rather
136
132
  # than durable Agent state.
137
133
  def context_policy(*args)
138
134
  if args.empty?
139
135
  return @context_policy if instance_variable_defined?(:@context_policy)
140
136
  return superclass.context_policy if superclass.respond_to?(:context_policy)
141
- return ContextPolicies::Default.instance
137
+ raise Phronomy::ConfigurationError, "Agent ContextPolicy has not been configured"
142
138
  end
143
- unless args.length == 1 && args.first.is_a?(ContextPolicy)
139
+ unless args.length == 1 && args.first.is_a?(Phronomy::Context::ContextPolicy)
144
140
  raise ArgumentError,
145
- "context_policy expects one Phronomy::Agent::ContextPolicy instance"
141
+ "context_policy expects one Phronomy::Context::ContextPolicy instance"
146
142
  end
147
143
 
148
144
  @context_policy = args.first
@@ -173,6 +169,7 @@ module Phronomy
173
169
  agent_id: SecureRandom.uuid,
174
170
  context: nil,
175
171
  knowledge: [],
172
+ retention: nil,
176
173
  persistence: nil,
177
174
  metadata: {},
178
175
  on_event: nil,
@@ -182,6 +179,7 @@ module Phronomy
182
179
  agent_id: agent_id,
183
180
  context: context,
184
181
  knowledge: knowledge,
182
+ retention: retention,
185
183
  persistence: persistence,
186
184
  metadata: metadata,
187
185
  on_event: on_event,
@@ -191,7 +189,7 @@ module Phronomy
191
189
 
192
190
  # Resolves one existing logical Agent. A live process-local owner wins
193
191
  # without a Persistence reload; otherwise the durable Agent is hydrated.
194
- def load(agent_id, persistence:, on_event: nil, &event_block)
192
+ def load(agent_id, persistence:, on_event: nil, execution_wiring: {}, &event_block)
195
193
  raise ArgumentError, "persistence is required" unless persistence
196
194
  if on_event && event_block
197
195
  raise ArgumentError, "Provide either on_event: or a block, not both"
@@ -201,10 +199,9 @@ module Phronomy
201
199
  raise ArgumentError, "agent_id must not be empty" if key.empty?
202
200
 
203
201
  listener_supplied = !on_event.nil? || !event_block.nil?
204
- runtime = Phronomy::Runtime.instance
205
202
  materialized = false
206
203
 
207
- agent = runtime.__load_agent(key, expected_class: self) do |owner_runtime|
204
+ agent = OwnershipRegistry.current.load(key, expected_class: self) do |owner_runtime|
208
205
  materialized = true
209
206
  instance = __construct_owned_agent(
210
207
  owner_runtime,
@@ -212,6 +209,7 @@ module Phronomy
212
209
  agent_id: key,
213
210
  persistence: persistence,
214
211
  load_existing: true,
212
+ execution_wiring: execution_wiring,
215
213
  on_event: on_event,
216
214
  &event_block
217
215
  )
@@ -235,14 +233,17 @@ module Phronomy
235
233
  # Returns only the process-local live Agent owner. Does not access
236
234
  # Persistence and returns nil when this Runtime has no live owner.
237
235
  def get(agent_id)
238
- Phronomy::Runtime.instance.__get_agent(agent_id, expected_class: self)
236
+ key = agent_id.to_s
237
+ raise ArgumentError, "agent_id must not be empty" if key.empty?
238
+
239
+ OwnershipRegistry.existing_current&.get(key, expected_class: self)
239
240
  end
240
241
 
241
242
  # Resolves the live Agent instance that currently owns execution_id in
242
243
  # this process. The Runtime returns only a read-only ownership view;
243
244
  # mutable Agent execution state remains EventLoop-owned.
244
245
  def live_for_execution(execution_id)
245
- owner = Phronomy::Runtime.instance.__agent_execution_owner(execution_id)
246
+ owner = Phronomy::Agent::ExecutionRegistry.existing_current&.agent_execution_owner(execution_id)
246
247
  unless owner
247
248
  raise Phronomy::ExecutionRehydrationRequiredError,
248
249
  "no live execution owner for #{execution_id}; durable rehydration is required"
@@ -267,9 +268,15 @@ module Phronomy
267
268
  end
268
269
  end
269
270
 
271
+ # Bound at Agent construction; no execution may silently switch Runtime.
270
272
  # @api private
271
- def __coordination_config
272
- @_phronomy_coordination_config || {}.freeze
273
+ def __execution_environment
274
+ @_phronomy_runtime_owner || ExecutionEnvironment.current
275
+ end
276
+
277
+ # @api private
278
+ def __execution_wiring
279
+ @_phronomy_execution_wiring || {}.freeze
273
280
  end
274
281
 
275
282
  # Framework-owned idempotent Tool operations opt into exact replay.
@@ -278,26 +285,29 @@ module Phronomy
278
285
 
279
286
  # @api private
280
287
  def __framework_call?(name)
281
- __framework_tool_replayable?(name) || Array(__coordination_config[:phronomy_handoff_bindings]).any? { |binding| binding.tool_name == name }
288
+ __framework_tool_replayable?(name) || Array(__execution_wiring[:phronomy_control_bindings]).any? { |binding| binding.tool_name == name }
282
289
  end
283
290
 
284
- # Captured Tool wiring is supplied explicitly; this hook only writes semantic values.
285
- # @api private
286
- def __prepare_coordination_record(execution, tx:) = execution
287
-
288
291
  # Runtime configuration only; no value returned here is serialized.
289
292
  # @api private
290
293
  def __invocation_config(config) = config
291
294
 
295
+ def knowledge_snapshot = persistence.knowledge_snapshot(agent_id: agent_id)
296
+
297
+ # Listener inheritance is an explicit orchestration choice, not a private getter.
298
+ def event_listener = _phronomy_event_listener
299
+
292
300
  attr_reader :agent_id, :persistence
293
301
 
294
302
  def initialize(
295
303
  agent_id: nil,
296
304
  context: nil,
297
305
  knowledge: [],
306
+ retention: nil,
298
307
  persistence: nil,
299
308
  metadata: {},
300
309
  load_existing: false,
310
+ execution_wiring: {},
301
311
  on_event: nil,
302
312
  &event_block
303
313
  )
@@ -305,6 +315,11 @@ module Phronomy
305
315
  raise ArgumentError, "Provide either on_event: or a block, not both"
306
316
  end
307
317
  @_phronomy_event_listener = on_event || event_block
318
+ @_phronomy_execution_wiring = execution_wiring.dup.freeze
319
+ @initial_retention = retention
320
+ if retention && retention.agent_id != agent_id.to_s
321
+ raise ArgumentError, "retention must refer to the new Agent"
322
+ end
308
323
 
309
324
  reserved_agent_id = @_phronomy_reserved_agent_id
310
325
  effective_agent_id = if agent_id.nil?
@@ -336,9 +351,8 @@ module Phronomy
336
351
  "load_existing: is an internal hydration option; use .load(agent_id, persistence:)"
337
352
  end
338
353
 
339
- runtime = Phronomy::Runtime.instance
340
354
  begin
341
- runtime.__create_agent(effective_agent_id, expected_class: self.class) do |owner_runtime|
355
+ OwnershipRegistry.current.create(effective_agent_id, expected_class: self.class) do |owner_runtime|
342
356
  __prepare_runtime_owner!(owner_runtime, effective_agent_id)
343
357
  initialize_owned_state(
344
358
  agent_id: effective_agent_id,
@@ -395,34 +409,8 @@ module Phronomy
395
409
  def add_knowledge(content, metadata: {})
396
410
  __assert_live_agent!
397
411
  current = agent_root
398
- next_root = nil
399
- appended = nil
400
- persistence.transaction do |tx|
401
- tx.executions.assert_idle!(agent_id)
402
- record = build_knowledge_record(
403
- tx: tx,
404
- root: current,
405
- content: content,
406
- metadata: metadata
407
- )
408
- appended = tx.journals.append(
409
- agent_id,
410
- expected_position: current.journal_position,
411
- records: [record]
412
- )
413
- next_root = current.with(
414
- agent_revision: current.agent_revision + 1,
415
- context_revision: current.context_revision + 1,
416
- journal_position: current.journal_position + appended.length
417
- )
418
- tx.agents.save(
419
- agent_id,
420
- expected_revision: current.agent_revision,
421
- root: next_root
422
- )
423
- end
424
- _append_journal_records(appended)
425
- @root = next_root
412
+ update = state_writer.add_knowledge(root: current, content: content, metadata: metadata)
413
+ apply_state_update(update)
426
414
  self
427
415
  end
428
416
 
@@ -450,9 +438,10 @@ module Phronomy
450
438
 
451
439
  __assert_live_agent!
452
440
  runtime = @_phronomy_runtime_owner
453
- token = runtime.__begin_agent_purge(self)
454
- if runtime.__agent_execution_admitted?(agent_id)
455
- runtime.__abort_agent_purge(self, token)
441
+ registry = OwnershipRegistry.for(runtime)
442
+ token = registry.begin_purge(self)
443
+ if runtime.existing_registry&.agent_execution_admitted?(agent_id)
444
+ registry.abort_purge(self, token)
456
445
  raise Phronomy::AgentBusyError,
457
446
  "Agent #{agent_id.inspect} has a nonterminal top-level execution"
458
447
  end
@@ -460,12 +449,11 @@ module Phronomy
460
449
  begin
461
450
  persistence.transaction do |tx|
462
451
  tx.executions.assert_idle!(agent_id)
463
- if (routing = tx.handoff_states.load(agent_id))
464
- unless routing.phase == "stable" && tx.executions.list_active(routing.active_agent_id).empty?
465
- raise Phronomy::AgentBusyError, "Handoff anchor #{agent_id} owns an unfinished turn"
466
- end
467
- tx.handoff_states.delete(agent_id, expected_revision: routing.handoff_revision)
452
+ tx.guard_agent!(agent_id)
453
+ unless tx.retentions.list(agent_id).empty?
454
+ raise Phronomy::AgentBusyError, "Agent #{agent_id} is retained by durable coordination history"
468
455
  end
456
+ tx.cancellations.delete_for_agent(agent_id)
469
457
  tx.journals.delete(agent_id)
470
458
  tx.executions.delete_for_agent(agent_id)
471
459
  tx.agents.delete(agent_id)
@@ -475,16 +463,16 @@ module Phronomy
475
463
  Phronomy::Persistence::NotFoundError,
476
464
  Phronomy::Persistence::SerializationError,
477
465
  ArgumentError
478
- runtime.__abort_agent_purge(self, token)
466
+ registry.abort_purge(self, token)
479
467
  raise
480
468
  rescue
481
469
  # Without F1 reconciliation support we cannot infer that a failed
482
470
  # durable delete did not commit. Keep the identity fail-closed.
483
- runtime.__leave_agent_purge_uncertain(self, token)
471
+ registry.leave_purge_uncertain(self, token)
484
472
  raise
485
473
  end
486
474
 
487
- runtime.__complete_agent_purge(self, token)
475
+ registry.complete_purge(self, token)
488
476
  true
489
477
  end
490
478
 
@@ -584,8 +572,8 @@ module Phronomy
584
572
  load_existing:
585
573
  )
586
574
  @persistence = persistence ||
587
- Phronomy.configuration.persistence ||
588
- Phronomy::Persistence::InMemory.new
575
+ Phronomy::Agent::Settings.current.agent_store ||
576
+ DefaultPersistence.build
589
577
  @agent_id = agent_id.to_s.freeze
590
578
 
591
579
  if load_existing
@@ -630,117 +618,27 @@ module Phronomy
630
618
 
631
619
  def create_agent_root!(context:, knowledge:, metadata:)
632
620
  definition = self.class.agent_definition
633
- root = Agent::AgentRoot.create(
634
- agent_id: agent_id,
635
- agent_definition_id: definition.fetch(:id),
636
- agent_definition_version: definition.fetch(:version),
637
- metadata: metadata
638
- )
639
- persistence.transaction do |tx|
640
- tx.agents.create(root)
641
- records = initial_context_records(tx: tx, root: root, context: context)
642
- records.concat(initial_knowledge_records(tx: tx, root: root, knowledge: knowledge))
643
- unless records.empty?
644
- appended = tx.journals.append(agent_id, expected_position: 0, records: records)
645
- root = root.with(
646
- agent_revision: 1,
647
- context_revision: 1,
648
- journal_position: appended.length
649
- )
650
- tx.agents.save(agent_id, expected_revision: 0, root: root)
651
- end
652
- end
653
- root
654
- end
655
-
656
- def initial_context_records(tx:, root:, context:)
657
- return [] unless context
658
-
659
- imported = context.respond_to?(:records) ? context :
660
- Agent::ContextImporter.import_messages(context)
661
- imported.records.map do |record|
662
- content_ref = case record.content_format
663
- when :text then tx.contents.put_text(record.content)
664
- when :json then tx.contents.put_json(record.content)
665
- else
666
- raise ArgumentError,
667
- "unsupported imported content format: #{record.content_format.inspect}"
668
- end
669
- Agent::JournalRecord.new(
670
- agent_id: agent_id,
671
- kind: record.kind,
672
- channel: record.channel,
673
- role: record.role,
674
- content_ref: content_ref,
675
- context_generation: root.transcript_generation,
676
- context_candidate: true,
677
- metadata: record.metadata
678
- )
679
- end
680
- end
681
-
682
- def initial_knowledge_records(tx:, root:, knowledge:)
683
- Array(knowledge).map do |content|
684
- build_knowledge_record(
685
- tx: tx,
686
- root: root,
687
- content: content,
688
- metadata: {}
689
- )
690
- end
691
- end
692
-
693
- def build_knowledge_record(tx:, root:, content:, metadata:)
694
- Agent::JournalRecord.new(
695
- agent_id: agent_id,
696
- kind: :knowledge,
697
- channel: :context,
698
- role: :user,
699
- content_ref: tx.contents.put_text(String(content)),
700
- context_generation: root.transcript_generation,
701
- context_candidate: true,
702
- metadata: metadata || {}
621
+ state_writer.create_root(
622
+ definition: definition, context: context, knowledge: knowledge, metadata: metadata, retention: @initial_retention
703
623
  )
704
624
  end
705
625
 
706
626
  def mutate_context!(kind, context_affecting: true)
707
627
  __assert_live_agent!
708
628
  current = agent_root
709
- next_root = nil
710
- appended = nil
711
- persistence.transaction do |tx|
712
- tx.executions.assert_idle!(agent_id)
713
- record = Agent::JournalRecord.new(
714
- agent_id: agent_id,
715
- kind: kind,
716
- channel: :state,
717
- context_generation: current.transcript_generation,
718
- context_candidate: false
719
- )
720
- appended = tx.journals.append(
721
- agent_id,
722
- expected_position: current.journal_position,
723
- records: [record]
724
- )
725
- proposed = yield(current)
726
- next_root = proposed.with(
727
- journal_position: current.journal_position + appended.length,
728
- context_revision: context_affecting ?
729
- yield_context_revision(current, proposed) : current.context_revision
730
- )
731
- tx.agents.save(
732
- agent_id,
733
- expected_revision: current.agent_revision,
734
- root: next_root
735
- )
736
- end
737
- _append_journal_records(appended)
738
- @root = next_root
629
+ update = state_writer.mutate_context(
630
+ root: current, kind: kind, context_affecting: context_affecting
631
+ ) { |root| yield(root) }
632
+ apply_state_update(update)
633
+ end
634
+
635
+ def state_writer
636
+ StateWriter.new(persistence: persistence, agent_id: agent_id)
739
637
  end
740
638
 
741
- def yield_context_revision(current, proposed)
742
- (proposed.context_revision == current.context_revision) ?
743
- current.context_revision + 1 : proposed.context_revision
639
+ def apply_state_update(update)
640
+ _append_journal_records(update.records)
641
+ @root = update.root
744
642
  end
745
643
 
746
644
  def _journal_records_snapshot
@@ -789,7 +687,8 @@ module Phronomy
789
687
  return true if @_phronomy_runtime_owner_state == :constructing
790
688
 
791
689
  runtime = @_phronomy_runtime_owner
792
- unless runtime&.__agent_owned?(self)
690
+ registry = OwnershipRegistry.existing_for(runtime) if runtime
691
+ unless registry&.owned?(self)
793
692
  raise Phronomy::RuntimeShutdownError,
794
693
  "Agent #{agent_id.inspect} is no longer the live owner in its Runtime"
795
694
  end
@@ -821,7 +720,7 @@ module Phronomy
821
720
  end
822
721
 
823
722
  def _check_event_loop_reentrancy(sync_method, async_method)
824
- if Phronomy::Runtime.instance.event_loop.current?
723
+ if Phronomy::WaitPolicy.blocking_forbidden?
825
724
  raise Phronomy::EventLoopReentrancyError,
826
725
  "#{self.class.name}##{sync_method} cannot run on the EventLoop thread. " \
827
726
  "Use #{async_method} and return immediately."
@@ -836,12 +735,6 @@ module Phronomy
836
735
  task.fail(error)
837
736
  end
838
737
 
839
- def _translated_error(error)
840
- translate_and_reraise!(error)
841
- rescue => translated
842
- translated
843
- end
844
-
845
738
  def _deliver_stream_event(listener, event)
846
739
  return unless listener
847
740
 
@@ -885,7 +778,7 @@ module Phronomy
885
778
  end
886
779
 
887
780
  def _warn_stream_callback_error(message)
888
- logger = Phronomy.configuration.logger
781
+ logger = Phronomy::RuntimeSettings.current.logger
889
782
  unless logger
890
783
  _kernel_warn_safely(message)
891
784
  return
@@ -933,52 +826,28 @@ module Phronomy
933
826
  meta
934
827
  end
935
828
 
936
- def _apply_runtime_projection_to_chat(chat, projection, invocation: nil)
937
- if projection.system
938
- apply_instructions(
939
- chat,
940
- projection.system,
941
- cache: projection.model_config["cache_instructions"],
942
- provider: projection.model_config["provider"]
943
- )
944
- end
945
- projection.tool_classes.each do |tool_class|
946
- chat.with_tool(prepare_tool_class(tool_class, invocation: invocation))
829
+ def prepare_runtime_input(projection, invocation:)
830
+ tools = projection.tool_classes.map do |tool|
831
+ prepared = prepare_tool_class(tool, invocation: invocation)
832
+ prepared.is_a?(Class) ? prepared.new : prepared
947
833
  end
948
- projection.messages.each { |message| chat.messages << message }
949
- chat
834
+ invocation.tools = tools.to_h { |tool| [tool.name.to_sym, tool] }.freeze
835
+ invocation.messages = projection.messages.dup
836
+ invocation
950
837
  end
951
838
 
952
- def build_chat(model_config: nil)
953
- config = model_config || {
954
- "model" => self.class.model,
955
- "provider" => self.class.provider,
956
- "temperature" => self.class.temperature,
957
- "max_output_tokens" => self.class.max_output_tokens,
958
- "parallel_tool_execution" => Phronomy.configuration.parallel_tool_execution
959
- }
960
- opts = {}
961
- model = config["model"]
962
- opts[:model] = model if model
963
- provider = config["provider"]
964
- if provider
965
- opts[:provider] = provider.to_sym
966
- opts[:assume_model_exists] = true
967
- end
968
- parallel_class = config["parallel_tool_execution"] ?
969
- Phronomy::MultiAgent::ParallelToolChat : nil
970
- chat = parallel_class ? parallel_class.new(**opts) : RubyLLM.chat(**opts)
971
- chat.with_temperature(config["temperature"]) if config["temperature"]
972
- if config["max_output_tokens"] && chat.respond_to?(:with_max_output_tokens)
973
- chat.with_max_output_tokens(config["max_output_tokens"])
974
- end
975
- chat
839
+ def build_llm_request(projection, invocation:)
840
+ definitions = Phronomy::Tool::DefinitionSet.build(tools: invocation.tools.values).definitions
841
+ Phronomy::LLMAdapter::Request.new(
842
+ model_config: projection.model_config, system: projection.system,
843
+ messages: invocation.messages, tools: definitions, message: projection.ask_message
844
+ )
976
845
  end
977
846
 
978
847
  def build_instructions(input)
979
848
  instr = self.class.instructions
980
849
  case instr
981
- when Phronomy::Agent::Context::Instruction::PromptTemplate
850
+ when Phronomy::Context::PromptTemplate
982
851
  vars = input.is_a?(Hash) ? input : {input: input}
983
852
  instr.format_system(**vars) || instr.format(**vars)
984
853
  when String then instr
@@ -987,15 +856,6 @@ module Phronomy
987
856
  end
988
857
  end
989
858
 
990
- def apply_instructions(chat, text, cache: false, provider: nil)
991
- if cache && provider.to_s == "anthropic"
992
- content = RubyLLM::Providers::Anthropic::Content.new(text, cache: true)
993
- chat.with_instructions(content)
994
- else
995
- chat.with_instructions(text)
996
- end
997
- end
998
-
999
859
  def extract_message(input)
1000
860
  case input
1001
861
  when String then input
@@ -1018,69 +878,21 @@ module Phronomy
1018
878
  raise Phronomy::CancellationError, message
1019
879
  end
1020
880
 
881
+ def tool_definition_set(additional_tools: [])
882
+ runtime_tools = (self.class.tools + Array(additional_tools)).freeze
883
+ Phronomy::Tool::DefinitionSet.build(
884
+ tools: runtime_tools.map { |tool| prepare_tool_class(tool) },
885
+ runtime_tools: runtime_tools
886
+ )
887
+ end
888
+
1021
889
  def prepare_tool_class(tool_class, invocation: nil)
1022
890
  return tool_class unless tool_class.is_a?(Class)
1023
891
 
1024
- resolved = if (alias_name = self.class.tool_aliases[tool_class])
1025
- Class.new(tool_class) do
1026
- tool_name alias_name
1027
- end
1028
- else
1029
- tool_class
1030
- end
1031
-
1032
- result_filters = _tool_result_filters_for(tool_class)
1033
- return resolved if result_filters.empty?
1034
-
1035
- effective_name = resolved.new.name
1036
- custom_async_call =
1037
- resolved.instance_method(:call_async).owner !=
1038
- Phronomy::Agent::Context::Capability::Base
1039
-
1040
- Class.new(resolved) do
1041
- tool_name effective_name
1042
- define_method(:call) do |args, **kwargs|
1043
- result = super(args, **kwargs)
1044
- result_filters.inject(result) { |val, filter|
1045
- filter.call(val, tool_name: name, args: args)
1046
- }
1047
- end
1048
-
1049
- if custom_async_call
1050
- define_method(:call_async) do |args, **kwargs|
1051
- source = super(args, **kwargs)
1052
- filtered = Phronomy::Concurrency::PhysicalCompletionTask.deferred(
1053
- name: "tool-filter-#{name}"
1054
- )
1055
- source_has_physical_signal = source.respond_to?(:on_physical_complete)
1056
- source.on_physical_complete { filtered.mark_physical_complete! } if
1057
- source_has_physical_signal
1058
- source.on_complete do |value, error|
1059
- if error
1060
- filtered.mark_physical_complete! unless source_has_physical_signal
1061
- if source.respond_to?(:status) && source.status == :cancelled
1062
- filtered.cancel!(error)
1063
- else
1064
- filtered.fail(error)
1065
- end
1066
- next
1067
- end
1068
-
1069
- begin
1070
- result = result_filters.inject(value) { |val, filter|
1071
- filter.call(val, tool_name: name, args: args)
1072
- }
1073
- filtered.mark_physical_complete! unless source_has_physical_signal
1074
- filtered.complete(result)
1075
- rescue => filter_error
1076
- filtered.mark_physical_complete! unless source_has_physical_signal
1077
- filtered.fail(filter_error)
1078
- end
1079
- end
1080
- filtered
1081
- end
1082
- end
1083
- end
892
+ binding = ToolBinding.new(
893
+ tool_class, alias_name: self.class.tool_aliases[tool_class]
894
+ )
895
+ binding.prepare(result_filters: _tool_result_filters_for(tool_class))
1084
896
  end
1085
897
  end
1086
898
  end