phronomy 0.26.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (557) hide show
  1. checksums.yaml +4 -4
  2. data/.mutant.yml +9 -9
  3. data/CHANGELOG.md +92 -0
  4. data/CONTRIBUTING.md +8 -8
  5. data/README.md +13 -3
  6. data/VERIFY.sh +27 -27
  7. data/benchmark/bench_agent_invoke.rb +26 -47
  8. data/benchmark/bench_context_assembler.rb +17 -15
  9. data/benchmark/bench_regression.rb +1 -1
  10. data/benchmark/bench_tool_schema.rb +1 -1
  11. data/docs/architecture/agent-chat-and-state-ownership.md +153 -0
  12. data/docs/architecture/agent-configuration-and-tool-binding.md +139 -0
  13. data/docs/architecture/agent-context.md +4 -2
  14. data/docs/architecture/agent-transition-ownership.md +13 -0
  15. data/docs/architecture/backend-groups.md +152 -0
  16. data/docs/architecture/before-llm-input.md +9 -3
  17. data/docs/architecture/context-management.md +31 -1
  18. data/docs/architecture/context-preparation-steps.md +90 -0
  19. data/docs/architecture/entry-action-and-team-wording.md +72 -0
  20. data/docs/architecture/execution-abstraction-review.md +88 -0
  21. data/docs/architecture/execution-boundaries.md +28 -0
  22. data/docs/architecture/execution-metadata-and-values.md +86 -0
  23. data/docs/architecture/generator-verifier-ownership.md +111 -0
  24. data/docs/architecture/multi-agent-handoff.md +11 -4
  25. data/docs/architecture/persistence-refactoring-plan.md +50 -0
  26. data/docs/architecture/persistence.md +45 -4
  27. data/docs/architecture/r8-unit1.md +104 -0
  28. data/docs/architecture/r8-unit10.md +72 -0
  29. data/docs/architecture/r8-unit11.md +71 -0
  30. data/docs/architecture/r8-unit12.md +67 -0
  31. data/docs/architecture/r8-unit13.md +61 -0
  32. data/docs/architecture/r8-unit14.md +72 -0
  33. data/docs/architecture/r8-unit15.md +65 -0
  34. data/docs/architecture/r8-unit16.md +54 -0
  35. data/docs/architecture/r8-unit17.md +73 -0
  36. data/docs/architecture/r8-unit18.md +92 -0
  37. data/docs/architecture/r8-unit19.md +61 -0
  38. data/docs/architecture/r8-unit2.md +55 -0
  39. data/docs/architecture/r8-unit20.md +51 -0
  40. data/docs/architecture/r8-unit21.md +79 -0
  41. data/docs/architecture/r8-unit22.md +74 -0
  42. data/docs/architecture/r8-unit23.md +81 -0
  43. data/docs/architecture/r8-unit3.md +130 -0
  44. data/docs/architecture/r8-unit4.md +79 -0
  45. data/docs/architecture/r8-unit5.md +115 -0
  46. data/docs/architecture/r8-unit6.md +71 -0
  47. data/docs/architecture/r8-unit7.md +70 -0
  48. data/docs/architecture/r8-unit8.md +78 -0
  49. data/docs/architecture/r8-unit9.md +73 -0
  50. data/docs/architecture/refactoring-closure.md +343 -0
  51. data/docs/architecture/remaining-refactoring-plan.md +390 -0
  52. data/docs/architecture/rubyllm-2-token-ownership.md +82 -0
  53. data/docs/architecture/storage-execution-boundaries.md +54 -0
  54. data/docs/architecture/tool-schema-recording-gap.md +56 -0
  55. data/docs/architecture/tracing.md +2 -2
  56. data/docs/architecture/workflow-terminal-ownership-design.md +104 -0
  57. data/docs/architecture.md +222 -0
  58. data/docs/decisions/014-unified-persistence-durable-state.md +11 -0
  59. data/docs/decisions/015-tool-public-facade-and-rbs-boundary.md +3 -3
  60. data/docs/decisions/024-event-loop-single-writer-agent-runtime.md +5 -1
  61. data/docs/decisions/025-process-local-agent-ownership-and-runtime-admission.md +13 -3
  62. data/docs/decisions/026-workflow-runtime-admission-and-durable-terminal-barrier.md +5 -1
  63. data/docs/decisions/027-llm-adapter-provider-boundary.md +2 -0
  64. data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +7 -0
  65. data/docs/decisions/031-durable-multi-agent-coordination.md +7 -0
  66. data/docs/decisions/032-storage-backend-composition.md +85 -0
  67. data/docs/decisions/033-domain-persistence-ownership.md +79 -0
  68. data/docs/decisions/034-handoff-runner-coordination-ownership.md +78 -0
  69. data/docs/decisions/035-tool-executor-capability-ownership.md +68 -0
  70. data/docs/decisions/036-context-contract-ownership.md +86 -0
  71. data/docs/decisions/037-common-definition-ownership.md +62 -0
  72. data/docs/decisions/038-responsibility-based-source-layout.md +145 -0
  73. data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +82 -0
  74. data/docs/decisions/040-configuration-default-composition.md +105 -0
  75. data/docs/decisions/041-feature-owned-identity-registries.md +100 -0
  76. data/docs/decisions/042-feature-owned-execution-state.md +106 -0
  77. data/docs/decisions/043-storage-execution-constraint-notifications.md +75 -0
  78. data/docs/decisions/044-agent-default-and-one-shot-composition.md +106 -0
  79. data/docs/decisions/045-worker-input-restriction-ownership.md +104 -0
  80. data/docs/decisions/046-agent-responsibility-layout-and-shared-records.md +110 -0
  81. data/docs/decisions/047-recovered-execution-continuation-contract.md +116 -0
  82. data/docs/decisions/048-dispatch-preparation-worker-ownership.md +120 -0
  83. data/docs/decisions/049-initial-preparation-worker-ownership.md +111 -0
  84. data/docs/decisions/050-approval-resume-snapshot-and-commit-ownership.md +98 -0
  85. data/docs/decisions/051-execution-outcome-worker-ownership.md +153 -0
  86. data/docs/decisions/052-tool-invocation-restoration-ownership.md +92 -0
  87. data/docs/decisions/053-shared-state-coordination-ownership.md +71 -0
  88. data/docs/decisions/054-workflow-terminal-save-single-owner.md +76 -0
  89. data/docs/decisions/055-terminal-observer-failure-settlement.md +60 -0
  90. data/docs/decisions/056-workflow-terminal-policy-ownership.md +87 -0
  91. data/docs/decisions/057-storage-transaction-boundaries.md +73 -0
  92. data/docs/decisions/058-neutral-storage-primitives.md +83 -0
  93. data/docs/decisions/059-backend-contracts-and-async-clients.md +74 -0
  94. data/docs/decisions/060-runtime-settings-and-application-configuration.md +86 -0
  95. data/docs/decisions/061-persistence-failure-contracts.md +89 -0
  96. data/docs/decisions/062-content-store-service.md +52 -0
  97. data/docs/decisions/063-execution-contracts-and-services.md +88 -0
  98. data/docs/decisions/064-agent-execution-and-multi-agent-responsibility.md +36 -0
  99. data/docs/decisions/065-vector-and-embeddings-common-operations.md +38 -0
  100. data/docs/decisions/066-llm-and-tool-operation-contracts.md +20 -0
  101. data/docs/decisions/067-agent-progress-and-engine-connection.md +29 -0
  102. data/docs/decisions/068-agent-tool-child-progress-and-tool-operation.md +36 -0
  103. data/docs/decisions/README.md +52 -6
  104. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_DESIGN_V2.md +6 -0
  105. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md +6 -0
  106. data/docs/execution-boundaries-preview.md +45 -0
  107. data/docs/features.md +16 -5
  108. data/docs/getting-started.md +11 -8
  109. data/docs/migrations/0.15.md +5 -0
  110. data/docs/migrations/0.22.md +2 -2
  111. data/docs/migrations/durable-semantic-coordination-v2.md +8 -0
  112. data/docs/migrations/handoff-runner-multi-agent.md +37 -0
  113. data/docs/migrations/neutral-storage-spi.md +43 -0
  114. data/docs/migrations/parallel-tool-chat-removal.md +49 -0
  115. data/docs/migrations/persistence-failure-contracts.md +67 -0
  116. data/docs/migrations/r8-unit10.md +36 -0
  117. data/docs/migrations/r8-unit11.md +33 -0
  118. data/docs/migrations/r8-unit12.md +31 -0
  119. data/docs/migrations/r8-unit13.md +36 -0
  120. data/docs/migrations/r8-unit14.md +31 -0
  121. data/docs/migrations/r8-unit15.md +31 -0
  122. data/docs/migrations/r8-unit16.md +26 -0
  123. data/docs/migrations/r8-unit17.md +28 -0
  124. data/docs/migrations/r8-unit18.md +29 -0
  125. data/docs/migrations/r8-unit19.md +28 -0
  126. data/docs/migrations/r8-unit20.md +24 -0
  127. data/docs/migrations/r8-unit21.md +28 -0
  128. data/docs/migrations/r8-unit22.md +29 -0
  129. data/docs/migrations/r8-unit23.md +20 -0
  130. data/docs/migrations/r8-unit4.md +67 -0
  131. data/docs/migrations/r8-unit5.md +75 -0
  132. data/docs/migrations/r8-unit6.md +109 -0
  133. data/docs/migrations/r8-unit7.md +31 -0
  134. data/docs/migrations/r8-unit8.md +33 -0
  135. data/docs/migrations/r8-unit9.md +28 -0
  136. data/docs/migrations/shared-state-multi-agent.md +44 -0
  137. data/docs/migrations/storage-async-client.md +103 -0
  138. data/docs/migrations/storage-backend-composition.md +117 -0
  139. data/docs/migrations/storage-transaction-boundaries.md +74 -0
  140. data/docs/migrations/vector-async-clients.md +110 -0
  141. data/docs/persistence-backends.md +190 -215
  142. data/docs/runtime-and-concurrency.md +142 -32
  143. data/lib/phronomy/agent/admission.rb +78 -0
  144. data/lib/phronomy/{agent_already_exists_error.rb → agent/agent_already_exists_error.rb} +2 -0
  145. data/lib/phronomy/{agent_busy_error.rb → agent/agent_busy_error.rb} +2 -0
  146. data/lib/phronomy/{agent_purged_error.rb → agent/agent_purged_error.rb} +2 -0
  147. data/lib/phronomy/agent/api/agent.rb +17 -0
  148. data/lib/phronomy/{execution_rehydration_required_error.rb → agent/api/execution_rehydration_required_error.rb} +2 -0
  149. data/lib/phronomy/agent/async_event_api.rb +38 -33
  150. data/lib/phronomy/agent/base.rb +106 -294
  151. data/lib/phronomy/agent/before_llm_input.rb +64 -0
  152. data/lib/phronomy/{agent.rb → agent/composition/run_once.rb} +4 -13
  153. data/lib/phronomy/agent/context_candidate_resolver.rb +2 -2
  154. data/lib/phronomy/agent/context_importer.rb +2 -2
  155. data/lib/phronomy/agent/{context_assembler.rb → context_preparation.rb} +157 -288
  156. data/lib/phronomy/agent/control_request.rb +21 -0
  157. data/lib/phronomy/agent/{agent_execution.rb → execution/agent_execution.rb} +8 -4
  158. data/lib/phronomy/agent/{agent_invocation.rb → execution/agent_invocation.rb} +32 -63
  159. data/lib/phronomy/agent/execution/approval_resume_commit.rb +108 -0
  160. data/lib/phronomy/agent/execution/dispatch_preparation.rb +195 -0
  161. data/lib/phronomy/agent/{exact_execution.rb → execution/exact_execution.rb} +43 -23
  162. data/lib/phronomy/agent/{execution_cancellation.rb → execution/execution_cancellation.rb} +8 -5
  163. data/lib/phronomy/agent/execution/execution_coordinator.rb +1744 -0
  164. data/lib/phronomy/agent/execution/execution_failure.rb +30 -0
  165. data/lib/phronomy/agent/execution/execution_metadata.rb +54 -0
  166. data/lib/phronomy/agent/execution/execution_outcome_committer.rb +288 -0
  167. data/lib/phronomy/agent/execution/execution_registry.rb +463 -0
  168. data/lib/phronomy/agent/execution/execution_session_runner.rb +113 -0
  169. data/lib/phronomy/agent/execution/initial_preparation.rb +323 -0
  170. data/lib/phronomy/agent/execution/invocation_actions.rb +368 -0
  171. data/lib/phronomy/agent/execution/invocation_transitions.rb +124 -0
  172. data/lib/phronomy/agent/execution/runtime_record_encoder.rb +210 -0
  173. data/lib/phronomy/agent/execution_change.rb +161 -0
  174. data/lib/phronomy/agent/execution_environment.rb +36 -0
  175. data/lib/phronomy/agent/execution_extension_state.rb +21 -0
  176. data/lib/phronomy/agent/execution_observation.rb +35 -0
  177. data/lib/phronomy/agent/filterable.rb +182 -0
  178. data/lib/phronomy/agent/{handoff_context.rb → handoff/transfer_context.rb} +6 -6
  179. data/lib/phronomy/agent/{handoff_projection.rb → handoff/transfer_projection.rb} +14 -33
  180. data/lib/phronomy/agent/handoff_error.rb +7 -0
  181. data/lib/phronomy/agent/{journal_projection.rb → journal/journal_projection.rb} +4 -0
  182. data/lib/phronomy/agent/{journal_record.rb → journal/journal_record.rb} +3 -3
  183. data/lib/phronomy/agent/{llm_call_record.rb → journal/llm_call_record.rb} +2 -2
  184. data/lib/phronomy/agent/knowledge_item.rb +13 -0
  185. data/lib/phronomy/agent/{agent_root.rb → lifecycle/agent_root.rb} +6 -2
  186. data/lib/phronomy/agent/lifecycle/default_persistence.rb +29 -0
  187. data/lib/phronomy/{engine/runtime/agent_ownership_registry.rb → agent/lifecycle/ownership_registry.rb} +24 -15
  188. data/lib/phronomy/agent/persistence/agent_repository.rb +69 -0
  189. data/lib/phronomy/agent/persistence/cancellation_repository.rb +51 -0
  190. data/lib/phronomy/agent/persistence/codec.rb +338 -0
  191. data/lib/phronomy/agent/persistence/execution_repository.rb +124 -0
  192. data/lib/phronomy/agent/persistence/journal_repository.rb +63 -0
  193. data/lib/phronomy/agent/persistence/records.rb +33 -0
  194. data/lib/phronomy/agent/persistence/retention_repository.rb +70 -0
  195. data/lib/phronomy/agent/persistence/storage_schema.rb +29 -0
  196. data/lib/phronomy/agent/persistence/watermark.rb +29 -0
  197. data/lib/phronomy/agent/recovery/invocation_restorer.rb +124 -0
  198. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/continuation.rb +28 -51
  199. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/installation.rb +50 -55
  200. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/resolution.rb +38 -43
  201. data/lib/phronomy/agent/{recovery_coordinator.rb → recovery/recovery_coordinator.rb} +26 -23
  202. data/lib/phronomy/agent/recovery/recovery_rules.rb +119 -0
  203. data/lib/phronomy/agent/recovery/recovery_support.rb +224 -0
  204. data/lib/phronomy/agent/reserved_execution.rb +16 -0
  205. data/lib/phronomy/agent/retention.rb +17 -0
  206. data/lib/phronomy/agent/runtime_binding/engine_environment.rb +60 -0
  207. data/lib/phronomy/agent/runtime_binding/engine_session_builder.rb +34 -0
  208. data/lib/phronomy/agent/runtime_binding/phase_machine_builder.rb +64 -0
  209. data/lib/phronomy/agent/runtime_binding/tool_session_builder.rb +34 -0
  210. data/lib/phronomy/agent/{ruby_llm_materializer.rb → runtime_input.rb} +19 -39
  211. data/lib/phronomy/agent/saved_context_reader.rb +55 -0
  212. data/lib/phronomy/agent/settings.rb +47 -0
  213. data/lib/phronomy/agent/state_writer.rb +166 -0
  214. data/lib/phronomy/agent/store.rb +253 -0
  215. data/lib/phronomy/{stream_callback_error.rb → agent/stream_callback_error.rb} +2 -0
  216. data/lib/phronomy/agent/{approval_evaluation_request.rb → tool_execution/approval_evaluation_request.rb} +1 -12
  217. data/lib/phronomy/agent/{tool_approval_request.rb → tool_execution/tool_approval_request.rb} +1 -10
  218. data/lib/phronomy/agent/tool_execution/tool_binding.rb +30 -0
  219. data/lib/phronomy/agent/{tool_invocation.rb → tool_execution/tool_invocation.rb} +114 -248
  220. data/lib/phronomy/agent/tool_execution/tool_invocation_actions.rb +107 -0
  221. data/lib/phronomy/agent/tool_execution/tool_invocation_transitions.rb +70 -0
  222. data/lib/phronomy/common/configuration_error.rb +7 -0
  223. data/lib/phronomy/common/error.rb +5 -0
  224. data/lib/phronomy/{agent → common/values}/immutable.rb +9 -1
  225. data/lib/phronomy/common/values/serializable.rb +32 -0
  226. data/lib/phronomy/configuration/runtime_settings.rb +49 -0
  227. data/lib/phronomy/content_store/storage_schema.rb +12 -0
  228. data/lib/phronomy/content_store/stored_contents.rb +44 -0
  229. data/lib/phronomy/context/assembly.rb +202 -0
  230. data/lib/phronomy/{context_budget_exceeded_error.rb → context/budget_exceeded_error.rb} +5 -1
  231. data/lib/phronomy/context/candidate.rb +51 -0
  232. data/lib/phronomy/context/constraint.rb +47 -0
  233. data/lib/phronomy/{agent → context}/context_plan.rb +2 -2
  234. data/lib/phronomy/{agent → context}/context_plan_validator.rb +3 -3
  235. data/lib/phronomy/{agent → context}/context_policy.rb +1 -1
  236. data/lib/phronomy/{agent → context}/context_policy_input.rb +7 -7
  237. data/lib/phronomy/{agent → context}/context_policy_input_builder.rb +1 -1
  238. data/lib/phronomy/context/default_policy.rb +140 -0
  239. data/lib/phronomy/context/final_budget_validator.rb +33 -0
  240. data/lib/phronomy/{agent → context}/llm_input_build_context.rb +2 -2
  241. data/lib/phronomy/{agent → context}/llm_input_manifest.rb +21 -21
  242. data/lib/phronomy/{agent → context}/llm_input_patch.rb +3 -3
  243. data/lib/phronomy/context/manifest_error.rb +10 -0
  244. data/lib/phronomy/context/prompt_template.rb +98 -0
  245. data/lib/phronomy/{vector_store → documents}/loader/base.rb +1 -1
  246. data/lib/phronomy/{vector_store → documents}/loader/csv_loader.rb +2 -2
  247. data/lib/phronomy/{vector_store → documents}/loader/markdown_loader.rb +3 -3
  248. data/lib/phronomy/{vector_store → documents}/loader/plain_text_loader.rb +2 -2
  249. data/lib/phronomy/{vector_store → documents}/splitter/base.rb +1 -1
  250. data/lib/phronomy/{vector_store → documents}/splitter/fixed_size_splitter.rb +2 -2
  251. data/lib/phronomy/{vector_store → documents}/splitter/recursive_splitter.rb +2 -2
  252. data/lib/phronomy/embeddings/async/async_client.rb +44 -0
  253. data/lib/phronomy/embeddings/backends/ruby_llm_embeddings.rb +48 -0
  254. data/lib/phronomy/embeddings/base.rb +43 -0
  255. data/lib/phronomy/embeddings/invalid_result_error.rb +11 -0
  256. data/lib/phronomy/embeddings/transport_error.rb +11 -0
  257. data/lib/phronomy/engine/concurrency/offload_pool.rb +39 -20
  258. data/lib/phronomy/engine/concurrency/pool_registry.rb +47 -12
  259. data/lib/phronomy/{diagnostics.rb → engine/diagnostics.rb} +1 -1
  260. data/lib/phronomy/engine/event_loop.rb +164 -630
  261. data/lib/phronomy/engine/execution_binding.rb +23 -0
  262. data/lib/phronomy/engine/execution_receiver_binding.rb +43 -0
  263. data/lib/phronomy/engine/fsm_protocol.rb +9 -0
  264. data/lib/phronomy/engine/fsm_session.rb +37 -30
  265. data/lib/phronomy/engine/metrics.rb +28 -0
  266. data/lib/phronomy/engine/runtime/timer_queue.rb +1 -1
  267. data/lib/phronomy/engine/runtime.rb +104 -95
  268. data/lib/phronomy/engine/terminal_decision.rb +7 -0
  269. data/lib/phronomy/execution/async_operation.rb +41 -0
  270. data/lib/phronomy/execution/backpressure_error.rb +7 -0
  271. data/lib/phronomy/{blocking.rb → execution/blocking.rb} +2 -2
  272. data/lib/phronomy/execution/cancellation_error.rb +7 -0
  273. data/lib/phronomy/{engine → execution}/concurrency/cancellation_token.rb +5 -1
  274. data/lib/phronomy/{engine → execution}/concurrency/deadline.rb +0 -17
  275. data/lib/phronomy/{engine → execution}/concurrency/physical_completion_task.rb +1 -1
  276. data/lib/phronomy/{engine → execution}/concurrency/result_collector.rb +1 -1
  277. data/lib/phronomy/{engine → execution}/concurrency/result_composition.rb +33 -12
  278. data/lib/phronomy/{engine/concurrency/subscriptions.rb → execution/concurrency/result_subscriptions.rb} +14 -18
  279. data/lib/phronomy/execution/concurrency/subscriptions.rb +25 -0
  280. data/lib/phronomy/execution/concurrency/worker_input_restricted.rb +14 -0
  281. data/lib/phronomy/execution/event_loop_reentrancy_error.rb +8 -0
  282. data/lib/phronomy/{execution.rb → execution/execution.rb} +57 -30
  283. data/lib/phronomy/{execution_cancellation_error.rb → execution/execution_cancellation_error.rb} +2 -0
  284. data/lib/phronomy/execution/execution_receiver.rb +78 -0
  285. data/lib/phronomy/execution/execution_scope_state.rb +35 -0
  286. data/lib/phronomy/{execution_timeout_error.rb → execution/execution_timeout_error.rb} +2 -0
  287. data/lib/phronomy/{invalid_async_entry_action_error.rb → execution/invalid_async_entry_action_error.rb} +2 -0
  288. data/lib/phronomy/{invalid_async_transition_action_error.rb → execution/invalid_async_transition_action_error.rb} +2 -0
  289. data/lib/phronomy/{invalid_async_workflow_action_error.rb → execution/invalid_async_workflow_action_error.rb} +2 -0
  290. data/lib/phronomy/{invocation_context.rb → execution/invocation_context.rb} +0 -15
  291. data/lib/phronomy/execution/invocation_controls.rb +73 -0
  292. data/lib/phronomy/execution/pool_shutdown_error.rb +7 -0
  293. data/lib/phronomy/execution/recursion_limit_error.rb +7 -0
  294. data/lib/phronomy/execution/runnable.rb +24 -0
  295. data/lib/phronomy/execution/runtime_shutdown_error.rb +7 -0
  296. data/lib/phronomy/execution/runtime_shutdown_reentrancy_error.rb +7 -0
  297. data/lib/phronomy/execution/scheduler_reentrancy_error.rb +9 -0
  298. data/lib/phronomy/{engine → execution}/task_result.rb +7 -3
  299. data/lib/phronomy/execution/timeout_error.rb +7 -0
  300. data/lib/phronomy/execution/wait_policy.rb +24 -0
  301. data/lib/phronomy/filter/filter_block_error.rb +14 -0
  302. data/lib/phronomy/generation/generator_verifier/agent_operation.rb +65 -0
  303. data/lib/phronomy/generation/generator_verifier/agent_result_receiver.rb +89 -0
  304. data/lib/phronomy/generation/generator_verifier/default_parser.rb +27 -0
  305. data/lib/phronomy/generation/generator_verifier/pipeline_state.rb +57 -0
  306. data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +112 -0
  307. data/lib/phronomy/generation/generator_verifier.rb +118 -0
  308. data/lib/phronomy/generation/low_confidence_error.rb +14 -0
  309. data/lib/phronomy/llm_adapter/async/async_client.rb +47 -0
  310. data/lib/phronomy/llm_adapter/authentication_error.rb +9 -0
  311. data/lib/phronomy/llm_adapter/backends/ruby_llm.rb +183 -0
  312. data/lib/phronomy/llm_adapter/base.rb +49 -68
  313. data/lib/phronomy/llm_adapter/context_length_error.rb +9 -0
  314. data/lib/phronomy/llm_adapter/invalid_result_error.rb +10 -0
  315. data/lib/phronomy/llm_adapter/message.rb +62 -0
  316. data/lib/phronomy/llm_adapter/rate_limit_error.rb +9 -0
  317. data/lib/phronomy/llm_adapter/request.rb +79 -0
  318. data/lib/phronomy/llm_adapter/response.rb +36 -0
  319. data/lib/phronomy/llm_adapter/stream_chunk.rb +18 -0
  320. data/lib/phronomy/llm_adapter/token_usage.rb +64 -0
  321. data/lib/phronomy/llm_adapter/transport_error.rb +9 -0
  322. data/lib/phronomy/llm_adapter.rb +3 -16
  323. data/lib/phronomy/llm_context_window/token_budget.rb +6 -7
  324. data/lib/phronomy/multi_agent/admission_registry.rb +15 -2
  325. data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +108 -84
  326. data/lib/phronomy/multi_agent/execution_environment.rb +29 -0
  327. data/lib/phronomy/{agent → multi_agent}/handoff.rb +2 -2
  328. data/lib/phronomy/{agent → multi_agent}/handoff_capability_factory.rb +21 -2
  329. data/lib/phronomy/multi_agent/handoff_participant.rb +79 -0
  330. data/lib/phronomy/{agent → multi_agent}/handoff_policy.rb +1 -1
  331. data/lib/phronomy/multi_agent/handoff_runner.rb +156 -0
  332. data/lib/phronomy/{agent → multi_agent}/handoff_state.rb +2 -2
  333. data/lib/phronomy/multi_agent/orchestrator.rb +61 -75
  334. data/lib/phronomy/multi_agent/persistence/codec.rb +75 -0
  335. data/lib/phronomy/multi_agent/persistence/handoff_state_repository.rb +77 -0
  336. data/lib/phronomy/multi_agent/persistence/records.rb +27 -0
  337. data/lib/phronomy/multi_agent/persistence/storage_schema.rb +21 -0
  338. data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +124 -0
  339. data/lib/phronomy/multi_agent/persistence/team_repository.rb +69 -0
  340. data/lib/phronomy/multi_agent/reserved_child_admission.rb +88 -0
  341. data/lib/phronomy/multi_agent/runtime_binding/engine_environment.rb +35 -0
  342. data/lib/phronomy/{agent → multi_agent}/shared_state.rb +56 -39
  343. data/lib/phronomy/multi_agent/store.rb +156 -0
  344. data/lib/phronomy/multi_agent/team_coordinator.rb +87 -75
  345. data/lib/phronomy/multi_agent/team_execution.rb +1 -1
  346. data/lib/phronomy/{engine/runtime → multi_agent}/team_ownership_registry.rb +5 -5
  347. data/lib/phronomy/multi_agent/team_root.rb +1 -1
  348. data/lib/phronomy/output_parser/parse_error.rb +7 -0
  349. data/lib/phronomy/persistence/content_repository.rb +26 -0
  350. data/lib/phronomy/persistence/errors.rb +16 -0
  351. data/lib/phronomy/persistence/migration/initial_format_migration.rb +21 -21
  352. data/lib/phronomy/persistence/persistence.rb +23 -0
  353. data/lib/phronomy/persistence/save_outcome.rb +24 -0
  354. data/lib/phronomy/persistence/snapshot_comparison.rb +81 -0
  355. data/lib/phronomy/persistence/storage_boundary.rb +32 -0
  356. data/lib/phronomy/persistence/transaction.rb +86 -0
  357. data/lib/phronomy/persistence_composition/storage_schema.rb +29 -0
  358. data/lib/phronomy/persistence_composition/stores.rb +34 -0
  359. data/lib/phronomy/persistence_composition/unit4_migration.rb +276 -0
  360. data/lib/phronomy/runtime_composition/agent_defaults.rb +11 -0
  361. data/lib/phronomy/runtime_composition/configuration.rb +146 -0
  362. data/lib/phronomy/runtime_composition/configuration_defaults.rb +9 -0
  363. data/lib/phronomy/runtime_composition/evaluation_defaults.rb +7 -0
  364. data/lib/phronomy/runtime_composition/execution_defaults.rb +5 -0
  365. data/lib/phronomy/runtime_composition/generation_defaults.rb +6 -0
  366. data/lib/phronomy/runtime_composition/global_configuration.rb +38 -0
  367. data/lib/phronomy/runtime_composition/global_runtime.rb +19 -0
  368. data/lib/phronomy/runtime_composition/multi_agent_defaults.rb +9 -0
  369. data/lib/phronomy/runtime_composition/workflow_defaults.rb +4 -0
  370. data/lib/phronomy/storage/async/async_client.rb +60 -0
  371. data/lib/phronomy/storage/backend.rb +107 -0
  372. data/lib/phronomy/storage/backends/in_memory.rb +157 -0
  373. data/lib/phronomy/storage/blob_conflict_error.rb +10 -0
  374. data/lib/phronomy/storage/blobs.rb +31 -0
  375. data/lib/phronomy/storage/condition.rb +25 -0
  376. data/lib/phronomy/storage/condition_failed_error.rb +16 -0
  377. data/lib/phronomy/storage/conflict_error.rb +9 -0
  378. data/lib/phronomy/{persistence → storage}/durable_record.rb +12 -12
  379. data/lib/phronomy/storage/entry.rb +33 -0
  380. data/lib/phronomy/storage/guard_ref.rb +13 -0
  381. data/lib/phronomy/storage/not_found_error.rb +9 -0
  382. data/lib/phronomy/storage/record_codec.rb +177 -0
  383. data/lib/phronomy/storage/records.rb +61 -0
  384. data/lib/phronomy/storage/resource.rb +126 -0
  385. data/lib/phronomy/storage/scope.rb +25 -0
  386. data/lib/phronomy/storage/serialization_error.rb +9 -0
  387. data/lib/phronomy/storage/streams.rb +44 -0
  388. data/lib/phronomy/storage/transaction_error.rb +10 -0
  389. data/lib/phronomy/storage/unique_constraint_error.rb +17 -0
  390. data/lib/phronomy/storage/unsupported_backend_error.rb +9 -0
  391. data/lib/phronomy/storage/validation.rb +53 -0
  392. data/lib/phronomy/storage/view.rb +131 -0
  393. data/lib/phronomy/testing/eval/metrics.rb +1 -1
  394. data/lib/phronomy/testing/eval/scorer/llm_judge.rb +19 -3
  395. data/lib/phronomy/testing/persistence_contract/a_content_store.rb +1 -1
  396. data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +2 -2
  397. data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +54 -48
  398. data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +1 -1
  399. data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +1 -1
  400. data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +5 -5
  401. data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +52 -46
  402. data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +263 -0
  403. data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +124 -0
  404. data/lib/phronomy/testing/persistence_contract.rb +4 -0
  405. data/lib/phronomy/tool/authorization.rb +77 -0
  406. data/lib/phronomy/tool/base.rb +549 -9
  407. data/lib/phronomy/tool/call_request.rb +40 -0
  408. data/lib/phronomy/{agent/tool_definition_set.rb → tool/definition_set.rb} +16 -12
  409. data/lib/phronomy/tool/operation.rb +88 -0
  410. data/lib/phronomy/tool/schema.rb +214 -0
  411. data/lib/phronomy/tool/settings.rb +31 -0
  412. data/lib/phronomy/tool/tool_error.rb +7 -0
  413. data/lib/phronomy/{agent → tool}/tool_executor.rb +10 -7
  414. data/lib/phronomy/tools/agent.rb +22 -90
  415. data/lib/phronomy/tools/mcp.rb +21 -14
  416. data/lib/phronomy/tools/vector_search.rb +4 -4
  417. data/lib/phronomy/tracing/automatic.rb +11 -6
  418. data/lib/phronomy/tracing/base.rb +1 -1
  419. data/lib/phronomy/tracing/observation.rb +37 -0
  420. data/lib/phronomy/tracing/settings.rb +36 -0
  421. data/lib/phronomy/vector_store/{async_backend.rb → async/async_client.rb} +35 -29
  422. data/lib/phronomy/vector_store/{in_memory.rb → backends/in_memory.rb} +12 -10
  423. data/lib/phronomy/vector_store/{pgvector.rb → backends/pgvector.rb} +44 -16
  424. data/lib/phronomy/vector_store/{redis_search.rb → backends/redis_search.rb} +74 -37
  425. data/lib/phronomy/vector_store/base.rb +118 -49
  426. data/lib/phronomy/vector_store/invalid_result_error.rb +12 -0
  427. data/lib/phronomy/vector_store.rb +2 -17
  428. data/lib/phronomy/version.rb +1 -1
  429. data/lib/phronomy/workflow/completion.rb +11 -0
  430. data/lib/phronomy/{workflow.rb → workflow/execution/workflow.rb} +10 -8
  431. data/lib/phronomy/workflow/execution/workflow_action_rules.rb +67 -0
  432. data/lib/phronomy/{workflow_context.rb → workflow/execution/workflow_context.rb} +5 -1
  433. data/lib/phronomy/workflow/execution/workflow_context_ownership_error.rb +7 -0
  434. data/lib/phronomy/workflow/execution/workflow_execution_environment.rb +27 -0
  435. data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +186 -0
  436. data/lib/phronomy/{workflow_runner.rb → workflow/execution/workflow_runner.rb} +145 -115
  437. data/lib/phronomy/workflow/execution/workflow_settings.rb +28 -0
  438. data/lib/phronomy/workflow/persistence/codec.rb +153 -0
  439. data/lib/phronomy/workflow/persistence/state_repository.rb +63 -0
  440. data/lib/phronomy/workflow/persistence/storage_schema.rb +13 -0
  441. data/lib/phronomy/workflow/runtime_binding/workflow_engine_environment.rb +38 -0
  442. data/lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb +156 -0
  443. data/lib/phronomy/workflow/runtime_binding/workflow_terminal_policy.rb +40 -0
  444. data/lib/phronomy.rb +97 -98
  445. data/scripts/api_snapshot.rb +36 -16
  446. data/scripts/storage_spi_snapshot.rb +36 -0
  447. data/sig/_private/phronomy/agent/settings.rbs +19 -0
  448. data/sig/_private/phronomy/configuration/runtime_settings.rbs +20 -0
  449. data/sig/_private/phronomy/llm_adapter/async/async_client.rbs +10 -0
  450. data/sig/_private/phronomy/multi_agent/store.rbs +8 -0
  451. data/sig/_private/phronomy/persistence/storage_boundary.rbs +7 -0
  452. data/sig/_private/phronomy/tool/settings.rbs +13 -0
  453. data/sig/_private/phronomy/tracing/settings.rbs +15 -0
  454. data/sig/_private/phronomy/workflow/execution/workflow_settings.rbs +13 -0
  455. data/sig/phronomy/agent/admission.rbs +19 -0
  456. data/sig/phronomy/agent/execution_contract.rbs +132 -0
  457. data/sig/phronomy/agent/execution_environment.rbs +20 -0
  458. data/sig/phronomy/agent/store.rbs +73 -0
  459. data/sig/phronomy/agent.rbs +14 -139
  460. data/sig/phronomy/common.rbs +7 -0
  461. data/sig/phronomy/content_store/contracts.rbs +13 -0
  462. data/sig/phronomy/context.rbs +152 -0
  463. data/sig/phronomy/embeddings/async/async_client.rbs +9 -0
  464. data/sig/phronomy/embeddings/base.rbs +17 -0
  465. data/sig/phronomy/engine/concurrency/timer_bindings.rbs +22 -0
  466. data/sig/phronomy/engine/control_bindings.rbs +11 -0
  467. data/sig/phronomy/engine/control_protocols.rbs +23 -0
  468. data/sig/phronomy/engine/execution_binding.rbs +6 -0
  469. data/sig/phronomy/engine/execution_receiver.rbs +15 -0
  470. data/sig/phronomy/engine/observability.rbs +13 -0
  471. data/sig/phronomy/execution/async_operation.rbs +7 -0
  472. data/sig/phronomy/execution/blocking.rbs +5 -0
  473. data/sig/phronomy/execution/concurrency/cancellation_token.rbs +13 -0
  474. data/sig/phronomy/execution/concurrency/deadline.rbs +10 -0
  475. data/sig/phronomy/execution/concurrency/result_composition.rbs +7 -0
  476. data/sig/phronomy/execution/control_protocols.rbs +18 -0
  477. data/sig/phronomy/execution/errors.rbs +7 -0
  478. data/sig/phronomy/execution/execution.rbs +41 -0
  479. data/sig/phronomy/execution/execution_receiver.rbs +40 -0
  480. data/sig/phronomy/execution/execution_scope_state.rbs +9 -0
  481. data/sig/phronomy/execution/invocation_context.rbs +18 -0
  482. data/sig/phronomy/execution/invocation_controls.rbs +13 -0
  483. data/sig/phronomy/execution/task_result.rbs +29 -0
  484. data/sig/phronomy/extensions.rbs +0 -21
  485. data/sig/phronomy/handoff.rbs +12 -4
  486. data/sig/phronomy/llm_adapter/backends/ruby_llm.rbs +6 -0
  487. data/sig/phronomy/llm_adapter/base.rbs +14 -0
  488. data/sig/phronomy/llm_adapter/usage.rbs +14 -0
  489. data/sig/phronomy/llm_adapter/values.rbs +43 -0
  490. data/sig/phronomy/llm_context_window.rbs +10 -0
  491. data/sig/phronomy/multi_agent/reserved_child_admission.rbs +8 -0
  492. data/sig/phronomy/multi_agent/store.rbs +54 -0
  493. data/sig/phronomy/multi_agent.rbs +24 -3
  494. data/sig/phronomy/persistence/errors.rbs +18 -0
  495. data/sig/phronomy/persistence/migration.rbs +13 -0
  496. data/sig/phronomy/persistence/transaction.rbs +19 -0
  497. data/sig/phronomy/persistence.rbs +3 -185
  498. data/sig/phronomy/persistence_composition.rbs +19 -0
  499. data/sig/phronomy/runtime_composition/configuration.rbs +36 -0
  500. data/sig/phronomy/storage/async/async_client.rbs +8 -0
  501. data/sig/phronomy/storage/backends/in_memory.rbs +9 -0
  502. data/sig/phronomy/storage/contracts.rbs +160 -0
  503. data/sig/phronomy/tool.rbs +58 -5
  504. data/sig/phronomy/tracing/observation.rbs +7 -0
  505. data/sig/phronomy/vector_store/async/async_client.rbs +13 -0
  506. data/sig/phronomy/vector_store/base.rbs +23 -0
  507. data/sig/phronomy/workflow/execution/workflow_execution_environment.rbs +14 -0
  508. data/sig/phronomy/workflow/runtime_binding/workflow_terminal_policy.rbs +7 -0
  509. data/sig/phronomy/workflow/store.rbs +8 -0
  510. data/sig/phronomy/workflow.rbs +1 -1
  511. data/sig/phronomy.rbs +0 -49
  512. metadata +462 -130
  513. data/lib/phronomy/agent/agent_invocation_session_builder.rb +0 -584
  514. data/lib/phronomy/agent/concerns/before_llm_input.rb +0 -66
  515. data/lib/phronomy/agent/concerns/error_translation.rb +0 -45
  516. data/lib/phronomy/agent/concerns/filterable.rb +0 -184
  517. data/lib/phronomy/agent/context/capability/base.rb +0 -507
  518. data/lib/phronomy/agent/context/instruction/prompt_template.rb +0 -102
  519. data/lib/phronomy/agent/context_parts/validators/final_budget_validator.rb +0 -37
  520. data/lib/phronomy/agent/context_policies/default.rb +0 -142
  521. data/lib/phronomy/agent/execution_coordinator.rb +0 -3151
  522. data/lib/phronomy/agent/handoff_execution_coordinator.rb +0 -143
  523. data/lib/phronomy/agent/handoff_request.rb +0 -45
  524. data/lib/phronomy/agent/handoff_runner.rb +0 -178
  525. data/lib/phronomy/agent/phase_machine_builder.rb +0 -152
  526. data/lib/phronomy/agent/provider_call_outcome.rb +0 -106
  527. data/lib/phronomy/agent/recovery_support.rb +0 -504
  528. data/lib/phronomy/agent/selection/candidate.rb +0 -53
  529. data/lib/phronomy/agent/selection/constraint.rb +0 -49
  530. data/lib/phronomy/agent/token_budget_resolver.rb +0 -70
  531. data/lib/phronomy/agent/tool_call_intercepted.rb +0 -29
  532. data/lib/phronomy/agent/tool_invocation_session_builder.rb +0 -276
  533. data/lib/phronomy/configuration.rb +0 -59
  534. data/lib/phronomy/engine/concurrency/cancellation_scope.rb +0 -124
  535. data/lib/phronomy/engine/concurrency/operation_binding.rb +0 -43
  536. data/lib/phronomy/generator_verifier.rb +0 -369
  537. data/lib/phronomy/invalid_context_budget_configuration_error.rb +0 -8
  538. data/lib/phronomy/llm_adapter/ruby_llm.rb +0 -47
  539. data/lib/phronomy/metrics.rb +0 -26
  540. data/lib/phronomy/multi_agent/parallel_tool_chat.rb +0 -116
  541. data/lib/phronomy/persistence/durable_codec.rb +0 -706
  542. data/lib/phronomy/persistence/in_memory.rb +0 -690
  543. data/lib/phronomy/persistence/repository_facades.rb +0 -535
  544. data/lib/phronomy/persistence.rb +0 -276
  545. data/lib/phronomy/recovery.rb +0 -186
  546. data/lib/phronomy/ruby_llm_patches.rb +0 -24
  547. data/lib/phronomy/runnable.rb +0 -54
  548. data/lib/phronomy/token_usage.rb +0 -85
  549. data/lib/phronomy/vector_store/embeddings/base.rb +0 -44
  550. data/lib/phronomy/vector_store/embeddings/ruby_llm_embeddings.rb +0 -47
  551. data/lib/phronomy/workflow/phase_machine_builder.rb +0 -205
  552. data/lib/phronomy/workflow_recovery.rb +0 -123
  553. data/sig/phronomy/llm_adapter.rbs +0 -11
  554. data/sig/phronomy/runtime.rbs +0 -76
  555. /data/lib/phronomy/agent/{llm_operation_result.rb → execution/llm_operation_result.rb} +0 -0
  556. /data/lib/phronomy/{canonical_json.rb → common/canonical_json.rb} +0 -0
  557. /data/lib/phronomy/{event.rb → engine/event.rb} +0 -0
