phronomy 0.27.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 (475) hide show
  1. checksums.yaml +4 -4
  2. data/.mutant.yml +9 -9
  3. data/CHANGELOG.md +52 -0
  4. data/CONTRIBUTING.md +8 -8
  5. data/README.md +9 -3
  6. data/benchmark/bench_agent_invoke.rb +25 -50
  7. data/benchmark/bench_context_assembler.rb +14 -11
  8. data/benchmark/bench_regression.rb +1 -1
  9. data/benchmark/bench_tool_schema.rb +1 -1
  10. data/docs/architecture/agent-chat-and-state-ownership.md +7 -1
  11. data/docs/architecture/agent-transition-ownership.md +13 -89
  12. data/docs/architecture/backend-groups.md +152 -0
  13. data/docs/architecture/before-llm-input.md +3 -3
  14. data/docs/architecture/context-management.md +1 -1
  15. data/docs/architecture/execution-abstraction-review.md +88 -0
  16. data/docs/architecture/execution-boundaries.md +28 -0
  17. data/docs/architecture/execution-metadata-and-values.md +6 -0
  18. data/docs/architecture/multi-agent-handoff.md +6 -5
  19. data/docs/architecture/persistence.md +27 -9
  20. data/docs/architecture/r8-unit1.md +104 -0
  21. data/docs/architecture/r8-unit10.md +72 -0
  22. data/docs/architecture/r8-unit11.md +71 -0
  23. data/docs/architecture/r8-unit12.md +67 -0
  24. data/docs/architecture/r8-unit13.md +61 -0
  25. data/docs/architecture/r8-unit14.md +72 -0
  26. data/docs/architecture/r8-unit15.md +65 -0
  27. data/docs/architecture/r8-unit16.md +54 -0
  28. data/docs/architecture/r8-unit17.md +73 -0
  29. data/docs/architecture/r8-unit18.md +92 -0
  30. data/docs/architecture/r8-unit19.md +61 -0
  31. data/docs/architecture/r8-unit2.md +55 -0
  32. data/docs/architecture/r8-unit20.md +51 -0
  33. data/docs/architecture/r8-unit21.md +79 -0
  34. data/docs/architecture/r8-unit22.md +74 -0
  35. data/docs/architecture/r8-unit23.md +81 -0
  36. data/docs/architecture/r8-unit3.md +130 -0
  37. data/docs/architecture/r8-unit4.md +79 -0
  38. data/docs/architecture/r8-unit5.md +115 -0
  39. data/docs/architecture/r8-unit6.md +71 -0
  40. data/docs/architecture/r8-unit7.md +70 -0
  41. data/docs/architecture/r8-unit8.md +78 -0
  42. data/docs/architecture/r8-unit9.md +73 -0
  43. data/docs/architecture/refactoring-closure.md +10 -1
  44. data/docs/architecture/remaining-refactoring-plan.md +16 -0
  45. data/docs/architecture/storage-execution-boundaries.md +54 -0
  46. data/docs/architecture/tool-schema-recording-gap.md +6 -0
  47. data/docs/architecture/tracing.md +1 -1
  48. data/docs/architecture/workflow-terminal-ownership-design.md +2 -2
  49. data/docs/architecture.md +24 -11
  50. data/docs/decisions/015-tool-public-facade-and-rbs-boundary.md +3 -3
  51. data/docs/decisions/027-llm-adapter-provider-boundary.md +2 -0
  52. data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +2 -0
  53. data/docs/decisions/031-durable-multi-agent-coordination.md +2 -0
  54. data/docs/decisions/032-storage-backend-composition.md +5 -0
  55. data/docs/decisions/034-handoff-runner-coordination-ownership.md +2 -0
  56. data/docs/decisions/035-tool-executor-capability-ownership.md +1 -1
  57. data/docs/decisions/038-responsibility-based-source-layout.md +14 -0
  58. data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +8 -0
  59. data/docs/decisions/040-configuration-default-composition.md +12 -1
  60. data/docs/decisions/045-worker-input-restriction-ownership.md +4 -0
  61. data/docs/decisions/051-execution-outcome-worker-ownership.md +2 -0
  62. data/docs/decisions/056-workflow-terminal-policy-ownership.md +1 -1
  63. data/docs/decisions/058-neutral-storage-primitives.md +5 -0
  64. data/docs/decisions/059-backend-contracts-and-async-clients.md +74 -0
  65. data/docs/decisions/060-runtime-settings-and-application-configuration.md +86 -0
  66. data/docs/decisions/061-persistence-failure-contracts.md +89 -0
  67. data/docs/decisions/062-content-store-service.md +52 -0
  68. data/docs/decisions/063-execution-contracts-and-services.md +88 -0
  69. data/docs/decisions/064-agent-execution-and-multi-agent-responsibility.md +36 -0
  70. data/docs/decisions/065-vector-and-embeddings-common-operations.md +38 -0
  71. data/docs/decisions/066-llm-and-tool-operation-contracts.md +20 -0
  72. data/docs/decisions/067-agent-progress-and-engine-connection.md +29 -0
  73. data/docs/decisions/068-agent-tool-child-progress-and-tool-operation.md +36 -0
  74. data/docs/decisions/README.md +18 -1
  75. data/docs/execution-boundaries-preview.md +45 -0
  76. data/docs/features.md +8 -3
  77. data/docs/getting-started.md +9 -6
  78. data/docs/migrations/0.22.md +2 -2
  79. data/docs/migrations/durable-semantic-coordination-v2.md +2 -0
  80. data/docs/migrations/handoff-runner-multi-agent.md +2 -0
  81. data/docs/migrations/persistence-failure-contracts.md +67 -0
  82. data/docs/migrations/r8-unit10.md +36 -0
  83. data/docs/migrations/r8-unit11.md +33 -0
  84. data/docs/migrations/r8-unit12.md +31 -0
  85. data/docs/migrations/r8-unit13.md +36 -0
  86. data/docs/migrations/r8-unit14.md +31 -0
  87. data/docs/migrations/r8-unit15.md +31 -0
  88. data/docs/migrations/r8-unit16.md +26 -0
  89. data/docs/migrations/r8-unit17.md +28 -0
  90. data/docs/migrations/r8-unit18.md +29 -0
  91. data/docs/migrations/r8-unit19.md +28 -0
  92. data/docs/migrations/r8-unit20.md +24 -0
  93. data/docs/migrations/r8-unit21.md +28 -0
  94. data/docs/migrations/r8-unit22.md +29 -0
  95. data/docs/migrations/r8-unit23.md +20 -0
  96. data/docs/migrations/r8-unit4.md +67 -0
  97. data/docs/migrations/r8-unit5.md +75 -0
  98. data/docs/migrations/r8-unit6.md +109 -0
  99. data/docs/migrations/r8-unit7.md +31 -0
  100. data/docs/migrations/r8-unit8.md +33 -0
  101. data/docs/migrations/r8-unit9.md +28 -0
  102. data/docs/migrations/storage-async-client.md +103 -0
  103. data/docs/migrations/vector-async-clients.md +110 -0
  104. data/docs/persistence-backends.md +38 -21
  105. data/docs/runtime-and-concurrency.md +26 -1
  106. data/lib/phronomy/agent/admission.rb +78 -0
  107. data/lib/phronomy/agent/{lifecycle_contract/agent_already_exists_error.rb → agent_already_exists_error.rb} +1 -1
  108. data/lib/phronomy/agent/{lifecycle_contract/agent_busy_error.rb → agent_busy_error.rb} +1 -1
  109. data/lib/phronomy/agent/{lifecycle_contract/agent_purged_error.rb → agent_purged_error.rb} +1 -1
  110. data/lib/phronomy/{recovery → agent/api}/execution_rehydration_required_error.rb +1 -1
  111. data/lib/phronomy/agent/async_event_api.rb +37 -32
  112. data/lib/phronomy/agent/base.rb +74 -72
  113. data/lib/phronomy/agent/before_llm_input.rb +64 -0
  114. data/lib/phronomy/agent/composition/run_once.rb +1 -1
  115. data/lib/phronomy/agent/{context_assembly/context_candidate_resolver.rb → context_candidate_resolver.rb} +2 -2
  116. data/lib/phronomy/agent/{context_assembly/context_assembler.rb → context_preparation.rb} +35 -221
  117. data/lib/phronomy/agent/control_request.rb +21 -0
  118. data/lib/phronomy/agent/execution/agent_execution.rb +1 -1
  119. data/lib/phronomy/agent/execution/agent_invocation.rb +27 -53
  120. data/lib/phronomy/agent/execution/approval_resume_commit.rb +1 -1
  121. data/lib/phronomy/agent/execution/dispatch_preparation.rb +17 -127
  122. data/lib/phronomy/agent/execution/exact_execution.rb +44 -23
  123. data/lib/phronomy/agent/execution/execution_cancellation.rb +8 -4
  124. data/lib/phronomy/agent/execution/execution_coordinator.rb +214 -395
  125. data/lib/phronomy/agent/execution/execution_metadata.rb +1 -0
  126. data/lib/phronomy/agent/execution/execution_outcome_committer.rb +70 -126
  127. data/lib/phronomy/agent/execution/execution_registry.rb +6 -2
  128. data/lib/phronomy/agent/execution/execution_session_runner.rb +22 -27
  129. data/lib/phronomy/agent/execution/initial_preparation.rb +18 -116
  130. data/lib/phronomy/agent/execution/{agent_invocation_session_builder.rb → invocation_actions.rb} +54 -194
  131. data/lib/phronomy/agent/execution/invocation_transitions.rb +39 -1
  132. data/lib/phronomy/agent/execution/runtime_record_encoder.rb +7 -7
  133. data/lib/phronomy/agent/execution_change.rb +161 -0
  134. data/lib/phronomy/agent/execution_environment.rb +36 -0
  135. data/lib/phronomy/agent/execution_extension_state.rb +21 -0
  136. data/lib/phronomy/agent/execution_observation.rb +35 -0
  137. data/lib/phronomy/agent/filterable.rb +182 -0
  138. data/lib/phronomy/agent/handoff/{handoff_context.rb → transfer_context.rb} +4 -4
  139. data/lib/phronomy/agent/handoff/{handoff_projection.rb → transfer_projection.rb} +14 -33
  140. data/lib/phronomy/{llm_contract/transport_error.rb → agent/handoff_error.rb} +1 -1
  141. data/lib/phronomy/agent/journal/journal_projection.rb +1 -1
  142. data/lib/phronomy/agent/knowledge_item.rb +13 -0
  143. data/lib/phronomy/agent/lifecycle/agent_root.rb +1 -1
  144. data/lib/phronomy/agent/lifecycle/ownership_registry.rb +18 -23
  145. data/lib/phronomy/agent/persistence/agent_repository.rb +29 -21
  146. data/lib/phronomy/agent/persistence/cancellation_repository.rb +51 -0
  147. data/lib/phronomy/agent/persistence/codec.rb +1 -21
  148. data/lib/phronomy/agent/persistence/execution_repository.rb +57 -41
  149. data/lib/phronomy/agent/persistence/journal_repository.rb +27 -18
  150. data/lib/phronomy/agent/persistence/records.rb +33 -0
  151. data/lib/phronomy/agent/persistence/retention_repository.rb +70 -0
  152. data/lib/phronomy/agent/persistence/storage_schema.rb +7 -2
  153. data/lib/phronomy/agent/persistence/watermark.rb +13 -11
  154. data/lib/phronomy/agent/recovery/invocation_restorer.rb +13 -21
  155. data/lib/phronomy/agent/recovery/recovery_coordinator/continuation.rb +10 -9
  156. data/lib/phronomy/agent/recovery/recovery_coordinator/installation.rb +27 -27
  157. data/lib/phronomy/agent/recovery/recovery_coordinator/resolution.rb +19 -21
  158. data/lib/phronomy/agent/recovery/recovery_coordinator.rb +23 -16
  159. data/lib/phronomy/agent/recovery/recovery_rules.rb +119 -0
  160. data/lib/phronomy/agent/recovery/recovery_support.rb +8 -11
  161. data/lib/phronomy/agent/reserved_execution.rb +16 -0
  162. data/lib/phronomy/agent/retention.rb +17 -0
  163. data/lib/phronomy/agent/runtime_binding/engine_environment.rb +60 -0
  164. data/lib/phronomy/agent/runtime_binding/engine_session_builder.rb +34 -0
  165. data/lib/phronomy/agent/runtime_binding/phase_machine_builder.rb +64 -0
  166. data/lib/phronomy/agent/runtime_binding/tool_session_builder.rb +34 -0
  167. data/lib/phronomy/agent/{context_assembly/ruby_llm_materializer.rb → runtime_input.rb} +18 -35
  168. data/lib/phronomy/agent/{context_assembly/saved_context_reader.rb → saved_context_reader.rb} +6 -4
  169. data/lib/phronomy/agent/settings.rb +47 -0
  170. data/lib/phronomy/agent/{context_assembly/state_writer.rb → state_writer.rb} +4 -3
  171. data/lib/phronomy/agent/store.rb +253 -0
  172. data/lib/phronomy/agent/{lifecycle_contract/stream_callback_error.rb → stream_callback_error.rb} +1 -1
  173. data/lib/phronomy/agent/tool_execution/tool_binding.rb +4 -64
  174. data/lib/phronomy/agent/tool_execution/tool_invocation.rb +50 -219
  175. data/lib/phronomy/agent/tool_execution/tool_invocation_actions.rb +107 -0
  176. data/lib/phronomy/agent/tool_execution/tool_invocation_transitions.rb +70 -0
  177. data/lib/phronomy/configuration/runtime_settings.rb +49 -0
  178. data/lib/phronomy/content_store/storage_schema.rb +1 -0
  179. data/lib/phronomy/content_store/stored_contents.rb +2 -1
  180. data/lib/phronomy/context/assembly.rb +202 -0
  181. data/lib/phronomy/{llm_contract/context_budget_exceeded_error.rb → context/budget_exceeded_error.rb} +3 -1
  182. data/lib/phronomy/context/candidate.rb +51 -0
  183. data/lib/phronomy/context/constraint.rb +47 -0
  184. data/lib/phronomy/{agent/context_contract → context}/context_plan.rb +1 -1
  185. data/lib/phronomy/{agent/context_contract → context}/context_plan_validator.rb +3 -3
  186. data/lib/phronomy/{agent/context_contract → context}/context_policy.rb +1 -1
  187. data/lib/phronomy/{agent/context_contract → context}/context_policy_input.rb +1 -1
  188. data/lib/phronomy/{agent/context_assembly → context}/context_policy_input_builder.rb +1 -1
  189. data/lib/phronomy/context/default_policy.rb +140 -0
  190. data/lib/phronomy/context/final_budget_validator.rb +33 -0
  191. data/lib/phronomy/{agent/context_contract → context}/llm_input_build_context.rb +1 -1
  192. data/lib/phronomy/{agent/context_contract → context}/llm_input_manifest.rb +19 -19
  193. data/lib/phronomy/{agent/context_contract → context}/llm_input_patch.rb +1 -1
  194. data/lib/phronomy/context/manifest_error.rb +10 -0
  195. data/lib/phronomy/context/prompt_template.rb +98 -0
  196. data/lib/phronomy/{vector_store → documents}/loader/base.rb +1 -1
  197. data/lib/phronomy/{vector_store → documents}/loader/csv_loader.rb +2 -2
  198. data/lib/phronomy/{vector_store → documents}/loader/markdown_loader.rb +3 -3
  199. data/lib/phronomy/{vector_store → documents}/loader/plain_text_loader.rb +2 -2
  200. data/lib/phronomy/{vector_store → documents}/splitter/base.rb +1 -1
  201. data/lib/phronomy/{vector_store → documents}/splitter/fixed_size_splitter.rb +2 -2
  202. data/lib/phronomy/{vector_store → documents}/splitter/recursive_splitter.rb +2 -2
  203. data/lib/phronomy/embeddings/async/async_client.rb +44 -0
  204. data/lib/phronomy/embeddings/backends/ruby_llm_embeddings.rb +48 -0
  205. data/lib/phronomy/embeddings/base.rb +43 -0
  206. data/lib/phronomy/embeddings/invalid_result_error.rb +11 -0
  207. data/lib/phronomy/embeddings/transport_error.rb +11 -0
  208. data/lib/phronomy/engine/concurrency/offload_pool.rb +36 -21
  209. data/lib/phronomy/engine/concurrency/pool_registry.rb +47 -12
  210. data/lib/phronomy/engine/diagnostics.rb +1 -1
  211. data/lib/phronomy/engine/event_loop.rb +7 -7
  212. data/lib/phronomy/engine/execution_binding.rb +23 -0
  213. data/lib/phronomy/engine/execution_receiver_binding.rb +43 -0
  214. data/lib/phronomy/engine/fsm_protocol.rb +1 -6
  215. data/lib/phronomy/engine/fsm_session.rb +3 -3
  216. data/lib/phronomy/engine/metrics.rb +16 -14
  217. data/lib/phronomy/engine/runtime/timer_queue.rb +1 -1
  218. data/lib/phronomy/engine/runtime.rb +25 -12
  219. data/lib/phronomy/engine/terminal_decision.rb +7 -0
  220. data/lib/phronomy/execution/async_operation.rb +41 -0
  221. data/lib/phronomy/{engine → execution}/blocking.rb +2 -2
  222. data/lib/phronomy/{engine → execution}/concurrency/cancellation_token.rb +1 -1
  223. data/lib/phronomy/{engine → execution}/concurrency/deadline.rb +0 -17
  224. data/lib/phronomy/{engine → execution}/concurrency/physical_completion_task.rb +1 -1
  225. data/lib/phronomy/{engine → execution}/concurrency/result_collector.rb +1 -1
  226. data/lib/phronomy/{engine → execution}/concurrency/result_composition.rb +33 -12
  227. data/lib/phronomy/{engine/concurrency/subscriptions.rb → execution/concurrency/result_subscriptions.rb} +14 -18
  228. data/lib/phronomy/execution/concurrency/subscriptions.rb +25 -0
  229. data/lib/phronomy/{engine → execution}/execution.rb +61 -27
  230. data/lib/phronomy/execution/execution_receiver.rb +78 -0
  231. data/lib/phronomy/execution/execution_scope_state.rb +35 -0
  232. data/lib/phronomy/{engine → execution}/invocation_context.rb +0 -15
  233. data/lib/phronomy/execution/invocation_controls.rb +73 -0
  234. data/lib/phronomy/execution/runnable.rb +24 -0
  235. data/lib/phronomy/{engine → execution}/task_result.rb +3 -3
  236. data/lib/phronomy/execution/wait_policy.rb +24 -0
  237. data/lib/phronomy/filter/{contract/filter_block_error.rb → filter_block_error.rb} +1 -1
  238. data/lib/phronomy/generation/generator_verifier/agent_operation.rb +65 -0
  239. data/lib/phronomy/generation/generator_verifier/default_parser.rb +27 -0
  240. data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +2 -2
  241. data/lib/phronomy/generation/generator_verifier.rb +7 -7
  242. data/lib/phronomy/llm_adapter/async/async_client.rb +47 -0
  243. data/lib/phronomy/{llm_contract → llm_adapter}/authentication_error.rb +3 -1
  244. data/lib/phronomy/llm_adapter/backends/ruby_llm.rb +183 -0
  245. data/lib/phronomy/llm_adapter/base.rb +49 -68
  246. data/lib/phronomy/{llm_contract → llm_adapter}/context_length_error.rb +3 -1
  247. data/lib/phronomy/llm_adapter/invalid_result_error.rb +10 -0
  248. data/lib/phronomy/llm_adapter/message.rb +62 -0
  249. data/lib/phronomy/{llm_contract → llm_adapter}/rate_limit_error.rb +3 -1
  250. data/lib/phronomy/llm_adapter/request.rb +79 -0
  251. data/lib/phronomy/llm_adapter/response.rb +36 -0
  252. data/lib/phronomy/llm_adapter/stream_chunk.rb +18 -0
  253. data/lib/phronomy/llm_adapter/token_usage.rb +64 -0
  254. data/lib/phronomy/llm_adapter/transport_error.rb +9 -0
  255. data/lib/phronomy/llm_adapter.rb +3 -16
  256. data/lib/phronomy/multi_agent/admission_registry.rb +1 -8
  257. data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +108 -84
  258. data/lib/phronomy/multi_agent/execution_environment.rb +29 -0
  259. data/lib/phronomy/{agent/handoff → multi_agent}/handoff.rb +2 -2
  260. data/lib/phronomy/{agent/handoff → multi_agent}/handoff_capability_factory.rb +21 -2
  261. data/lib/phronomy/multi_agent/handoff_participant.rb +79 -0
  262. data/lib/phronomy/{agent/handoff → multi_agent}/handoff_policy.rb +1 -1
  263. data/lib/phronomy/multi_agent/handoff_runner.rb +77 -100
  264. data/lib/phronomy/{agent/handoff → multi_agent}/handoff_state.rb +1 -1
  265. data/lib/phronomy/multi_agent/orchestrator.rb +61 -75
  266. data/lib/phronomy/multi_agent/persistence/codec.rb +20 -0
  267. data/lib/phronomy/multi_agent/persistence/handoff_state_repository.rb +77 -0
  268. data/lib/phronomy/multi_agent/persistence/records.rb +27 -0
  269. data/lib/phronomy/multi_agent/persistence/storage_schema.rb +21 -0
  270. data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +58 -42
  271. data/lib/phronomy/multi_agent/persistence/team_repository.rb +29 -21
  272. data/lib/phronomy/multi_agent/reserved_child_admission.rb +88 -0
  273. data/lib/phronomy/multi_agent/runtime_binding/engine_environment.rb +35 -0
  274. data/lib/phronomy/multi_agent/shared_state.rb +2 -2
  275. data/lib/phronomy/multi_agent/store.rb +156 -0
  276. data/lib/phronomy/multi_agent/team_coordinator.rb +83 -74
  277. data/lib/phronomy/multi_agent/team_ownership_registry.rb +2 -12
  278. data/lib/phronomy/output_parser/parse_error.rb +7 -0
  279. data/lib/phronomy/persistence/content_repository.rb +26 -0
  280. data/lib/phronomy/persistence/errors.rb +16 -0
  281. data/lib/phronomy/persistence/migration/initial_format_migration.rb +2 -2
  282. data/lib/phronomy/persistence/persistence.rb +23 -0
  283. data/lib/phronomy/persistence/save_outcome.rb +24 -0
  284. data/lib/phronomy/persistence/snapshot_comparison.rb +81 -0
  285. data/lib/phronomy/persistence/storage_boundary.rb +32 -0
  286. data/lib/phronomy/persistence/transaction.rb +86 -0
  287. data/lib/phronomy/persistence_composition/storage_schema.rb +9 -4
  288. data/lib/phronomy/persistence_composition/stores.rb +34 -0
  289. data/lib/phronomy/persistence_composition/unit4_migration.rb +276 -0
  290. data/lib/phronomy/runtime_composition/agent_defaults.rb +5 -1
  291. data/lib/phronomy/runtime_composition/configuration.rb +146 -0
  292. data/lib/phronomy/runtime_composition/evaluation_defaults.rb +7 -0
  293. data/lib/phronomy/runtime_composition/execution_defaults.rb +5 -0
  294. data/lib/phronomy/runtime_composition/generation_defaults.rb +6 -0
  295. data/lib/phronomy/runtime_composition/global_configuration.rb +38 -0
  296. data/lib/phronomy/runtime_composition/multi_agent_defaults.rb +9 -0
  297. data/lib/phronomy/runtime_composition/workflow_defaults.rb +4 -0
  298. data/lib/phronomy/storage/async/async_client.rb +60 -0
  299. data/lib/phronomy/storage/backend.rb +8 -2
  300. data/lib/phronomy/testing/eval/metrics.rb +1 -1
  301. data/lib/phronomy/testing/eval/scorer/llm_judge.rb +19 -3
  302. data/lib/phronomy/testing/persistence_contract/a_content_store.rb +2 -2
  303. data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +6 -6
  304. data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +55 -52
  305. data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +3 -3
  306. data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +7 -7
  307. data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +11 -11
  308. data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +57 -51
  309. data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +1 -1
  310. data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +23 -22
  311. data/lib/phronomy/tool/authorization.rb +77 -0
  312. data/lib/phronomy/tool/base.rb +549 -9
  313. data/lib/phronomy/tool/call_request.rb +40 -0
  314. data/lib/phronomy/{agent/tool_execution/tool_definition_set.rb → tool/definition_set.rb} +7 -8
  315. data/lib/phronomy/tool/operation.rb +88 -0
  316. data/lib/phronomy/tool/schema.rb +214 -0
  317. data/lib/phronomy/tool/settings.rb +31 -0
  318. data/lib/phronomy/tool/tool_error.rb +7 -0
  319. data/lib/phronomy/tool/tool_executor.rb +58 -0
  320. data/lib/phronomy/tools/agent.rb +22 -90
  321. data/lib/phronomy/tools/mcp.rb +21 -14
  322. data/lib/phronomy/tools/vector_search.rb +4 -4
  323. data/lib/phronomy/tracing/automatic.rb +11 -6
  324. data/lib/phronomy/tracing/base.rb +1 -1
  325. data/lib/phronomy/tracing/observation.rb +37 -0
  326. data/lib/phronomy/tracing/settings.rb +36 -0
  327. data/lib/phronomy/vector_store/{async_backend.rb → async/async_client.rb} +35 -29
  328. data/lib/phronomy/vector_store/{in_memory.rb → backends/in_memory.rb} +12 -10
  329. data/lib/phronomy/vector_store/{pgvector.rb → backends/pgvector.rb} +44 -16
  330. data/lib/phronomy/vector_store/{redis_search.rb → backends/redis_search.rb} +74 -37
  331. data/lib/phronomy/vector_store/base.rb +118 -49
  332. data/lib/phronomy/vector_store/invalid_result_error.rb +12 -0
  333. data/lib/phronomy/vector_store.rb +2 -17
  334. data/lib/phronomy/version.rb +1 -1
  335. data/lib/phronomy/workflow/completion.rb +11 -0
  336. data/lib/phronomy/workflow/execution/workflow.rb +7 -7
  337. data/lib/phronomy/workflow/execution/workflow_action_rules.rb +67 -0
  338. data/lib/phronomy/workflow/execution/workflow_context.rb +2 -2
  339. data/lib/phronomy/workflow/execution/workflow_execution_environment.rb +27 -0
  340. data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +3 -5
  341. data/lib/phronomy/workflow/execution/workflow_runner.rb +58 -79
  342. data/lib/phronomy/workflow/execution/workflow_settings.rb +28 -0
  343. data/lib/phronomy/workflow/persistence/state_repository.rb +28 -22
  344. data/lib/phronomy/workflow/persistence/storage_schema.rb +13 -0
  345. data/lib/phronomy/workflow/runtime_binding/workflow_engine_environment.rb +38 -0
  346. data/lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb +156 -0
  347. data/lib/phronomy/workflow/{execution → runtime_binding}/workflow_terminal_policy.rb +4 -4
  348. data/lib/phronomy.rb +57 -16
  349. data/scripts/api_snapshot.rb +35 -15
  350. data/sig/_private/phronomy/agent/settings.rbs +19 -0
  351. data/sig/_private/phronomy/configuration/runtime_settings.rbs +20 -0
  352. data/sig/_private/phronomy/llm_adapter/async/async_client.rbs +10 -0
  353. data/sig/_private/phronomy/multi_agent/store.rbs +8 -0
  354. data/sig/_private/phronomy/persistence/storage_boundary.rbs +7 -0
  355. data/sig/_private/phronomy/tool/settings.rbs +13 -0
  356. data/sig/_private/phronomy/tracing/settings.rbs +15 -0
  357. data/sig/_private/phronomy/workflow/execution/workflow_settings.rbs +13 -0
  358. data/sig/phronomy/agent/admission.rbs +19 -0
  359. data/sig/phronomy/agent/execution_contract.rbs +132 -0
  360. data/sig/phronomy/agent/execution_environment.rbs +20 -0
  361. data/sig/phronomy/agent/store.rbs +73 -0
  362. data/sig/phronomy/agent.rbs +14 -138
  363. data/sig/phronomy/common.rbs +7 -0
  364. data/sig/phronomy/content_store/contracts.rbs +13 -0
  365. data/sig/phronomy/context.rbs +152 -0
  366. data/sig/phronomy/embeddings/async/async_client.rbs +9 -0
  367. data/sig/phronomy/embeddings/base.rbs +17 -0
  368. data/sig/phronomy/engine/concurrency/timer_bindings.rbs +22 -0
  369. data/sig/phronomy/engine/control_bindings.rbs +11 -0
  370. data/sig/phronomy/engine/control_protocols.rbs +23 -0
  371. data/sig/phronomy/engine/execution_binding.rbs +6 -0
  372. data/sig/phronomy/engine/execution_receiver.rbs +15 -0
  373. data/sig/phronomy/engine/observability.rbs +13 -0
  374. data/sig/phronomy/execution/async_operation.rbs +7 -0
  375. data/sig/phronomy/execution/blocking.rbs +5 -0
  376. data/sig/phronomy/execution/concurrency/cancellation_token.rbs +13 -0
  377. data/sig/phronomy/execution/concurrency/deadline.rbs +10 -0
  378. data/sig/phronomy/execution/concurrency/result_composition.rbs +7 -0
  379. data/sig/phronomy/execution/control_protocols.rbs +18 -0
  380. data/sig/phronomy/execution/errors.rbs +7 -0
  381. data/sig/phronomy/execution/execution.rbs +41 -0
  382. data/sig/phronomy/execution/execution_receiver.rbs +40 -0
  383. data/sig/phronomy/execution/execution_scope_state.rbs +9 -0
  384. data/sig/phronomy/execution/invocation_context.rbs +18 -0
  385. data/sig/phronomy/execution/invocation_controls.rbs +13 -0
  386. data/sig/phronomy/execution/task_result.rbs +29 -0
  387. data/sig/phronomy/extensions.rbs +0 -21
  388. data/sig/phronomy/handoff.rbs +10 -4
  389. data/sig/phronomy/llm_adapter/backends/ruby_llm.rbs +6 -0
  390. data/sig/phronomy/llm_adapter/base.rbs +14 -0
  391. data/sig/phronomy/llm_adapter/usage.rbs +14 -0
  392. data/sig/phronomy/llm_adapter/values.rbs +43 -0
  393. data/sig/phronomy/llm_context_window.rbs +10 -0
  394. data/sig/phronomy/multi_agent/reserved_child_admission.rbs +8 -0
  395. data/sig/phronomy/multi_agent/store.rbs +54 -0
  396. data/sig/phronomy/multi_agent.rbs +6 -3
  397. data/sig/phronomy/persistence/errors.rbs +18 -0
  398. data/sig/phronomy/persistence/migration.rbs +13 -0
  399. data/sig/phronomy/persistence/transaction.rbs +19 -0
  400. data/sig/phronomy/persistence.rbs +1 -99
  401. data/sig/phronomy/persistence_composition.rbs +19 -0
  402. data/sig/phronomy/runtime_composition/configuration.rbs +36 -0
  403. data/sig/phronomy/storage/async/async_client.rbs +8 -0
  404. data/sig/phronomy/storage/backends/in_memory.rbs +9 -0
  405. data/sig/phronomy/{storage.rbs → storage/contracts.rbs} +2 -16
  406. data/sig/phronomy/tool.rbs +48 -4
  407. data/sig/phronomy/tracing/observation.rbs +7 -0
  408. data/sig/phronomy/vector_store/async/async_client.rbs +13 -0
  409. data/sig/phronomy/vector_store/base.rbs +23 -0
  410. data/sig/phronomy/workflow/execution/workflow_execution_environment.rbs +14 -0
  411. data/sig/phronomy/workflow/runtime_binding/workflow_terminal_policy.rbs +7 -0
  412. data/sig/phronomy/workflow/store.rbs +8 -0
  413. data/sig/phronomy/workflow.rbs +1 -1
  414. data/sig/phronomy.rbs +0 -49
  415. metadata +318 -117
  416. data/lib/phronomy/agent/concerns/before_llm_input.rb +0 -66
  417. data/lib/phronomy/agent/concerns/error_translation.rb +0 -45
  418. data/lib/phronomy/agent/concerns/filterable.rb +0 -184
  419. data/lib/phronomy/agent/context/capability/base.rb +0 -548
  420. data/lib/phronomy/agent/context/capability/tool_executor.rb +0 -62
  421. data/lib/phronomy/agent/context/instruction/prompt_template.rb +0 -102
  422. data/lib/phronomy/agent/context_assembly/runtime_chat_builder.rb +0 -36
  423. data/lib/phronomy/agent/context_assembly/token_budget_resolver.rb +0 -22
  424. data/lib/phronomy/agent/context_parts/validators/final_budget_validator.rb +0 -37
  425. data/lib/phronomy/agent/context_policies/default.rb +0 -142
  426. data/lib/phronomy/agent/execution/phase_machine_builder.rb +0 -101
  427. data/lib/phronomy/agent/execution/provider_call_outcome.rb +0 -106
  428. data/lib/phronomy/agent/handoff/handoff_execution_coordinator.rb +0 -15
  429. data/lib/phronomy/agent/handoff/handoff_outcome_committer.rb +0 -131
  430. data/lib/phronomy/agent/handoff/handoff_request.rb +0 -45
  431. data/lib/phronomy/agent/lifecycle_contract/handoff_error.rb +0 -7
  432. data/lib/phronomy/agent/persistence/handoff_state_repository.rb +0 -58
  433. data/lib/phronomy/agent/persistence/queries.rb +0 -61
  434. data/lib/phronomy/agent/selection/candidate.rb +0 -53
  435. data/lib/phronomy/agent/selection/constraint.rb +0 -49
  436. data/lib/phronomy/agent/tool_execution/tool_call_intercepted.rb +0 -29
  437. data/lib/phronomy/agent/tool_execution/tool_invocation_session_builder.rb +0 -276
  438. data/lib/phronomy/configuration/configuration.rb +0 -68
  439. data/lib/phronomy/configuration/global_configuration.rb +0 -26
  440. data/lib/phronomy/engine/concurrency/cancellation_scope.rb +0 -124
  441. data/lib/phronomy/engine/concurrency/operation_binding.rb +0 -43
  442. data/lib/phronomy/engine/execution_receiver.rb +0 -65
  443. data/lib/phronomy/engine/runnable.rb +0 -54
  444. data/lib/phronomy/llm_adapter/ruby_llm.rb +0 -47
  445. data/lib/phronomy/llm_contract/token_usage.rb +0 -85
  446. data/lib/phronomy/multi_agent/persistence/queries.rb +0 -30
  447. data/lib/phronomy/multi_agent/storage_contract/team_storage_schema.rb +0 -15
  448. data/lib/phronomy/output_parser/contract/parse_error.rb +0 -7
  449. data/lib/phronomy/persistence/api/persistence.rb +0 -140
  450. data/lib/phronomy/persistence_composition/repositories.rb +0 -77
  451. data/lib/phronomy/recovery/recovery.rb +0 -186
  452. data/lib/phronomy/tool/contract/tool_error.rb +0 -7
  453. data/lib/phronomy/vector_store/embeddings/base.rb +0 -44
  454. data/lib/phronomy/vector_store/embeddings/ruby_llm_embeddings.rb +0 -47
  455. data/lib/phronomy/workflow/phase_machine_builder.rb +0 -205
  456. data/lib/phronomy/workflow/storage_contract/workflow_storage_schema.rb +0 -9
  457. data/sig/phronomy/execution_receiver.rbs +0 -34
  458. data/sig/phronomy/llm_adapter.rbs +0 -11
  459. data/sig/phronomy/runtime.rbs +0 -76
  460. /data/lib/phronomy/agent/{context_assembly/context_importer.rb → context_importer.rb} +0 -0
  461. /data/lib/phronomy/{engine → execution}/backpressure_error.rb +0 -0
  462. /data/lib/phronomy/{engine → execution}/cancellation_error.rb +0 -0
  463. /data/lib/phronomy/{engine → execution}/concurrency/worker_input_restricted.rb +0 -0
  464. /data/lib/phronomy/{engine → execution}/event_loop_reentrancy_error.rb +0 -0
  465. /data/lib/phronomy/{engine → execution}/execution_cancellation_error.rb +0 -0
  466. /data/lib/phronomy/{engine → execution}/execution_timeout_error.rb +0 -0
  467. /data/lib/phronomy/{engine → execution}/invalid_async_entry_action_error.rb +0 -0
  468. /data/lib/phronomy/{engine → execution}/invalid_async_transition_action_error.rb +0 -0
  469. /data/lib/phronomy/{engine → execution}/invalid_async_workflow_action_error.rb +0 -0
  470. /data/lib/phronomy/{engine → execution}/pool_shutdown_error.rb +0 -0
  471. /data/lib/phronomy/{engine → execution}/recursion_limit_error.rb +0 -0
  472. /data/lib/phronomy/{engine → execution}/runtime_shutdown_error.rb +0 -0
  473. /data/lib/phronomy/{engine → execution}/runtime_shutdown_reentrancy_error.rb +0 -0
  474. /data/lib/phronomy/{engine → execution}/scheduler_reentrancy_error.rb +0 -0
  475. /data/lib/phronomy/{engine → execution}/timeout_error.rb +0 -0
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Agent
5
+ # Process-local execution connection selected by composition. Agent owns
6
+ # admission, progress, approval, durable outcome and recovery decisions;
7
+ # this extension supplies serial delivery, session routing and resources.
8
+ # Environments and session handles are never persistence authority.
9
+ # @api private
10
+ class ExecutionEnvironment
11
+ class << self
12
+ def install_provider(provider)
13
+ raise ArgumentError, "Agent execution provider must be callable" unless provider.respond_to?(:call)
14
+ @provider = provider
15
+ end
16
+
17
+ def current
18
+ @provider&.call || raise(Phronomy::ConfigurationError, "Agent execution environment has not been configured")
19
+ end
20
+ end
21
+
22
+ def registry = raise(NotImplementedError)
23
+ def existing_registry = raise(NotImplementedError)
24
+ def ownership = raise(NotImplementedError)
25
+ def existing_ownership = raise(NotImplementedError)
26
+ def executing? = raise(NotImplementedError)
27
+ def session_phase(id) = raise(NotImplementedError)
28
+ def submit(**options, &operation) = raise(NotImplementedError)
29
+ def submit_authorization(timeout:, cancellation_token:, &operation) = raise(NotImplementedError)
30
+ def build_llm_client(adapter:) = raise(NotImplementedError)
31
+ def build_agent_session(invocation:, resume_event: nil, resume_phase: nil) = raise(NotImplementedError)
32
+ def build_tool_session(invocation:, parent_sink:, resume_event: nil, resume_phase: nil) = raise(NotImplementedError)
33
+ def register_session(session, completion:) = raise(NotImplementedError)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Agent
5
+ # An opaque state reference owned and interpreted by its bound participant.
6
+ # @api public
7
+ ExecutionExtensionState = Data.define(:binding_key, :binding_version, :state_ref) do
8
+ def initialize(binding_key:, binding_version:, state_ref: nil)
9
+ raise ArgumentError, "binding_key is required" if binding_key.to_s.empty?
10
+ raise ArgumentError, "binding_version must be positive" unless binding_version.is_a?(Integer) && binding_version.positive?
11
+ super(binding_key: binding_key.to_s.freeze, binding_version: binding_version, state_ref: state_ref&.to_s&.freeze)
12
+ end
13
+
14
+ def to_h
15
+ {"binding_key" => binding_key, "binding_version" => binding_version, "state_ref" => state_ref}.freeze
16
+ end
17
+
18
+ def self.from_h(value) = new(**value.transform_keys(&:to_sym))
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Agent
5
+ # Stable domain observation; does not expose repositories or FSM phases.
6
+ # @api public
7
+ ExecutionObservation = Data.define(:agent_id, :execution_id, :status, :result,
8
+ :error, :reservation, :transfer_receipt, :cancellation_requested) do
9
+ def initialize(**values)
10
+ super(**values.transform_values { |value| Phronomy::Values::Immutable.copy(value) })
11
+ end
12
+
13
+ def self.from_result(value)
14
+ new(agent_id: value.fetch(:agent_id), execution_id: value.fetch(:execution_id), status: value.fetch(:status),
15
+ result: value[:result], error: value[:error], reservation: nil, transfer_receipt: nil, cancellation_requested: false)
16
+ end
17
+
18
+ def absent? = status == :absent
19
+ def terminal? = !%i[absent preparing active suspended].include?(status)
20
+ def continuable? = %i[preparing active].include?(status) && !cancellation_requested
21
+ def active? = %i[preparing active suspended].include?(status)
22
+
23
+ def value!
24
+ raise Phronomy::Persistence::NotFoundError, "Execution #{execution_id} is absent" if absent?
25
+ raise RecoverySupport.error_from_failure(error) if error
26
+ to_result
27
+ end
28
+
29
+ def to_result
30
+ Phronomy::Values::Immutable.copy(execution_id: execution_id, agent_id: agent_id,
31
+ status: status, result: result, output: result, error: error)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,182 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Agent
5
+ # Adds input, output, and tool-result filter support to an agent.
6
+ #
7
+ # Filters transform (or block) values at three call sites:
8
+ # - *input* — the raw user input string, before the LLM is called
9
+ # - *output* — the final LLM output string, before it is returned
10
+ # - *tool result* — the return value of each tool call
11
+ #
12
+ # Each filter in the chain receives the value returned by the previous one.
13
+ # A {Phronomy::FilterBlockError} raised inside any filter propagates to the
14
+ # caller.
15
+ #
16
+ # @api private
17
+ module Filterable
18
+ def self.included(base)
19
+ base.extend(ClassMethods)
20
+ end
21
+
22
+ # Class-level DSL mixed into the including agent class.
23
+ module ClassMethods
24
+ # Registers a filter applied to every invocation's user input.
25
+ # Accepts either a {Phronomy::Filter::Base} instance or a subclass;
26
+ # when a class is given it is instantiated with +.new+.
27
+ # @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>]
28
+ # @return [void]
29
+ # @api public
30
+ def input_filter(filter)
31
+ @_class_input_filters ||= []
32
+ @_class_input_filters << _resolve_filter(filter)
33
+ end
34
+
35
+ # Registers a filter applied to every invocation's final LLM output.
36
+ # Accepts either a {Phronomy::Filter::Base} instance or a subclass;
37
+ # when a class is given it is instantiated with +.new+.
38
+ # @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>]
39
+ # @return [void]
40
+ # @api public
41
+ def output_filter(filter)
42
+ @_class_output_filters ||= []
43
+ @_class_output_filters << _resolve_filter(filter)
44
+ end
45
+
46
+ # Registers a filter applied to every tool result for all tools.
47
+ # Accepts either a {Phronomy::Filter::Base} instance or a subclass;
48
+ # when a class is given it is instantiated with +.new+.
49
+ # @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>]
50
+ # @return [void]
51
+ # @api public
52
+ def tool_result_filter(filter)
53
+ @_class_tool_result_filters ||= []
54
+ @_class_tool_result_filters << _resolve_filter(filter)
55
+ end
56
+
57
+ # @return [Array<Phronomy::Filter::Base>]
58
+ # @api private
59
+ def _class_input_filters
60
+ @_class_input_filters || []
61
+ end
62
+
63
+ # @return [Array<Phronomy::Filter::Base>]
64
+ # @api private
65
+ def _class_output_filters
66
+ @_class_output_filters || []
67
+ end
68
+
69
+ # @return [Array<Phronomy::Filter::Base>]
70
+ # @api private
71
+ def _class_tool_result_filters
72
+ @_class_tool_result_filters || []
73
+ end
74
+
75
+ private
76
+
77
+ # Coerce +filter+ to an instance: if a Class is passed, call +.new+;
78
+ # otherwise return the object as-is.
79
+ # @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>]
80
+ # @return [Phronomy::Filter::Base]
81
+ # @api private
82
+ def _resolve_filter(filter)
83
+ filter.is_a?(Class) ? filter.new : filter
84
+ end
85
+ end
86
+
87
+ # Registers an input filter on this instance.
88
+ # Accepts either a {Phronomy::Filter::Base} instance or a subclass;
89
+ # when a class is given it is instantiated with +.new+.
90
+ # Runs in addition to any class-level input filters.
91
+ # @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>]
92
+ # @return [self]
93
+ # @api public
94
+ def add_input_filter(filter)
95
+ @_instance_input_filters ||= []
96
+ @_instance_input_filters << _resolve_filter(filter)
97
+ self
98
+ end
99
+
100
+ # Registers an output filter on this instance.
101
+ # Accepts either a {Phronomy::Filter::Base} instance or a subclass;
102
+ # when a class is given it is instantiated with +.new+.
103
+ # Runs in addition to any class-level output filters.
104
+ # @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>]
105
+ # @return [self]
106
+ # @api public
107
+ def add_output_filter(filter)
108
+ @_instance_output_filters ||= []
109
+ @_instance_output_filters << _resolve_filter(filter)
110
+ self
111
+ end
112
+
113
+ # Registers a tool result filter on this instance.
114
+ #
115
+ # When called with two arguments, the filter is scoped to the given tool
116
+ # class only. When called with one argument, it applies to all tools.
117
+ # Accepts either a {Phronomy::Filter::Base} instance or a subclass;
118
+ # when a class is given it is instantiated with +.new+.
119
+ #
120
+ # @overload add_tool_result_filter(filter)
121
+ # @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>] applied to every tool
122
+ # @overload add_tool_result_filter(tool_class, filter)
123
+ # @param tool_class [Class] scope the filter to this tool
124
+ # @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>]
125
+ # @return [self]
126
+ # @api public
127
+ def add_tool_result_filter(tool_class_or_filter, filter = nil)
128
+ if filter.nil?
129
+ # Single-argument form: apply to all tools.
130
+ @_instance_tool_result_filters ||= []
131
+ @_instance_tool_result_filters << _resolve_filter(tool_class_or_filter)
132
+ else
133
+ # Two-argument form: scoped to one tool class.
134
+ @_scoped_tool_result_filters ||= {}
135
+ (@_scoped_tool_result_filters[tool_class_or_filter] ||= []) << _resolve_filter(filter)
136
+ end
137
+ self
138
+ end
139
+
140
+ private
141
+
142
+ # Run input filters (class-level then instance-level).
143
+ # @param input [String, Hash] the raw user input
144
+ # @return [String, Hash] the (possibly transformed) input
145
+ # @api private
146
+ def run_input_filters!(input)
147
+ class_filters = self.class._class_input_filters
148
+ inst_filters = @_instance_input_filters || []
149
+ (class_filters + inst_filters).inject(input) { |val, f| f.call(val) }
150
+ end
151
+
152
+ # Run output filters (class-level then instance-level).
153
+ # @param output [String] the LLM output
154
+ # @return [String] the (possibly transformed) output
155
+ # @api private
156
+ def run_output_filters!(output)
157
+ class_filters = self.class._class_output_filters
158
+ inst_filters = @_instance_output_filters || []
159
+ (class_filters + inst_filters).inject(output) { |val, f| f.call(val) }
160
+ end
161
+
162
+ # Collect all tool-result filters (global + scoped) for a given tool class.
163
+ # @param tool_class [Class]
164
+ # @return [Array<Phronomy::Filter::Base>]
165
+ # @api private
166
+ def _tool_result_filters_for(tool_class)
167
+ global = self.class._class_tool_result_filters + (@_instance_tool_result_filters || [])
168
+ scoped = (@_scoped_tool_result_filters || {})[tool_class] || []
169
+ global + scoped
170
+ end
171
+
172
+ # Coerce +filter+ to an instance: if a Class is passed, call +.new+;
173
+ # otherwise return the object as-is.
174
+ # @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>]
175
+ # @return [Phronomy::Filter::Base]
176
+ # @api private
177
+ def _resolve_filter(filter)
178
+ filter.is_a?(Class) ? filter.new : filter
179
+ end
180
+ end
181
+ end
182
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Phronomy
4
4
  module Agent
