phronomy 0.27.0 → 0.29.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 (517) 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 +28 -7
  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/application-recipes.md +229 -0
  11. data/docs/architecture/agent-chat-and-state-ownership.md +7 -1
  12. data/docs/architecture/agent-transition-ownership.md +13 -89
  13. data/docs/architecture/backend-groups.md +152 -0
  14. data/docs/architecture/before-llm-input.md +3 -3
  15. data/docs/architecture/context-management.md +1 -1
  16. data/docs/architecture/dx05-agent-scope.md +119 -0
  17. data/docs/architecture/dx05-workflow-task-state.md +134 -0
  18. data/docs/architecture/error-api-review.md +245 -0
  19. data/docs/architecture/execution-abstraction-review.md +88 -0
  20. data/docs/architecture/execution-boundaries.md +28 -0
  21. data/docs/architecture/execution-metadata-and-values.md +6 -0
  22. data/docs/architecture/multi-agent-handoff.md +6 -5
  23. data/docs/architecture/persistence.md +27 -9
  24. data/docs/architecture/r8-unit1.md +104 -0
  25. data/docs/architecture/r8-unit10.md +72 -0
  26. data/docs/architecture/r8-unit11.md +71 -0
  27. data/docs/architecture/r8-unit12.md +67 -0
  28. data/docs/architecture/r8-unit13.md +61 -0
  29. data/docs/architecture/r8-unit14.md +72 -0
  30. data/docs/architecture/r8-unit15.md +65 -0
  31. data/docs/architecture/r8-unit16.md +54 -0
  32. data/docs/architecture/r8-unit17.md +73 -0
  33. data/docs/architecture/r8-unit18.md +92 -0
  34. data/docs/architecture/r8-unit19.md +61 -0
  35. data/docs/architecture/r8-unit2.md +55 -0
  36. data/docs/architecture/r8-unit20.md +51 -0
  37. data/docs/architecture/r8-unit21.md +79 -0
  38. data/docs/architecture/r8-unit22.md +74 -0
  39. data/docs/architecture/r8-unit23.md +81 -0
  40. data/docs/architecture/r8-unit3.md +130 -0
  41. data/docs/architecture/r8-unit4.md +79 -0
  42. data/docs/architecture/r8-unit5.md +115 -0
  43. data/docs/architecture/r8-unit6.md +71 -0
  44. data/docs/architecture/r8-unit7.md +70 -0
  45. data/docs/architecture/r8-unit8.md +78 -0
  46. data/docs/architecture/r8-unit9.md +73 -0
  47. data/docs/architecture/refactoring-closure.md +10 -1
  48. data/docs/architecture/remaining-refactoring-plan.md +16 -0
  49. data/docs/architecture/storage-execution-boundaries.md +54 -0
  50. data/docs/architecture/tool-schema-recording-gap.md +6 -0
  51. data/docs/architecture/tracing.md +1 -1
  52. data/docs/architecture/workflow-terminal-ownership-design.md +2 -2
  53. data/docs/architecture.md +24 -11
  54. data/docs/decisions/015-tool-public-facade-and-rbs-boundary.md +3 -3
  55. data/docs/decisions/027-llm-adapter-provider-boundary.md +2 -0
  56. data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +2 -0
  57. data/docs/decisions/031-durable-multi-agent-coordination.md +2 -0
  58. data/docs/decisions/032-storage-backend-composition.md +5 -0
  59. data/docs/decisions/034-handoff-runner-coordination-ownership.md +2 -0
  60. data/docs/decisions/035-tool-executor-capability-ownership.md +1 -1
  61. data/docs/decisions/038-responsibility-based-source-layout.md +14 -0
  62. data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +8 -0
  63. data/docs/decisions/040-configuration-default-composition.md +12 -1
  64. data/docs/decisions/045-worker-input-restriction-ownership.md +4 -0
  65. data/docs/decisions/051-execution-outcome-worker-ownership.md +2 -0
  66. data/docs/decisions/056-workflow-terminal-policy-ownership.md +1 -1
  67. data/docs/decisions/058-neutral-storage-primitives.md +5 -0
  68. data/docs/decisions/059-backend-contracts-and-async-clients.md +74 -0
  69. data/docs/decisions/060-runtime-settings-and-application-configuration.md +86 -0
  70. data/docs/decisions/061-persistence-failure-contracts.md +89 -0
  71. data/docs/decisions/062-content-store-service.md +52 -0
  72. data/docs/decisions/063-execution-contracts-and-services.md +88 -0
  73. data/docs/decisions/064-agent-execution-and-multi-agent-responsibility.md +36 -0
  74. data/docs/decisions/065-vector-and-embeddings-common-operations.md +38 -0
  75. data/docs/decisions/066-llm-and-tool-operation-contracts.md +20 -0
  76. data/docs/decisions/067-agent-progress-and-engine-connection.md +29 -0
  77. data/docs/decisions/068-agent-tool-child-progress-and-tool-operation.md +36 -0
  78. data/docs/decisions/README.md +18 -1
  79. data/docs/execution-boundaries-preview.md +45 -0
  80. data/docs/failure-reason-codes.md +45 -0
  81. data/docs/features.md +12 -4
  82. data/docs/getting-started.md +52 -48
  83. data/docs/migrations/0.22.md +2 -2
  84. data/docs/migrations/durable-semantic-coordination-v2.md +2 -0
  85. data/docs/migrations/handoff-runner-multi-agent.md +2 -0
  86. data/docs/migrations/persistence-failure-contracts.md +67 -0
  87. data/docs/migrations/r8-unit10.md +36 -0
  88. data/docs/migrations/r8-unit11.md +33 -0
  89. data/docs/migrations/r8-unit12.md +31 -0
  90. data/docs/migrations/r8-unit13.md +36 -0
  91. data/docs/migrations/r8-unit14.md +31 -0
  92. data/docs/migrations/r8-unit15.md +31 -0
  93. data/docs/migrations/r8-unit16.md +26 -0
  94. data/docs/migrations/r8-unit17.md +28 -0
  95. data/docs/migrations/r8-unit18.md +29 -0
  96. data/docs/migrations/r8-unit19.md +28 -0
  97. data/docs/migrations/r8-unit20.md +24 -0
  98. data/docs/migrations/r8-unit21.md +28 -0
  99. data/docs/migrations/r8-unit22.md +29 -0
  100. data/docs/migrations/r8-unit23.md +20 -0
  101. data/docs/migrations/r8-unit4.md +67 -0
  102. data/docs/migrations/r8-unit5.md +75 -0
  103. data/docs/migrations/r8-unit6.md +109 -0
  104. data/docs/migrations/r8-unit7.md +31 -0
  105. data/docs/migrations/r8-unit8.md +33 -0
  106. data/docs/migrations/r8-unit9.md +28 -0
  107. data/docs/migrations/storage-async-client.md +103 -0
  108. data/docs/migrations/vector-async-clients.md +110 -0
  109. data/docs/ordered-event-delivery.md +54 -0
  110. data/docs/persistence-backends.md +38 -21
  111. data/docs/runtime-and-concurrency.md +26 -1
  112. data/docs/workflow-completion.md +92 -0
  113. data/gemfiles/http_minimum.gemfile +5 -0
  114. data/lib/phronomy/agent/admission.rb +78 -0
  115. data/lib/phronomy/agent/{lifecycle_contract/agent_already_exists_error.rb → agent_already_exists_error.rb} +1 -1
  116. data/lib/phronomy/agent/{lifecycle_contract/agent_busy_error.rb → agent_busy_error.rb} +1 -1
  117. data/lib/phronomy/agent/agent_not_found_error.rb +16 -0
  118. data/lib/phronomy/agent/{lifecycle_contract/agent_purged_error.rb → agent_purged_error.rb} +1 -1
  119. data/lib/phronomy/agent/agent_unloaded_error.rb +14 -0
  120. data/lib/phronomy/agent/api/agent.rb +13 -0
  121. data/lib/phronomy/{recovery → agent/api}/execution_rehydration_required_error.rb +1 -1
  122. data/lib/phronomy/agent/async_event_api.rb +132 -103
  123. data/lib/phronomy/agent/base.rb +146 -113
  124. data/lib/phronomy/agent/before_llm_input.rb +70 -0
  125. data/lib/phronomy/agent/composition/run_once.rb +1 -1
  126. data/lib/phronomy/agent/{context_assembly/context_candidate_resolver.rb → context_candidate_resolver.rb} +2 -2
  127. data/lib/phronomy/agent/{context_assembly/context_assembler.rb → context_preparation.rb} +35 -221
  128. data/lib/phronomy/agent/control_request.rb +21 -0
  129. data/lib/phronomy/agent/execution/agent_execution.rb +1 -1
  130. data/lib/phronomy/agent/execution/agent_invocation.rb +27 -53
  131. data/lib/phronomy/agent/execution/approval_resume_commit.rb +1 -1
  132. data/lib/phronomy/agent/execution/dispatch_preparation.rb +17 -127
  133. data/lib/phronomy/agent/execution/exact_execution.rb +55 -32
  134. data/lib/phronomy/agent/execution/execution_cancellation.rb +8 -4
  135. data/lib/phronomy/agent/execution/execution_coordinator.rb +229 -407
  136. data/lib/phronomy/agent/execution/execution_metadata.rb +1 -0
  137. data/lib/phronomy/agent/execution/execution_outcome_committer.rb +71 -127
  138. data/lib/phronomy/agent/execution/execution_registry.rb +6 -2
  139. data/lib/phronomy/agent/execution/execution_session_runner.rb +22 -27
  140. data/lib/phronomy/agent/execution/initial_preparation.rb +19 -120
  141. data/lib/phronomy/agent/execution/{agent_invocation_session_builder.rb → invocation_actions.rb} +54 -194
  142. data/lib/phronomy/agent/execution/invocation_transitions.rb +39 -1
  143. data/lib/phronomy/agent/execution/runtime_record_encoder.rb +8 -11
  144. data/lib/phronomy/agent/execution_change.rb +161 -0
  145. data/lib/phronomy/agent/execution_environment.rb +36 -0
  146. data/lib/phronomy/agent/execution_extension_state.rb +21 -0
  147. data/lib/phronomy/agent/execution_observation.rb +35 -0
  148. data/lib/phronomy/agent/filterable.rb +188 -0
  149. data/lib/phronomy/agent/handoff/{handoff_context.rb → transfer_context.rb} +4 -4
  150. data/lib/phronomy/agent/handoff/{handoff_projection.rb → transfer_projection.rb} +14 -33
  151. data/lib/phronomy/{llm_contract/transport_error.rb → agent/handoff_error.rb} +1 -1
  152. data/lib/phronomy/agent/journal/journal_projection.rb +1 -1
  153. data/lib/phronomy/agent/knowledge_item.rb +13 -0
  154. data/lib/phronomy/agent/lifecycle/agent_root.rb +1 -1
  155. data/lib/phronomy/agent/lifecycle/lifetime_api.rb +76 -0
  156. data/lib/phronomy/agent/lifecycle/operation_lease.rb +33 -0
  157. data/lib/phronomy/agent/lifecycle/operation_result.rb +53 -0
  158. data/lib/phronomy/agent/lifecycle/ownership_registry.rb +117 -31
  159. data/lib/phronomy/agent/lifecycle/scope.rb +198 -0
  160. data/lib/phronomy/agent/lifecycle/scope_cleanup_error.rb +19 -0
  161. data/lib/phronomy/agent/lifecycle/unload_transition.rb +78 -0
  162. data/lib/phronomy/agent/persistence/agent_repository.rb +29 -21
  163. data/lib/phronomy/agent/persistence/cancellation_repository.rb +51 -0
  164. data/lib/phronomy/agent/persistence/codec.rb +1 -21
  165. data/lib/phronomy/agent/persistence/execution_repository.rb +57 -41
  166. data/lib/phronomy/agent/persistence/journal_repository.rb +27 -18
  167. data/lib/phronomy/agent/persistence/records.rb +33 -0
  168. data/lib/phronomy/agent/persistence/retention_repository.rb +70 -0
  169. data/lib/phronomy/agent/persistence/storage_schema.rb +7 -2
  170. data/lib/phronomy/agent/persistence/watermark.rb +13 -11
  171. data/lib/phronomy/agent/recovery/invocation_restorer.rb +13 -21
  172. data/lib/phronomy/agent/recovery/recovery_coordinator/continuation.rb +13 -12
  173. data/lib/phronomy/agent/recovery/recovery_coordinator/installation.rb +34 -34
  174. data/lib/phronomy/agent/recovery/recovery_coordinator/resolution.rb +33 -39
  175. data/lib/phronomy/agent/recovery/recovery_coordinator.rb +28 -25
  176. data/lib/phronomy/agent/recovery/recovery_rules.rb +119 -0
  177. data/lib/phronomy/agent/recovery/recovery_support.rb +10 -19
  178. data/lib/phronomy/agent/reserved_execution.rb +16 -0
  179. data/lib/phronomy/agent/retention.rb +17 -0
  180. data/lib/phronomy/agent/runtime_binding/engine_environment.rb +60 -0
  181. data/lib/phronomy/agent/runtime_binding/engine_session_builder.rb +34 -0
  182. data/lib/phronomy/agent/runtime_binding/phase_machine_builder.rb +64 -0
  183. data/lib/phronomy/agent/runtime_binding/tool_session_builder.rb +34 -0
  184. data/lib/phronomy/agent/{context_assembly/ruby_llm_materializer.rb → runtime_input.rb} +18 -35
  185. data/lib/phronomy/agent/{context_assembly/saved_context_reader.rb → saved_context_reader.rb} +6 -4
  186. data/lib/phronomy/agent/settings.rb +47 -0
  187. data/lib/phronomy/agent/{context_assembly/state_writer.rb → state_writer.rb} +4 -3
  188. data/lib/phronomy/agent/store.rb +253 -0
  189. data/lib/phronomy/agent/{lifecycle_contract/stream_callback_error.rb → stream_callback_error.rb} +3 -3
  190. data/lib/phronomy/agent/tool_execution/tool_binding.rb +4 -64
  191. data/lib/phronomy/agent/tool_execution/tool_invocation.rb +50 -219
  192. data/lib/phronomy/agent/tool_execution/tool_invocation_actions.rb +107 -0
  193. data/lib/phronomy/agent/tool_execution/tool_invocation_transitions.rb +70 -0
  194. data/lib/phronomy/common/error.rb +65 -1
  195. data/lib/phronomy/configuration/runtime_settings.rb +49 -0
  196. data/lib/phronomy/content_store/base.rb +7 -2
  197. data/lib/phronomy/content_store/storage_schema.rb +1 -0
  198. data/lib/phronomy/content_store/stored_contents.rb +2 -1
  199. data/lib/phronomy/context/assembly.rb +195 -0
  200. data/lib/phronomy/{llm_contract/context_budget_exceeded_error.rb → context/budget_exceeded_error.rb} +3 -1
  201. data/lib/phronomy/context/candidate.rb +51 -0
  202. data/lib/phronomy/context/constraint.rb +47 -0
  203. data/lib/phronomy/{agent/context_contract → context}/context_plan.rb +1 -1
  204. data/lib/phronomy/{agent/context_contract → context}/context_plan_validator.rb +3 -3
  205. data/lib/phronomy/{agent/context_contract → context}/context_policy.rb +1 -1
  206. data/lib/phronomy/{agent/context_contract → context}/context_policy_input.rb +1 -1
  207. data/lib/phronomy/{agent/context_assembly → context}/context_policy_input_builder.rb +1 -1
  208. data/lib/phronomy/context/default_policy.rb +140 -0
  209. data/lib/phronomy/context/final_budget_validator.rb +33 -0
  210. data/lib/phronomy/{agent/context_contract → context}/llm_input_build_context.rb +1 -1
  211. data/lib/phronomy/{agent/context_contract → context}/llm_input_manifest.rb +19 -19
  212. data/lib/phronomy/{agent/context_contract → context}/llm_input_patch.rb +1 -1
  213. data/lib/phronomy/context/manifest_error.rb +10 -0
  214. data/lib/phronomy/context/prompt_template.rb +98 -0
  215. data/lib/phronomy/{vector_store → documents}/loader/base.rb +1 -1
  216. data/lib/phronomy/{vector_store → documents}/loader/csv_loader.rb +2 -2
  217. data/lib/phronomy/{vector_store → documents}/loader/markdown_loader.rb +3 -3
  218. data/lib/phronomy/{vector_store → documents}/loader/plain_text_loader.rb +2 -2
  219. data/lib/phronomy/{vector_store → documents}/splitter/base.rb +1 -1
  220. data/lib/phronomy/{vector_store → documents}/splitter/fixed_size_splitter.rb +2 -2
  221. data/lib/phronomy/{vector_store → documents}/splitter/recursive_splitter.rb +2 -2
  222. data/lib/phronomy/embeddings/async/async_client.rb +44 -0
  223. data/lib/phronomy/embeddings/authentication_error.rb +11 -0
  224. data/lib/phronomy/embeddings/backends/ruby_llm_embeddings.rb +52 -0
  225. data/lib/phronomy/embeddings/base.rb +43 -0
  226. data/lib/phronomy/embeddings/invalid_result_error.rb +11 -0
  227. data/lib/phronomy/embeddings/rate_limit_error.rb +11 -0
  228. data/lib/phronomy/embeddings/transport_error.rb +11 -0
  229. data/lib/phronomy/engine/concurrency/offload_pool.rb +36 -21
  230. data/lib/phronomy/engine/concurrency/pool_registry.rb +47 -12
  231. data/lib/phronomy/engine/diagnostics.rb +1 -1
  232. data/lib/phronomy/engine/event_loop.rb +7 -7
  233. data/lib/phronomy/engine/execution_binding.rb +23 -0
  234. data/lib/phronomy/engine/execution_receiver_binding.rb +43 -0
  235. data/lib/phronomy/engine/fsm_protocol.rb +1 -6
  236. data/lib/phronomy/engine/fsm_session.rb +3 -3
  237. data/lib/phronomy/engine/metrics.rb +16 -14
  238. data/lib/phronomy/engine/runtime/timer_queue.rb +1 -1
  239. data/lib/phronomy/engine/runtime.rb +25 -12
  240. data/lib/phronomy/engine/terminal_decision.rb +7 -0
  241. data/lib/phronomy/execution/async_operation.rb +41 -0
  242. data/lib/phronomy/{engine → execution}/blocking.rb +2 -2
  243. data/lib/phronomy/{engine → execution}/concurrency/cancellation_token.rb +1 -1
  244. data/lib/phronomy/{engine → execution}/concurrency/deadline.rb +0 -17
  245. data/lib/phronomy/{engine → execution}/concurrency/physical_completion_task.rb +1 -1
  246. data/lib/phronomy/{engine → execution}/concurrency/result_collector.rb +1 -1
  247. data/lib/phronomy/{engine → execution}/concurrency/result_composition.rb +33 -12
  248. data/lib/phronomy/{engine/concurrency/subscriptions.rb → execution/concurrency/result_subscriptions.rb} +14 -18
  249. data/lib/phronomy/execution/concurrency/subscriptions.rb +25 -0
  250. data/lib/phronomy/{engine → execution}/execution.rb +61 -27
  251. data/lib/phronomy/{engine → execution}/execution_cancellation_error.rb +2 -2
  252. data/lib/phronomy/execution/execution_receiver.rb +78 -0
  253. data/lib/phronomy/execution/execution_scope_state.rb +35 -0
  254. data/lib/phronomy/{engine → execution}/execution_timeout_error.rb +2 -2
  255. data/lib/phronomy/{engine → execution}/invocation_context.rb +0 -15
  256. data/lib/phronomy/execution/invocation_controls.rb +73 -0
  257. data/lib/phronomy/execution/runnable.rb +24 -0
  258. data/lib/phronomy/{engine → execution}/task_result.rb +9 -4
  259. data/lib/phronomy/execution/wait_policy.rb +24 -0
  260. data/lib/phronomy/filter/{contract/filter_block_error.rb → filter_block_error.rb} +3 -3
  261. data/lib/phronomy/generation/generator_verifier/agent_operation.rb +65 -0
  262. data/lib/phronomy/generation/generator_verifier/default_parser.rb +27 -0
  263. data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +2 -2
  264. data/lib/phronomy/generation/generator_verifier.rb +7 -7
  265. data/lib/phronomy/generation/low_confidence_error.rb +2 -2
  266. data/lib/phronomy/integrations/ordered_event_delivery.rb +208 -0
  267. data/lib/phronomy/llm_adapter/async/async_client.rb +47 -0
  268. data/lib/phronomy/{llm_contract → llm_adapter}/authentication_error.rb +3 -1
  269. data/lib/phronomy/llm_adapter/backends/ruby_llm.rb +183 -0
  270. data/lib/phronomy/llm_adapter/base.rb +49 -68
  271. data/lib/phronomy/{llm_contract → llm_adapter}/context_length_error.rb +3 -1
  272. data/lib/phronomy/llm_adapter/invalid_result_error.rb +10 -0
  273. data/lib/phronomy/llm_adapter/message.rb +62 -0
  274. data/lib/phronomy/{llm_contract → llm_adapter}/rate_limit_error.rb +3 -1
  275. data/lib/phronomy/llm_adapter/request.rb +79 -0
  276. data/lib/phronomy/llm_adapter/response.rb +36 -0
  277. data/lib/phronomy/llm_adapter/stream_chunk.rb +18 -0
  278. data/lib/phronomy/llm_adapter/token_usage.rb +64 -0
  279. data/lib/phronomy/llm_adapter/transport_error.rb +9 -0
  280. data/lib/phronomy/llm_adapter.rb +3 -16
  281. data/lib/phronomy/multi_agent/admission_registry.rb +1 -8
  282. data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +108 -84
  283. data/lib/phronomy/multi_agent/execution_environment.rb +29 -0
  284. data/lib/phronomy/{agent/handoff → multi_agent}/handoff.rb +2 -2
  285. data/lib/phronomy/{agent/handoff → multi_agent}/handoff_capability_factory.rb +21 -2
  286. data/lib/phronomy/multi_agent/handoff_participant.rb +79 -0
  287. data/lib/phronomy/{agent/handoff → multi_agent}/handoff_policy.rb +1 -1
  288. data/lib/phronomy/multi_agent/handoff_runner.rb +77 -100
  289. data/lib/phronomy/{agent/handoff → multi_agent}/handoff_state.rb +1 -1
  290. data/lib/phronomy/multi_agent/orchestrator.rb +61 -75
  291. data/lib/phronomy/multi_agent/persistence/codec.rb +20 -0
  292. data/lib/phronomy/multi_agent/persistence/handoff_state_repository.rb +77 -0
  293. data/lib/phronomy/multi_agent/persistence/records.rb +27 -0
  294. data/lib/phronomy/multi_agent/persistence/storage_schema.rb +21 -0
  295. data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +60 -44
  296. data/lib/phronomy/multi_agent/persistence/team_repository.rb +29 -21
  297. data/lib/phronomy/multi_agent/reserved_child_admission.rb +88 -0
  298. data/lib/phronomy/multi_agent/runtime_binding/engine_environment.rb +35 -0
  299. data/lib/phronomy/multi_agent/shared_state.rb +2 -2
  300. data/lib/phronomy/multi_agent/store.rb +156 -0
  301. data/lib/phronomy/multi_agent/team_busy_error.rb +18 -0
  302. data/lib/phronomy/multi_agent/team_coordinator.rb +99 -77
  303. data/lib/phronomy/multi_agent/team_not_found_error.rb +18 -0
  304. data/lib/phronomy/multi_agent/team_ownership_registry.rb +2 -12
  305. data/lib/phronomy/output_parser/parse_error.rb +7 -0
  306. data/lib/phronomy/persistence/content_repository.rb +26 -0
  307. data/lib/phronomy/persistence/errors.rb +16 -0
  308. data/lib/phronomy/persistence/migration/initial_format_migration.rb +2 -2
  309. data/lib/phronomy/persistence/persistence.rb +23 -0
  310. data/lib/phronomy/persistence/save_outcome.rb +24 -0
  311. data/lib/phronomy/persistence/snapshot_comparison.rb +81 -0
  312. data/lib/phronomy/persistence/storage_boundary.rb +32 -0
  313. data/lib/phronomy/persistence/transaction.rb +86 -0
  314. data/lib/phronomy/persistence_composition/storage_schema.rb +9 -4
  315. data/lib/phronomy/persistence_composition/stores.rb +34 -0
  316. data/lib/phronomy/persistence_composition/unit4_migration.rb +276 -0
  317. data/lib/phronomy/runtime_composition/agent_defaults.rb +5 -1
  318. data/lib/phronomy/runtime_composition/configuration.rb +146 -0
  319. data/lib/phronomy/runtime_composition/evaluation_defaults.rb +7 -0
  320. data/lib/phronomy/runtime_composition/execution_defaults.rb +5 -0
  321. data/lib/phronomy/runtime_composition/generation_defaults.rb +6 -0
  322. data/lib/phronomy/runtime_composition/global_configuration.rb +38 -0
  323. data/lib/phronomy/runtime_composition/multi_agent_defaults.rb +9 -0
  324. data/lib/phronomy/runtime_composition/workflow_defaults.rb +4 -0
  325. data/lib/phronomy/storage/async/async_client.rb +60 -0
  326. data/lib/phronomy/storage/backend.rb +8 -2
  327. data/lib/phronomy/storage/condition_failed_error.rb +2 -2
  328. data/lib/phronomy/storage/unique_constraint_error.rb +2 -2
  329. data/lib/phronomy/testing/eval/metrics.rb +1 -1
  330. data/lib/phronomy/testing/eval/scorer/llm_judge.rb +19 -3
  331. data/lib/phronomy/testing/persistence_contract/a_content_store.rb +2 -2
  332. data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +6 -6
  333. data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +55 -52
  334. data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +3 -3
  335. data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +7 -7
  336. data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +11 -11
  337. data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +57 -51
  338. data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +1 -1
  339. data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +23 -22
  340. data/lib/phronomy/tool/authorization.rb +77 -0
  341. data/lib/phronomy/tool/base.rb +551 -9
  342. data/lib/phronomy/tool/call_request.rb +40 -0
  343. data/lib/phronomy/{agent/tool_execution/tool_definition_set.rb → tool/definition_set.rb} +7 -8
  344. data/lib/phronomy/tool/operation.rb +88 -0
  345. data/lib/phronomy/tool/schema.rb +214 -0
  346. data/lib/phronomy/tool/settings.rb +31 -0
  347. data/lib/phronomy/tool/tool_error.rb +7 -0
  348. data/lib/phronomy/tool/tool_executor.rb +58 -0
  349. data/lib/phronomy/tools/agent.rb +22 -90
  350. data/lib/phronomy/tools/mcp.rb +21 -14
  351. data/lib/phronomy/tools/vector_search.rb +4 -4
  352. data/lib/phronomy/tracing/automatic.rb +11 -6
  353. data/lib/phronomy/tracing/base.rb +36 -7
  354. data/lib/phronomy/tracing/observation.rb +37 -0
  355. data/lib/phronomy/tracing/open_telemetry_tracer.rb +4 -29
  356. data/lib/phronomy/tracing/settings.rb +36 -0
  357. data/lib/phronomy/vector_store/{async_backend.rb → async/async_client.rb} +35 -29
  358. data/lib/phronomy/vector_store/{in_memory.rb → backends/in_memory.rb} +12 -10
  359. data/lib/phronomy/vector_store/{pgvector.rb → backends/pgvector.rb} +44 -16
  360. data/lib/phronomy/vector_store/{redis_search.rb → backends/redis_search.rb} +74 -37
  361. data/lib/phronomy/vector_store/base.rb +118 -49
  362. data/lib/phronomy/vector_store/invalid_result_error.rb +12 -0
  363. data/lib/phronomy/vector_store.rb +2 -17
  364. data/lib/phronomy/version.rb +1 -1
  365. data/lib/phronomy/workflow/completion.rb +11 -0
  366. data/lib/phronomy/workflow/execution/workflow.rb +37 -9
  367. data/lib/phronomy/workflow/execution/workflow_action_rules.rb +67 -0
  368. data/lib/phronomy/workflow/execution/workflow_context.rb +2 -2
  369. data/lib/phronomy/workflow/execution/workflow_execution_environment.rb +28 -0
  370. data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +77 -8
  371. data/lib/phronomy/workflow/execution/workflow_runner.rb +105 -92
  372. data/lib/phronomy/workflow/execution/workflow_settings.rb +28 -0
  373. data/lib/phronomy/workflow/execution/workflow_task_execution.rb +153 -0
  374. data/lib/phronomy/workflow/execution/workflow_task_reception.rb +72 -0
  375. data/lib/phronomy/workflow/execution/workflow_task_resume_error.rb +14 -0
  376. data/lib/phronomy/workflow/persistence/state_repository.rb +28 -22
  377. data/lib/phronomy/workflow/persistence/storage_schema.rb +13 -0
  378. data/lib/phronomy/workflow/runtime_binding/workflow_engine_environment.rb +61 -0
  379. data/lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb +158 -0
  380. data/lib/phronomy/workflow/runtime_binding/workflow_task_session.rb +76 -0
  381. data/lib/phronomy/workflow/{execution → runtime_binding}/workflow_terminal_policy.rb +4 -4
  382. data/lib/phronomy.rb +63 -16
  383. data/scripts/api_snapshot.rb +47 -15
  384. data/scripts/check_readme_ruby.rb +24 -29
  385. data/scripts/check_readme_runnable.rb +92 -81
  386. data/sig/_private/phronomy/agent/settings.rbs +19 -0
  387. data/sig/_private/phronomy/configuration/runtime_settings.rbs +20 -0
  388. data/sig/_private/phronomy/llm_adapter/async/async_client.rbs +10 -0
  389. data/sig/_private/phronomy/multi_agent/store.rbs +8 -0
  390. data/sig/_private/phronomy/persistence/storage_boundary.rbs +7 -0
  391. data/sig/_private/phronomy/tool/settings.rbs +13 -0
  392. data/sig/_private/phronomy/tracing/settings.rbs +15 -0
  393. data/sig/_private/phronomy/workflow/execution/workflow_settings.rbs +13 -0
  394. data/sig/phronomy/agent/admission.rbs +19 -0
  395. data/sig/phronomy/agent/agent_not_found_error.rbs +6 -0
  396. data/sig/phronomy/agent/agent_unloaded_error.rbs +6 -0
  397. data/sig/phronomy/agent/execution_contract.rbs +132 -0
  398. data/sig/phronomy/agent/execution_environment.rbs +20 -0
  399. data/sig/phronomy/agent/store.rbs +73 -0
  400. data/sig/phronomy/agent/stream_callback_error.rbs +8 -0
  401. data/sig/phronomy/agent.rbs +29 -135
  402. data/sig/phronomy/common.rbs +7 -0
  403. data/sig/phronomy/content_store/contracts.rbs +13 -0
  404. data/sig/phronomy/context.rbs +152 -0
  405. data/sig/phronomy/embeddings/async/async_client.rbs +9 -0
  406. data/sig/phronomy/embeddings/base.rbs +23 -0
  407. data/sig/phronomy/engine/concurrency/timer_bindings.rbs +22 -0
  408. data/sig/phronomy/engine/control_bindings.rbs +11 -0
  409. data/sig/phronomy/engine/control_protocols.rbs +23 -0
  410. data/sig/phronomy/engine/execution_binding.rbs +6 -0
  411. data/sig/phronomy/engine/execution_receiver.rbs +15 -0
  412. data/sig/phronomy/engine/observability.rbs +13 -0
  413. data/sig/phronomy/execution/async_operation.rbs +7 -0
  414. data/sig/phronomy/execution/blocking.rbs +5 -0
  415. data/sig/phronomy/execution/concurrency/cancellation_token.rbs +13 -0
  416. data/sig/phronomy/execution/concurrency/deadline.rbs +10 -0
  417. data/sig/phronomy/execution/concurrency/result_composition.rbs +7 -0
  418. data/sig/phronomy/execution/control_protocols.rbs +18 -0
  419. data/sig/phronomy/execution/errors.rbs +7 -0
  420. data/sig/phronomy/execution/execution.rbs +41 -0
  421. data/sig/phronomy/execution/execution_receiver.rbs +40 -0
  422. data/sig/phronomy/execution/execution_scope_state.rbs +9 -0
  423. data/sig/phronomy/execution/invocation_context.rbs +18 -0
  424. data/sig/phronomy/execution/invocation_controls.rbs +13 -0
  425. data/sig/phronomy/execution/task_result.rbs +29 -0
  426. data/sig/phronomy/extensions.rbs +1 -21
  427. data/sig/phronomy/filter/filter_block_error.rbs +6 -0
  428. data/sig/phronomy/generation/low_confidence_error.rbs +6 -0
  429. data/sig/phronomy/handoff.rbs +10 -4
  430. data/sig/phronomy/integrations.rbs +14 -0
  431. data/sig/phronomy/llm_adapter/backends/ruby_llm.rbs +6 -0
  432. data/sig/phronomy/llm_adapter/base.rbs +14 -0
  433. data/sig/phronomy/llm_adapter/usage.rbs +14 -0
  434. data/sig/phronomy/llm_adapter/values.rbs +43 -0
  435. data/sig/phronomy/llm_context_window.rbs +10 -0
  436. data/sig/phronomy/multi_agent/reserved_child_admission.rbs +8 -0
  437. data/sig/phronomy/multi_agent/store.rbs +54 -0
  438. data/sig/phronomy/multi_agent/team_busy_error.rbs +8 -0
  439. data/sig/phronomy/multi_agent/team_not_found_error.rbs +8 -0
  440. data/sig/phronomy/multi_agent.rbs +6 -3
  441. data/sig/phronomy/persistence/errors.rbs +18 -0
  442. data/sig/phronomy/persistence/migration.rbs +13 -0
  443. data/sig/phronomy/persistence/transaction.rbs +19 -0
  444. data/sig/phronomy/persistence.rbs +1 -99
  445. data/sig/phronomy/persistence_composition.rbs +19 -0
  446. data/sig/phronomy/runtime_composition/configuration.rbs +36 -0
  447. data/sig/phronomy/storage/async/async_client.rbs +8 -0
  448. data/sig/phronomy/storage/backends/in_memory.rbs +9 -0
  449. data/sig/phronomy/{storage.rbs → storage/contracts.rbs} +4 -18
  450. data/sig/phronomy/tool.rbs +48 -4
  451. data/sig/phronomy/tracing/observation.rbs +7 -0
  452. data/sig/phronomy/vector_store/async/async_client.rbs +13 -0
  453. data/sig/phronomy/vector_store/base.rbs +23 -0
  454. data/sig/phronomy/workflow/execution/workflow_execution_environment.rbs +15 -0
  455. data/sig/phronomy/workflow/runtime_binding/workflow_terminal_policy.rbs +7 -0
  456. data/sig/phronomy/workflow/store.rbs +8 -0
  457. data/sig/phronomy/workflow.rbs +5 -1
  458. data/sig/phronomy.rbs +4 -49
  459. metadata +365 -117
  460. data/lib/phronomy/agent/concerns/before_llm_input.rb +0 -66
  461. data/lib/phronomy/agent/concerns/error_translation.rb +0 -45
  462. data/lib/phronomy/agent/concerns/filterable.rb +0 -184
  463. data/lib/phronomy/agent/context/capability/base.rb +0 -548
  464. data/lib/phronomy/agent/context/capability/tool_executor.rb +0 -62
  465. data/lib/phronomy/agent/context/instruction/prompt_template.rb +0 -102
  466. data/lib/phronomy/agent/context_assembly/runtime_chat_builder.rb +0 -36
  467. data/lib/phronomy/agent/context_assembly/token_budget_resolver.rb +0 -22
  468. data/lib/phronomy/agent/context_parts/validators/final_budget_validator.rb +0 -37
  469. data/lib/phronomy/agent/context_policies/default.rb +0 -142
  470. data/lib/phronomy/agent/execution/phase_machine_builder.rb +0 -101
  471. data/lib/phronomy/agent/execution/provider_call_outcome.rb +0 -106
  472. data/lib/phronomy/agent/handoff/handoff_execution_coordinator.rb +0 -15
  473. data/lib/phronomy/agent/handoff/handoff_outcome_committer.rb +0 -131
  474. data/lib/phronomy/agent/handoff/handoff_request.rb +0 -45
  475. data/lib/phronomy/agent/lifecycle_contract/handoff_error.rb +0 -7
  476. data/lib/phronomy/agent/persistence/handoff_state_repository.rb +0 -58
  477. data/lib/phronomy/agent/persistence/queries.rb +0 -61
  478. data/lib/phronomy/agent/selection/candidate.rb +0 -53
  479. data/lib/phronomy/agent/selection/constraint.rb +0 -49
  480. data/lib/phronomy/agent/tool_execution/tool_call_intercepted.rb +0 -29
  481. data/lib/phronomy/agent/tool_execution/tool_invocation_session_builder.rb +0 -276
  482. data/lib/phronomy/configuration/configuration.rb +0 -68
  483. data/lib/phronomy/configuration/global_configuration.rb +0 -26
  484. data/lib/phronomy/engine/concurrency/cancellation_scope.rb +0 -124
  485. data/lib/phronomy/engine/concurrency/operation_binding.rb +0 -43
  486. data/lib/phronomy/engine/execution_receiver.rb +0 -65
  487. data/lib/phronomy/engine/runnable.rb +0 -54
  488. data/lib/phronomy/llm_adapter/ruby_llm.rb +0 -47
  489. data/lib/phronomy/llm_contract/token_usage.rb +0 -85
  490. data/lib/phronomy/multi_agent/persistence/queries.rb +0 -30
  491. data/lib/phronomy/multi_agent/storage_contract/team_storage_schema.rb +0 -15
  492. data/lib/phronomy/output_parser/contract/parse_error.rb +0 -7
  493. data/lib/phronomy/persistence/api/persistence.rb +0 -140
  494. data/lib/phronomy/persistence_composition/repositories.rb +0 -77
  495. data/lib/phronomy/recovery/recovery.rb +0 -186
  496. data/lib/phronomy/tool/contract/tool_error.rb +0 -7
  497. data/lib/phronomy/vector_store/embeddings/base.rb +0 -44
  498. data/lib/phronomy/vector_store/embeddings/ruby_llm_embeddings.rb +0 -47
  499. data/lib/phronomy/workflow/phase_machine_builder.rb +0 -205
  500. data/lib/phronomy/workflow/storage_contract/workflow_storage_schema.rb +0 -9
  501. data/sig/phronomy/execution_receiver.rbs +0 -34
  502. data/sig/phronomy/llm_adapter.rbs +0 -11
  503. data/sig/phronomy/runtime.rbs +0 -76
  504. /data/lib/phronomy/agent/{context_assembly/context_importer.rb → context_importer.rb} +0 -0
  505. /data/lib/phronomy/{engine → execution}/backpressure_error.rb +0 -0
  506. /data/lib/phronomy/{engine → execution}/cancellation_error.rb +0 -0
  507. /data/lib/phronomy/{engine → execution}/concurrency/worker_input_restricted.rb +0 -0
  508. /data/lib/phronomy/{engine → execution}/event_loop_reentrancy_error.rb +0 -0
  509. /data/lib/phronomy/{engine → execution}/invalid_async_entry_action_error.rb +0 -0
  510. /data/lib/phronomy/{engine → execution}/invalid_async_transition_action_error.rb +0 -0
  511. /data/lib/phronomy/{engine → execution}/invalid_async_workflow_action_error.rb +0 -0
  512. /data/lib/phronomy/{engine → execution}/pool_shutdown_error.rb +0 -0
  513. /data/lib/phronomy/{engine → execution}/recursion_limit_error.rb +0 -0
  514. /data/lib/phronomy/{engine → execution}/runtime_shutdown_error.rb +0 -0
  515. /data/lib/phronomy/{engine → execution}/runtime_shutdown_reentrancy_error.rb +0 -0
  516. /data/lib/phronomy/{engine → execution}/scheduler_reentrancy_error.rb +0 -0
  517. /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,188 @@
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
+ __with_operation do
96
+ @_instance_input_filters ||= []
97
+ @_instance_input_filters << _resolve_filter(filter)
98
+ self
99
+ end
100
+ end
101
+
102
+ # Registers an output filter on this instance.
103
+ # Accepts either a {Phronomy::Filter::Base} instance or a subclass;
104
+ # when a class is given it is instantiated with +.new+.
105
+ # Runs in addition to any class-level output filters.
106
+ # @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>]
107
+ # @return [self]
108
+ # @api public
109
+ def add_output_filter(filter)
110
+ __with_operation do
111
+ @_instance_output_filters ||= []
112
+ @_instance_output_filters << _resolve_filter(filter)
113
+ self
114
+ end
115
+ end
116
+
117
+ # Registers a tool result filter on this instance.
118
+ #
119
+ # When called with two arguments, the filter is scoped to the given tool
120
+ # class only. When called with one argument, it applies to all tools.
121
+ # Accepts either a {Phronomy::Filter::Base} instance or a subclass;
122
+ # when a class is given it is instantiated with +.new+.
123
+ #
124
+ # @overload add_tool_result_filter(filter)
125
+ # @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>] applied to every tool
126
+ # @overload add_tool_result_filter(tool_class, filter)
127
+ # @param tool_class [Class] scope the filter to this tool
128
+ # @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>]
129
+ # @return [self]
130
+ # @api public
131
+ def add_tool_result_filter(tool_class_or_filter, filter = nil)
132
+ __with_operation do
133
+ if filter.nil?
134
+ # Single-argument form: apply to all tools.
135
+ @_instance_tool_result_filters ||= []
136
+ @_instance_tool_result_filters << _resolve_filter(tool_class_or_filter)
137
+ else
138
+ # Two-argument form: scoped to one tool class.
139
+ @_scoped_tool_result_filters ||= {}
140
+ (@_scoped_tool_result_filters[tool_class_or_filter] ||= []) << _resolve_filter(filter)
141
+ end
142
+ self
143
+ end
144
+ end
145
+
146
+ private
147
+
148
+ # Run input filters (class-level then instance-level).
149
+ # @param input [String, Hash] the raw user input
150
+ # @return [String, Hash] the (possibly transformed) input
151
+ # @api private
152
+ def run_input_filters!(input)
153
+ class_filters = self.class._class_input_filters
154
+ inst_filters = @_instance_input_filters || []
155
+ (class_filters + inst_filters).inject(input) { |val, f| f.call(val) }
156
+ end
157
+
158
+ # Run output filters (class-level then instance-level).
159
+ # @param output [String] the LLM output
160
+ # @return [String] the (possibly transformed) output
161
+ # @api private
162
+ def run_output_filters!(output)
163
+ class_filters = self.class._class_output_filters
164
+ inst_filters = @_instance_output_filters || []
165
+ (class_filters + inst_filters).inject(output) { |val, f| f.call(val) }
166
+ end
167
+
168
+ # Collect all tool-result filters (global + scoped) for a given tool class.
169
+ # @param tool_class [Class]
170
+ # @return [Array<Phronomy::Filter::Base>]
171
+ # @api private
172
+ def _tool_result_filters_for(tool_class)
173
+ global = self.class._class_tool_result_filters + (@_instance_tool_result_filters || [])
174
+ scoped = (@_scoped_tool_result_filters || {})[tool_class] || []
175
+ global + scoped
176
+ end
177
+
178
+ # Coerce +filter+ to an instance: if a Class is passed, call +.new+;
179
+ # otherwise return the object as-is.
180
+ # @param filter [Phronomy::Filter::Base, Class<Phronomy::Filter::Base>]
181
+ # @return [Phronomy::Filter::Base]
182
+ # @api private
183
+ def _resolve_filter(filter)
184
+ filter.is_a?(Class) ? filter.new : filter
185
+ end
186
+ end
187
+ end
188
+ 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
 
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Agent
5
+ # Public admission boundaries. The explicit operation capability belongs to
6
+ # C04, including queued work and domain completion after logical cancellation.
7
+ # @api private
8
+ module LifetimeApi
9
+ # Releases only the Runtime incarnation; durable state and Store stay intact.
10
+ # @return [TaskResult<Boolean>]
11
+ # @api public
12
+ def unload_async
13
+ return Phronomy::TaskResult.completed(false) if @_phronomy_runtime_owner_state == :unloaded
14
+
15
+ __assert_agent_accessible!
16
+ transition, start = __execution_environment.ownership.reserve_unload(self)
17
+ start ? transition.start : transition.result
18
+ rescue => error
19
+ Phronomy::TaskResult.failed(error)
20
+ end
21
+
22
+ # A timeout stops this wait, never the accepted release.
23
+ # @return [Boolean]
24
+ # @api public
25
+ def unload!(timeout: nil)
26
+ _check_event_loop_reentrancy(:unload!, :unload_async)
27
+ unload_async.wait_result(timeout: timeout)
28
+ end
29
+
30
+ private
31
+
32
+ def __with_operation(async: false, read_only: false)
33
+ lease = __acquire_operation!(read_only: read_only)
34
+ followed = false
35
+ value = yield
36
+ if async && lease
37
+ lease.follow(value)
38
+ followed = true
39
+ end
40
+ value
41
+ rescue Phronomy::AgentUnloadedError, Phronomy::AgentBusyError, Phronomy::RuntimeShutdownError => error
42
+ raise unless async
43
+ Phronomy::TaskResult.failed(error)
44
+ ensure
45
+ lease&.release unless followed
46
+ end
47
+
48
+ # Accepted C04 commands carry their admission/operation capability. Their
49
+ # continuations read the snapshot without entering the public admission gate.
50
+ def __root_for_operation
51
+ __assert_agent_accessible!
52
+ @root
53
+ end
54
+
55
+ def __acquire_operation!(read_only: false)
56
+ __assert_agent_accessible!
57
+ return nil if @_phronomy_runtime_owner_state == :constructing
58
+
59
+ __execution_environment.ownership.acquire_operation(self, read_only: read_only)
60
+ end
61
+
62
+ def __mark_unloaded!
63
+ @_phronomy_runtime_owner_state = :unloaded
64
+ @root = nil
65
+ @_phronomy_journal_records = [].freeze
66
+ @_phronomy_event_listener = nil
67
+ @_phronomy_execution_wiring = {}.freeze
68
+ @execution_coordinator = nil
69
+ @tool_approval_policy = @before_llm_input = nil
70
+ @_instance_input_filters = @_instance_output_filters = nil
71
+ @_instance_tool_result_filters = @_scoped_tool_result_filters = nil
72
+ @initial_retention = nil
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Agent
5
+ # C04 capability for accepted work. Never inferred from the current thread.
6
+ # @api private
7
+ class OperationLease
8
+ def initialize(registry, agent, token)
9
+ @registry, @agent, @token = registry, agent, token
10
+ @mutex = Mutex.new
11
+ end
12
+
13
+ def release
14
+ ownership = @mutex.synchronize do
15
+ next unless @registry
16
+ current = [@registry, @agent, @token]
17
+ @registry = @agent = @token = nil
18
+ current
19
+ end
20
+ ownership&.first&.release_operation(ownership[1], ownership[2])
21
+ end
22
+
23
+ def follow(task)
24
+ if task.respond_to?(:on_physical_complete)
25
+ task.on_physical_complete { release }
26
+ else
27
+ task.on_complete { |_value, _error| release }
28
+ end
29
+ task
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Agent
5
+ # Logical cancellation of a public handle does not release Agent ownership.
6
+ # Only the domain runner acknowledges the end of its accepted operation.
7
+ # @api private
8
+ class OperationResult < Phronomy::Concurrency::PhysicalCompletionTask
9
+ def initialize(agent:, name:)
10
+ super(name: name)
11
+ @operation_lease = agent.send(:__acquire_operation!)
12
+ @lifetime_mutex = Mutex.new
13
+ @work = 0
14
+ @finished = false
15
+ @released = false
16
+ end
17
+
18
+ def finish_operation!
19
+ @lifetime_mutex.synchronize { @finished = true }
20
+ release_if_quiet
21
+ end
22
+
23
+ def track(task)
24
+ @lifetime_mutex.synchronize { @work += 1 }
25
+ task.on_physical_complete do
26
+ @lifetime_mutex.synchronize { @work -= 1 }
27
+ release_if_quiet
28
+ end
29
+ task
30
+ end
31
+
32
+ def settle(value = nil, error: nil)
33
+ finish_operation!
34
+ error ? fail(error) : complete(value)
35
+ end
36
+
37
+ private
38
+
39
+ def release_if_quiet
40
+ released = @lifetime_mutex.synchronize do
41
+ next unless @finished && @work.zero? && !@released
42
+ @released = true
43
+ current = @operation_lease
44
+ @operation_lease = nil
45
+ [current]
46
+ end
47
+ return unless released
48
+ released.first&.release
49
+ mark_physical_complete!
50
+ end
51
+ end
52
+ end
53
+ end