@@ -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)
@@ -69,11 +69,11 @@ module Phronomy
69
69
  candidate_category: values.fetch(:candidate_category).to_sym,
70
70
  policy_category: values.fetch(:policy_category).to_sym,
71
71
  role: values[:role]&.to_sym,
72
- content: Phronomy::Agent::Immutable.copy(values.fetch(:content)),
72
+ content: Phronomy::Values::Immutable.copy(values.fetch(:content)),
73
73
  content_format: format,
74
74
  tool_call_id: values[:tool_call_id]&.to_s&.freeze,
75
75
  provenance: provenance,
76
- metadata: Phronomy::Agent::Immutable.copy(values[:metadata] || {})
76
+ metadata: Phronomy::Values::Immutable.copy(values[:metadata] || {})
77
77
  ))
78
78
  freeze
79
79
  end
@@ -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"],
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../common/error"
4
+
5
+ module Phronomy
6
+ class HandoffError < Error; end
7
+ end
@@ -1,8 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../../execution/concurrency/worker_input_restricted"
4
+
3
5
  module Phronomy
4
6
  module Agent
5
7
  class JournalProjection
8
+ include Phronomy::Concurrency::WorkerInputRestricted
9
+
6
10
  MESSAGE_KINDS = %i[
7
11
  external_message
8
12
  assistant_message
@@ -41,9 +41,9 @@ module Phronomy
41
41
  ATTRIBUTES.each do |name|
42
42
  value = values.fetch(name)
43
43
  value = value.to_sym if %i[kind channel role visibility].include?(name) && value
44
- instance_variable_set("@#{name}", Immutable.copy(value))
44
+ instance_variable_set("@#{name}", Phronomy::Values::Immutable.copy(value))
45
45
  end
46
- Immutable.validate_canonical_json!(metadata, label: "Journal metadata")
46
+ Phronomy::Values::Immutable.validate_canonical_json!(metadata, label: "Journal metadata")
47
47
  freeze
48
48
  end
49
49
 
@@ -52,7 +52,7 @@ module Phronomy
52
52
  end
53
53
 
54
54
  # Current semantic payload representation. Persistence format identity and
55
- # compatibility validation are owned by Persistence::DurableCodec.
55
+ # compatibility validation are owned by Agent::Persistence::Codec.
56
56
  def to_h
57
57
  ATTRIBUTES.to_h { |name| [name.to_s, public_send(name)] }
58
58
  end
@@ -31,11 +31,11 @@ module Phronomy
31
31
  ATTRIBUTES.each do |name|
32
32
  value = values.fetch(name)
33
33
  value = value.to_sym if name == :status
34
- instance_variable_set("@#{name}", Immutable.copy(value))
34
+ instance_variable_set("@#{name}", Phronomy::Values::Immutable.copy(value))
35
35
  end
36
36
  raise ArgumentError, "unknown LLM Call status: #{status.inspect}" unless STATUSES.include?(status)
37
37
  raise ArgumentError, "LLM Call sequence must be positive" unless sequence.positive?
38
- Immutable.validate_canonical_json!(metadata, label: "LLM Call metadata")
38
+ Phronomy::Values::Immutable.validate_canonical_json!(metadata, label: "LLM Call metadata")
39
39
  freeze
40
40
  end
41
41
 
@@ -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,10 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../../execution/concurrency/worker_input_restricted"
4
+
3
5
  require "time"
4
6
 
5
7
  module Phronomy
6
8
  module Agent
7
9
  class AgentRoot
10
+ include Phronomy::Concurrency::WorkerInputRestricted
11
+
8
12
  LIFECYCLE_STATUSES = %i[idle active suspended closed invalidated].freeze
9
13
 
10
14
  ATTRIBUTES = %i[
@@ -36,7 +40,7 @@ module Phronomy
36
40
  ATTRIBUTES.each do |name|
37
41
  value = attributes.fetch(name)
38
42
  value = value.to_sym if name == :lifecycle_status
39
- instance_variable_set("@#{name}", Immutable.copy(value))
43
+ instance_variable_set("@#{name}", Phronomy::Values::Immutable.copy(value))
40
44
  end
41
45
  unless LIFECYCLE_STATUSES.include?(lifecycle_status)
42
46
  raise ArgumentError, "unknown Agent lifecycle status: #{lifecycle_status.inspect}"
@@ -44,7 +48,7 @@ module Phronomy
44
48
  raise ArgumentError, "agent_revision must be non-negative" if agent_revision.negative?
45
49
  raise ArgumentError, "context_revision must be non-negative" if context_revision.negative?
46
50
  raise ArgumentError, "journal_position must be non-negative" if journal_position.negative?
47
- Immutable.validate_canonical_json!(metadata, label: "Agent metadata")
51
+ Phronomy::Values::Immutable.validate_canonical_json!(metadata, label: "Agent metadata")
48
52
  freeze
49
53
  end
50
54
 
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Agent
5
+ # Agent owns this fresh-instance construction contract. Application
6
+ # composition supplies the concrete default once during loading.
7
+ # @api private
8
+ module DefaultPersistence
9
+ FACTORIES = {}
10
+ private_constant :FACTORIES
11
+
12
+ # A private boot binding, not an application extension registry.
13
+ # @api private
14
+ def self.install_factory(factory)
15
+ raise ArgumentError, "factory must respond to call" unless factory.respond_to?(:call)
16
+
17
+ FACTORIES.replace(persistence: factory).freeze
18
+ nil
19
+ end
20
+
21
+ # Invoke only after explicit and configured instances have been excluded.
22
+ # No instance is cached, shared, or installed in global configuration.
23
+ # @api private
24
+ def self.build
25
+ FACTORIES.fetch(:persistence).call
26
+ end
27
+ end
28
+ end
29
+ end
@@ -1,18 +1,27 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Phronomy
4
- class Runtime
4
+ module Agent
5
5
  # Process-local authoritative owner registry for mutable Agent instances.
6
6
  #
7
7
  # This is not a cache. While a Runtime is alive, one agent_id maps to at most
8
8
  # one mutable live Agent object. Construction is reserved before durable
9
9
  # create/load so concurrent callers cannot materialize independent objects.
10
- class AgentOwnershipRegistry
10
+ # @api private
11
+ class OwnershipRegistry
12
+ # Candidates have no side effects. Runtime retains the first registration.
13
+ # Existing instances remain available for lookup and transition completion
14
+ # after admission closes; their own gate rejects new ownership changes.
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
19
+
11
20
  Entry = Data.define(:state, :agent, :token)
12
21
  private_constant :Entry
13
22
 
14
- def initialize(runtime:)
15
- @runtime = runtime
23
+ def initialize(environment:)
24
+ @environment = environment
16
25
  @mutex = Mutex.new
17
26
  @condition = ConditionVariable.new
18
27
  @entries = {}
@@ -24,7 +33,7 @@ module Phronomy
24
33
  token = reserve_create!(key)
25
34
 
26
35
  begin
27
- agent = yield(@runtime)
36
+ agent = yield(@environment)
28
37
  rescue Phronomy::AgentAlreadyExistsError,
29
38
  Phronomy::Persistence::ConflictError,
30
39
  Phronomy::Persistence::NotFoundError,
@@ -84,7 +93,7 @@ module Phronomy
84
93
  end
85
94
 
86
95
  begin
87
- agent = yield(@runtime)
96
+ agent = yield(@environment)
88
97
  publish_constructed!(
89
98
  key,
90
99
  token,
@@ -147,7 +156,7 @@ module Phronomy
147
156
  end
148
157
  @entries[key] = Entry.new(state: :purging, agent: agent, token: token)
149
158
  end
150
- agent.send(:__mark_purging!, @runtime)
159
+ agent.send(:__mark_purging!, @environment)
151
160
  token
152
161
  end
153
162
 
@@ -156,10 +165,10 @@ module Phronomy
156
165
  @mutex.synchronize do
157
166
  entry = @entries[key]
158
167
  validate_purge_entry!(entry, agent, token, key)
168
+ agent.send(:__mark_purged!, @environment)
159
169
  @entries.delete(key)
160
170
  @condition.broadcast
161
171
  end
162
- agent.send(:__mark_purged!, @runtime)
163
172
  true
164
173
  end
165
174
 
@@ -169,10 +178,10 @@ module Phronomy
169
178
  @mutex.synchronize do
170
179
  entry = @entries[key]
171
180
  validate_purge_entry!(entry, agent, token, key)
181
+ agent.send(:__restore_live_after_purge_abort!, @environment)
172
182
  @entries[key] = Entry.new(state: :live, agent: agent, token: nil)
173
183
  @condition.broadcast
174
184
  end
175
- agent.send(:__restore_live_after_purge_abort!, @runtime)
176
185
  true
177
186
  end
178
187
 
@@ -183,6 +192,7 @@ module Phronomy
183
192
  key = normalize_agent_id(agent.agent_id)
184
193
  @mutex.synchronize do
185
194
  validate_purge_entry!(@entries[key], agent, token, key)
195
+ agent.send(:__mark_ownership_recovery_required!, @environment)
186
196
  @entries[key] = Entry.new(
187
197
  state: :recovery_required,
188
198
  agent: agent,
@@ -190,7 +200,6 @@ module Phronomy
190
200
  )
191
201
  @condition.broadcast
192
202
  end
193
- agent.send(:__mark_ownership_recovery_required!, @runtime)
194
203
  true
195
204
  end
196
205
 
@@ -204,7 +213,7 @@ module Phronomy
204
213
 
205
214
  # Live/recovery-required entries do not prevent Runtime shutdown. Only an
206
215
  # ownership transition that is still actively changing state must settle.
207
- def wait_until_stable(deadline)
216
+ def wait_until_idle(deadline)
208
217
  @mutex.synchronize do
209
218
  while @entries.values.any? { |entry| %i[constructing purging].include?(entry.state) }
210
219
  remaining = deadline - monotonic_now
@@ -217,7 +226,7 @@ module Phronomy
217
226
  end
218
227
  end
219
228
 
220
- def shutdown!
229
+ def after_runtime_shutdown
221
230
  agents = @mutex.synchronize do
222
231
  @state = :terminated
223
232
  owned = @entries.values.filter_map(&:agent).uniq
@@ -225,7 +234,7 @@ module Phronomy
225
234
  @condition.broadcast
226
235
  owned
227
236
  end
228
- agents.each { |agent| agent.send(:__release_runtime_owner!, @runtime) }
237
+ agents.each { |agent| agent.send(:__release_runtime_owner!, @environment) }
229
238
  true
230
239
  end
231
240
 
@@ -253,7 +262,7 @@ module Phronomy
253
262
  "constructed Agent identity mismatch: reserved #{key.inspect}, got #{agent.agent_id.inspect}"
254
263
  end
255
264
 
256
- agent.send(:__bind_runtime_owner!, @runtime)
265
+ agent.send(:__bind_runtime_owner!, @environment)
257
266
  @mutex.synchronize do
258
267
  entry = @entries[key]
259
268
  unless entry&.state == :constructing && entry.token.equal?(token)
@@ -268,7 +277,7 @@ module Phronomy
268
277
  # :nocov:
269
278
  if fail_closed
270
279
  fail_construction_closed!(key, token, agent: agent)
271
- agent&.send(:__mark_ownership_recovery_required!, @runtime)
280
+ agent&.send(:__mark_ownership_recovery_required!, @environment)
272
281
  else
273
282
  release_construction!(key, token)
274
283
  end