5
- class HandoffContext
5
+ class TransferContext
6
6
  Provenance = Data.define(
7
7
  :origin_agent_id,
8
8
  :origin_record_id,
@@ -59,7 +59,7 @@ module Phronomy
59
59
  def initialize(**values)
60
60
  provenance = values.fetch(:provenance)
61
61
  unless provenance.is_a?(Provenance)
62
- raise ArgumentError, "HandoffContext::Item provenance must be Provenance"
62
+ raise ArgumentError, "TransferContext::Item provenance must be Provenance"
63
63
  end
64
64
  format = (values[:content_format] || :text).to_sym
65
65
  unless %i[text json].include?(format)
@@ -96,7 +96,7 @@ module Phronomy
96
96
 
97
97
  def self.from_h(value)
98
98
  source = value.transform_keys(&:to_s)
99
- raise ArgumentError, "HandoffContext schema mismatch" unless source.keys.sort == %w[items responsibility]
99
+ raise ArgumentError, "TransferContext schema mismatch" unless source.keys.sort == %w[items responsibility]
100
100
  new(responsibility: source.fetch("responsibility"), items: source.fetch("items").map do |raw|
101
101
  values = raw.transform_keys(&:to_sym)
102
102
  values[:provenance] = Provenance.new(**values.fetch(:provenance).transform_keys(&:to_sym))
@@ -111,7 +111,7 @@ module Phronomy
111
111
  @items = Array(items).freeze
112
112
  raise ArgumentError, "Handoff Context responsibility must not be empty" if @responsibility.strip.empty?
113
113
  unless @items.all? { |item| item.is_a?(Item) }
114
- raise ArgumentError, "Handoff Context items must be HandoffContext::Item values"
114
+ raise ArgumentError, "Handoff Context items must be TransferContext::Item values"
115
115
  end
116
116
  freeze
117
117
  end
@@ -2,32 +2,19 @@
2
2
 
3
3
  module Phronomy
4
4
  module Agent
5
- class HandoffProjection
5
+ class TransferProjection
6
6
  CONTROL_CATEGORIES = %i[instruction handoff_responsibility].freeze
7
7
  JSON_CATEGORIES = %i[assistant_message tool_message].freeze
8
8
 
9
9
  Identity = Data.define(:agent_id)
10
- Edge = Data.define(:policy, :target_agent)
11
- Snapshot = Data.define(:handoff, :responsibility, :selection_intent)
12
- private_constant :Identity, :Edge, :Snapshot
13
-
14
- # A terminal command contains values only, captured on EventLoop.
15
- def build_terminal(view:, manifest:, persistence:, source_agent_id:)
16
- request = Snapshot.new(
17
- handoff: Edge.new(policy: HandoffPolicy.from_h(view.policy), target_agent: Identity.new(agent_id: view.target_agent_id)),
18
- responsibility: view.responsibility,
19
- selection_intent: view.selection_intent.transform_keys(&:to_sym)
20
- )
21
- build(request: request, manifest: manifest, persistence: persistence,
22
- source_agent: Identity.new(agent_id: source_agent_id))
23
- end
10
+ private_constant :Identity
24
11
 
25
12
  def build(request:, manifest:, persistence:, source_agent:)
26
- unless request.is_a?(HandoffRequest) || request.is_a?(Snapshot)
27
- raise ArgumentError, "request must be a HandoffRequest"
13
+ unless request.is_a?(ControlRequest)
14
+ raise ArgumentError, "request must be an Agent::ControlRequest"
28
15
  end
29
- unless manifest.is_a?(Phronomy::Agent::LLMInputManifest)
30
- raise ArgumentError, "manifest must be an LLMInputManifest"
16
+ unless manifest.is_a?(Phronomy::Context::LLMInputManifest)
17
+ raise ArgumentError, "manifest must be an Phronomy::Context::LLMInputManifest"
31
18
  end
32
19
 
33
20
  groups = project_visible_groups(manifest)
@@ -42,12 +29,12 @@ module Phronomy
42
29
  policy_category: group.fetch(:policy_category),
43
30
  persistence: persistence,
44
31
  source_agent: source_agent,
45
- target_agent: request.handoff.target_agent
32
+ target_agent: Identity.new(agent_id: request.target_agent_id)
46
33
  )
47
34
  end
48
35
  end
49
36
 
50
- HandoffContext.new(
37
+ TransferContext.new(
51
38
  responsibility: request.responsibility,
52
39
  items: items
53
40
  )
@@ -116,7 +103,7 @@ module Phronomy
116
103
  end
117
104
 
118
105
  # Compatibility for finalized pre-ACS-04 manifests. New manifests use
119
- # context_policy_conversation_group_id and do not recreate Selection::Unit.
106
+ # context_policy_conversation_group_id and do not recreate Phronomy::Context::Unit.
120
107
  unit_id = segment.metadata["selection_unit_id"] ||
121
108
  segment.metadata[:selection_unit_id]
122
109
  return "unit:#{unit_id}" if unit_id
@@ -125,13 +112,7 @@ module Phronomy
125
112
  end
126
113
 
127
114
  def include_group?(request, category)
128
- policy = request.handoff.policy
129
- return true if policy.required?(category)
130
- return false if policy.forbidden?(category)
131
-
132
- request.selection_intent.fetch(category) do
133
- policy.default_include?(category)
134
- end
115
+ request.selection.fetch(category, false)
135
116
  end
136
117
 
137
118
  def materialize_item(segment, policy_category:, persistence:, source_agent:, target_agent:)
@@ -147,7 +128,7 @@ module Phronomy
147
128
  target_agent: target_agent
148
129
  )
149
130
 
150
- HandoffContext::Item.new(
131
+ TransferContext::Item.new(
151
132
  candidate_category: category,
152
133
  policy_category: policy_category,
153
134
  role: segment.role,
@@ -169,7 +150,7 @@ module Phronomy
169
150
  metadata[:context_policy_content_format]
170
151
  if explicit
171
152
  format = explicit.to_sym
172
- unless Phronomy::Agent::ContextPolicyInput::CONTENT_FORMATS.include?(format)
153
+ unless Phronomy::Context::ContextPolicyInput::CONTENT_FORMATS.include?(format)
173
154
  raise Phronomy::HandoffError,
174
155
  "unsupported Context content format in Manifest: #{explicit.inspect}"
175
156
  end
@@ -182,7 +163,7 @@ module Phronomy
182
163
  def provenance_for(metadata, segment:, source_agent:, target_agent:)
183
164
  inherited = metadata["handoff_provenance"]
184
165
  if inherited
185
- base = HandoffContext::Provenance.new(
166
+ base = TransferContext::Provenance.new(
186
167
  origin_agent_id: inherited["origin_agent_id"] || inherited[:origin_agent_id],
187
168
  origin_record_id: inherited["origin_record_id"] || inherited[:origin_record_id],
188
169
  origin_execution_id: inherited["origin_execution_id"] || inherited[:origin_execution_id],
@@ -193,7 +174,7 @@ module Phronomy
193
174
  return base.forwarded_to(target_agent.agent_id)
194
175
  end
195
176
 
196
- HandoffContext::Provenance.new(
177
+ TransferContext::Provenance.new(
197
178
  origin_agent_id: metadata["source_agent_id"] || source_agent.agent_id,
198
179
  origin_record_id: metadata["journal_record_id"],
199
180
  origin_execution_id: metadata["source_execution_id"],
@@ -3,5 +3,5 @@
3
3
  require_relative "../common/error"
4
4
 
5
5
  module Phronomy
6
- class TransportError < Error; end
6
+ class HandoffError < Error; end
7
7
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../../engine/concurrency/worker_input_restricted"
3
+ require_relative "../../execution/concurrency/worker_input_restricted"
4
4
 
5
5
  module Phronomy
6
6
  module Agent
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Agent
5
+ # Materialized knowledge accepted by create(knowledge:) without internal DTOs.
6
+ # @api public
7
+ KnowledgeItem = Data.define(:content, :metadata) do
8
+ def initialize(content:, metadata: {})
9
+ super(content: String(content).dup.freeze, metadata: Phronomy::Values::Immutable.copy(metadata))
10
+ end
11
+ end
12
+ end
13
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "../../engine/concurrency/worker_input_restricted"
3
+ require_relative "../../execution/concurrency/worker_input_restricted"
4
4
 
5
5
  require "time"
6
6
 
@@ -12,21 +12,16 @@ module Phronomy
12
12
  # Candidates have no side effects. Runtime retains the first registration.
13
13
  # Existing instances remain available for lookup and transition completion
14
14
  # after admission closes; their own gate rejects new ownership changes.
15
- def self.for(runtime)
16
- existing_for(runtime) || runtime.__register_shutdown_participant(
17
- key: self, participant: new(runtime: runtime)
18
- )
19
- end
20
-
21
- def self.existing_for(runtime)
22
- runtime.__shutdown_participant(key: self)
23
- end
15
+ def self.current = ExecutionEnvironment.current.ownership
16
+ def self.existing_current = ExecutionEnvironment.current.existing_ownership
17
+ def self.for(environment) = environment.ownership
18
+ def self.existing_for(environment) = environment.existing_ownership
24
19
 
25
20
  Entry = Data.define(:state, :agent, :token)
26
21
  private_constant :Entry
27
22
 
28
- def initialize(runtime:)
29
- @runtime = runtime
23
+ def initialize(environment:)
24
+ @environment = environment
30
25
  @mutex = Mutex.new
31
26
  @condition = ConditionVariable.new
32
27
  @entries = {}
@@ -38,11 +33,11 @@ module Phronomy
38
33
  token = reserve_create!(key)
39
34
 
40
35
  begin
41
- agent = yield(@runtime)
36
+ agent = yield(@environment)
42
37
  rescue Phronomy::AgentAlreadyExistsError,
43
- Phronomy::Storage::ConflictError,
44
- Phronomy::Storage::NotFoundError,
45
- Phronomy::Storage::SerializationError,
38
+ Phronomy::Persistence::ConflictError,
39
+ Phronomy::Persistence::NotFoundError,
40
+ Phronomy::Persistence::SerializationError,
46
41
  ArgumentError,
47
42
  Phronomy::ConfigurationError
48
43
  release_construction!(key, token)
@@ -98,7 +93,7 @@ module Phronomy
98
93
  end
99
94
 
100
95
  begin
101
- agent = yield(@runtime)
96
+ agent = yield(@environment)
102
97
  publish_constructed!(
103
98
  key,
104
99
  token,
@@ -161,7 +156,7 @@ module Phronomy
161
156
  end
162
157
  @entries[key] = Entry.new(state: :purging, agent: agent, token: token)
163
158
  end
164
- agent.send(:__mark_purging!, @runtime)
159
+ agent.send(:__mark_purging!, @environment)
165
160
  token
166
161
  end
167
162
 
@@ -170,7 +165,7 @@ module Phronomy
170
165
  @mutex.synchronize do
171
166
  entry = @entries[key]
172
167
  validate_purge_entry!(entry, agent, token, key)
173
- agent.send(:__mark_purged!, @runtime)
168
+ agent.send(:__mark_purged!, @environment)
174
169
  @entries.delete(key)
175
170
  @condition.broadcast
176
171
  end
@@ -183,7 +178,7 @@ module Phronomy
183
178
  @mutex.synchronize do
184
179
  entry = @entries[key]
185
180
  validate_purge_entry!(entry, agent, token, key)
186
- agent.send(:__restore_live_after_purge_abort!, @runtime)
181
+ agent.send(:__restore_live_after_purge_abort!, @environment)
187
182
  @entries[key] = Entry.new(state: :live, agent: agent, token: nil)
188
183
  @condition.broadcast
189
184
  end
@@ -197,7 +192,7 @@ module Phronomy
197
192
  key = normalize_agent_id(agent.agent_id)
198
193
  @mutex.synchronize do
199
194
  validate_purge_entry!(@entries[key], agent, token, key)
200
- agent.send(:__mark_ownership_recovery_required!, @runtime)
195
+ agent.send(:__mark_ownership_recovery_required!, @environment)
201
196
  @entries[key] = Entry.new(
202
197
  state: :recovery_required,
203
198
  agent: agent,
@@ -239,7 +234,7 @@ module Phronomy
239
234
  @condition.broadcast
240
235
  owned
241
236
  end
242
- agents.each { |agent| agent.send(:__release_runtime_owner!, @runtime) }
237
+ agents.each { |agent| agent.send(:__release_runtime_owner!, @environment) }
243
238
  true
244
239
  end
245
240
 
@@ -267,7 +262,7 @@ module Phronomy
267
262
  "constructed Agent identity mismatch: reserved #{key.inspect}, got #{agent.agent_id.inspect}"
268
263
  end
269
264
 
270
- agent.send(:__bind_runtime_owner!, @runtime)
265
+ agent.send(:__bind_runtime_owner!, @environment)
271
266
  @mutex.synchronize do
272
267
  entry = @entries[key]
273
268
  unless entry&.state == :constructing && entry.token.equal?(token)
@@ -282,7 +277,7 @@ module Phronomy
282
277
  # :nocov:
283
278
  if fail_closed
284
279
  fail_construction_closed!(key, token, agent: agent)
285
- agent&.send(:__mark_ownership_recovery_required!, @runtime)
280
+ agent&.send(:__mark_ownership_recovery_required!, @environment)
286
281
  else
287
282
  release_construction!(key, token)
288
283
  end
@@ -9,39 +9,47 @@ module Phronomy
9
9
  def initialize(view) = @view = view
10
10
 
11
11
  def create(root)
12
- record = Codec.encode_agent_root(root)
13
- @view.atomic do |bound|
14
- entry = bound.records(StorageSchema::ROOTS).insert(key: root.agent_id.to_s,
15
- revision: Integer(root.agent_revision), attributes: {}, record: record)
16
- decode(entry, root.agent_id, revision: root.agent_revision)
12
+ Phronomy::Persistence::StorageBoundary.call do
13
+ record = Codec.encode_agent_root(root)
14
+ @view.atomic do |bound|
15
+ entry = bound.records(StorageSchema::ROOTS).insert(key: root.agent_id.to_s,
16
+ revision: Integer(root.agent_revision), attributes: {}, record: record)
17
+ decode(entry, root.agent_id, revision: root.agent_revision)
18
+ end
17
19
  end
18
20
  end
19
21
 
20
22
  def load(agent_id)
21
- value = @view.atomic do |bound|
22
- entry = bound.records(StorageSchema::ROOTS).read(agent_id.to_s)
23
- entry && decode(entry, agent_id)
23
+ Phronomy::Persistence::StorageBoundary.call do
24
+ value = @view.atomic do |bound|
25
+ entry = bound.records(StorageSchema::ROOTS).read(agent_id.to_s)
26
+ entry && decode(entry, agent_id)
27
+ end
28
+ value || raise(Phronomy::Storage::NotFoundError, "Agent not found: #{agent_id}")
24
29
  end
25
- value || raise(Phronomy::Storage::NotFoundError, "Agent not found: #{agent_id}")
26
30
  end
27
31
 
28
32
  def save(agent_id, expected_revision:, root:)
29
- expected = Integer(expected_revision)
30
- revision = Integer(root.agent_revision)
31
- raise Phronomy::Storage::ConflictError, "root save must advance revision exactly once" unless revision == expected + 1
32
- unless root.agent_id.to_s == agent_id.to_s
33
- raise Phronomy::Storage::SerializationError, "Agent root identity mismatch"
34
- end
35
- record = Codec.encode_agent_root(root)
36
- @view.atomic do |bound|
37
- entry = bound.records(StorageSchema::ROOTS).replace(key: agent_id.to_s,
38
- expected_revision: expected, next_revision: revision, attributes: {}, record: record)
39
- decode(entry, agent_id, revision: revision)
33
+ Phronomy::Persistence::StorageBoundary.call do
34
+ expected = Integer(expected_revision)
35
+ revision = Integer(root.agent_revision)
36
+ raise Phronomy::Storage::ConflictError, "root save must advance revision exactly once" unless revision == expected + 1
37
+ unless root.agent_id.to_s == agent_id.to_s
38
+ raise Phronomy::Storage::SerializationError, "Agent root identity mismatch"
39
+ end
40
+ record = Codec.encode_agent_root(root)
41
+ @view.atomic do |bound|
42
+ entry = bound.records(StorageSchema::ROOTS).replace(key: agent_id.to_s,
43
+ expected_revision: expected, next_revision: revision, attributes: {}, record: record)
44
+ decode(entry, agent_id, revision: revision)
45
+ end
40
46
  end
41
47
  end
42
48
 
43
49
  def delete(agent_id)
44
- @view.records(StorageSchema::ROOTS).delete(key: agent_id.to_s)
50
+ Phronomy::Persistence::StorageBoundary.call do
51
+ @view.records(StorageSchema::ROOTS).delete(key: agent_id.to_s)
52
+ end
45
53
  end
46
54
 
47
55
  private