phronomy 0.26.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (557) hide show
  1. checksums.yaml +4 -4
  2. data/.mutant.yml +9 -9
  3. data/CHANGELOG.md +92 -0
  4. data/CONTRIBUTING.md +8 -8
  5. data/README.md +13 -3
  6. data/VERIFY.sh +27 -27
  7. data/benchmark/bench_agent_invoke.rb +26 -47
  8. data/benchmark/bench_context_assembler.rb +17 -15
  9. data/benchmark/bench_regression.rb +1 -1
  10. data/benchmark/bench_tool_schema.rb +1 -1
  11. data/docs/architecture/agent-chat-and-state-ownership.md +153 -0
  12. data/docs/architecture/agent-configuration-and-tool-binding.md +139 -0
  13. data/docs/architecture/agent-context.md +4 -2
  14. data/docs/architecture/agent-transition-ownership.md +13 -0
  15. data/docs/architecture/backend-groups.md +152 -0
  16. data/docs/architecture/before-llm-input.md +9 -3
  17. data/docs/architecture/context-management.md +31 -1
  18. data/docs/architecture/context-preparation-steps.md +90 -0
  19. data/docs/architecture/entry-action-and-team-wording.md +72 -0
  20. data/docs/architecture/execution-abstraction-review.md +88 -0
  21. data/docs/architecture/execution-boundaries.md +28 -0
  22. data/docs/architecture/execution-metadata-and-values.md +86 -0
  23. data/docs/architecture/generator-verifier-ownership.md +111 -0
  24. data/docs/architecture/multi-agent-handoff.md +11 -4
  25. data/docs/architecture/persistence-refactoring-plan.md +50 -0
  26. data/docs/architecture/persistence.md +45 -4
  27. data/docs/architecture/r8-unit1.md +104 -0
  28. data/docs/architecture/r8-unit10.md +72 -0
  29. data/docs/architecture/r8-unit11.md +71 -0
  30. data/docs/architecture/r8-unit12.md +67 -0
  31. data/docs/architecture/r8-unit13.md +61 -0
  32. data/docs/architecture/r8-unit14.md +72 -0
  33. data/docs/architecture/r8-unit15.md +65 -0
  34. data/docs/architecture/r8-unit16.md +54 -0
  35. data/docs/architecture/r8-unit17.md +73 -0
  36. data/docs/architecture/r8-unit18.md +92 -0
  37. data/docs/architecture/r8-unit19.md +61 -0
  38. data/docs/architecture/r8-unit2.md +55 -0
  39. data/docs/architecture/r8-unit20.md +51 -0
  40. data/docs/architecture/r8-unit21.md +79 -0
  41. data/docs/architecture/r8-unit22.md +74 -0
  42. data/docs/architecture/r8-unit23.md +81 -0
  43. data/docs/architecture/r8-unit3.md +130 -0
  44. data/docs/architecture/r8-unit4.md +79 -0
  45. data/docs/architecture/r8-unit5.md +115 -0
  46. data/docs/architecture/r8-unit6.md +71 -0
  47. data/docs/architecture/r8-unit7.md +70 -0
  48. data/docs/architecture/r8-unit8.md +78 -0
  49. data/docs/architecture/r8-unit9.md +73 -0
  50. data/docs/architecture/refactoring-closure.md +343 -0
  51. data/docs/architecture/remaining-refactoring-plan.md +390 -0
  52. data/docs/architecture/rubyllm-2-token-ownership.md +82 -0
  53. data/docs/architecture/storage-execution-boundaries.md +54 -0
  54. data/docs/architecture/tool-schema-recording-gap.md +56 -0
  55. data/docs/architecture/tracing.md +2 -2
  56. data/docs/architecture/workflow-terminal-ownership-design.md +104 -0
  57. data/docs/architecture.md +222 -0
  58. data/docs/decisions/014-unified-persistence-durable-state.md +11 -0
  59. data/docs/decisions/015-tool-public-facade-and-rbs-boundary.md +3 -3
  60. data/docs/decisions/024-event-loop-single-writer-agent-runtime.md +5 -1
  61. data/docs/decisions/025-process-local-agent-ownership-and-runtime-admission.md +13 -3
  62. data/docs/decisions/026-workflow-runtime-admission-and-durable-terminal-barrier.md +5 -1
  63. data/docs/decisions/027-llm-adapter-provider-boundary.md +2 -0
  64. data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +7 -0
  65. data/docs/decisions/031-durable-multi-agent-coordination.md +7 -0
  66. data/docs/decisions/032-storage-backend-composition.md +85 -0
  67. data/docs/decisions/033-domain-persistence-ownership.md +79 -0
  68. data/docs/decisions/034-handoff-runner-coordination-ownership.md +78 -0
  69. data/docs/decisions/035-tool-executor-capability-ownership.md +68 -0
  70. data/docs/decisions/036-context-contract-ownership.md +86 -0
  71. data/docs/decisions/037-common-definition-ownership.md +62 -0
  72. data/docs/decisions/038-responsibility-based-source-layout.md +145 -0
  73. data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +82 -0
  74. data/docs/decisions/040-configuration-default-composition.md +105 -0
  75. data/docs/decisions/041-feature-owned-identity-registries.md +100 -0
  76. data/docs/decisions/042-feature-owned-execution-state.md +106 -0
  77. data/docs/decisions/043-storage-execution-constraint-notifications.md +75 -0
  78. data/docs/decisions/044-agent-default-and-one-shot-composition.md +106 -0
  79. data/docs/decisions/045-worker-input-restriction-ownership.md +104 -0
  80. data/docs/decisions/046-agent-responsibility-layout-and-shared-records.md +110 -0
  81. data/docs/decisions/047-recovered-execution-continuation-contract.md +116 -0
  82. data/docs/decisions/048-dispatch-preparation-worker-ownership.md +120 -0
  83. data/docs/decisions/049-initial-preparation-worker-ownership.md +111 -0
  84. data/docs/decisions/050-approval-resume-snapshot-and-commit-ownership.md +98 -0
  85. data/docs/decisions/051-execution-outcome-worker-ownership.md +153 -0
  86. data/docs/decisions/052-tool-invocation-restoration-ownership.md +92 -0
  87. data/docs/decisions/053-shared-state-coordination-ownership.md +71 -0
  88. data/docs/decisions/054-workflow-terminal-save-single-owner.md +76 -0
  89. data/docs/decisions/055-terminal-observer-failure-settlement.md +60 -0
  90. data/docs/decisions/056-workflow-terminal-policy-ownership.md +87 -0
  91. data/docs/decisions/057-storage-transaction-boundaries.md +73 -0
  92. data/docs/decisions/058-neutral-storage-primitives.md +83 -0
  93. data/docs/decisions/059-backend-contracts-and-async-clients.md +74 -0
  94. data/docs/decisions/060-runtime-settings-and-application-configuration.md +86 -0
  95. data/docs/decisions/061-persistence-failure-contracts.md +89 -0
  96. data/docs/decisions/062-content-store-service.md +52 -0
  97. data/docs/decisions/063-execution-contracts-and-services.md +88 -0
  98. data/docs/decisions/064-agent-execution-and-multi-agent-responsibility.md +36 -0
  99. data/docs/decisions/065-vector-and-embeddings-common-operations.md +38 -0
  100. data/docs/decisions/066-llm-and-tool-operation-contracts.md +20 -0
  101. data/docs/decisions/067-agent-progress-and-engine-connection.md +29 -0
  102. data/docs/decisions/068-agent-tool-child-progress-and-tool-operation.md +36 -0
  103. data/docs/decisions/README.md +52 -6
  104. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_DESIGN_V2.md +6 -0
  105. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md +6 -0
  106. data/docs/execution-boundaries-preview.md +45 -0
  107. data/docs/features.md +16 -5
  108. data/docs/getting-started.md +11 -8
  109. data/docs/migrations/0.15.md +5 -0
  110. data/docs/migrations/0.22.md +2 -2
  111. data/docs/migrations/durable-semantic-coordination-v2.md +8 -0
  112. data/docs/migrations/handoff-runner-multi-agent.md +37 -0
  113. data/docs/migrations/neutral-storage-spi.md +43 -0
  114. data/docs/migrations/parallel-tool-chat-removal.md +49 -0
  115. data/docs/migrations/persistence-failure-contracts.md +67 -0
  116. data/docs/migrations/r8-unit10.md +36 -0
  117. data/docs/migrations/r8-unit11.md +33 -0
  118. data/docs/migrations/r8-unit12.md +31 -0
  119. data/docs/migrations/r8-unit13.md +36 -0
  120. data/docs/migrations/r8-unit14.md +31 -0
  121. data/docs/migrations/r8-unit15.md +31 -0
  122. data/docs/migrations/r8-unit16.md +26 -0
  123. data/docs/migrations/r8-unit17.md +28 -0
  124. data/docs/migrations/r8-unit18.md +29 -0
  125. data/docs/migrations/r8-unit19.md +28 -0
  126. data/docs/migrations/r8-unit20.md +24 -0
  127. data/docs/migrations/r8-unit21.md +28 -0
  128. data/docs/migrations/r8-unit22.md +29 -0
  129. data/docs/migrations/r8-unit23.md +20 -0
  130. data/docs/migrations/r8-unit4.md +67 -0
  131. data/docs/migrations/r8-unit5.md +75 -0
  132. data/docs/migrations/r8-unit6.md +109 -0
  133. data/docs/migrations/r8-unit7.md +31 -0
  134. data/docs/migrations/r8-unit8.md +33 -0
  135. data/docs/migrations/r8-unit9.md +28 -0
  136. data/docs/migrations/shared-state-multi-agent.md +44 -0
  137. data/docs/migrations/storage-async-client.md +103 -0
  138. data/docs/migrations/storage-backend-composition.md +117 -0
  139. data/docs/migrations/storage-transaction-boundaries.md +74 -0
  140. data/docs/migrations/vector-async-clients.md +110 -0
  141. data/docs/persistence-backends.md +190 -215
  142. data/docs/runtime-and-concurrency.md +142 -32
  143. data/lib/phronomy/agent/admission.rb +78 -0
  144. data/lib/phronomy/{agent_already_exists_error.rb → agent/agent_already_exists_error.rb} +2 -0
  145. data/lib/phronomy/{agent_busy_error.rb → agent/agent_busy_error.rb} +2 -0
  146. data/lib/phronomy/{agent_purged_error.rb → agent/agent_purged_error.rb} +2 -0
  147. data/lib/phronomy/agent/api/agent.rb +17 -0
  148. data/lib/phronomy/{execution_rehydration_required_error.rb → agent/api/execution_rehydration_required_error.rb} +2 -0
  149. data/lib/phronomy/agent/async_event_api.rb +38 -33
  150. data/lib/phronomy/agent/base.rb +106 -294
  151. data/lib/phronomy/agent/before_llm_input.rb +64 -0
  152. data/lib/phronomy/{agent.rb → agent/composition/run_once.rb} +4 -13
  153. data/lib/phronomy/agent/context_candidate_resolver.rb +2 -2
  154. data/lib/phronomy/agent/context_importer.rb +2 -2
  155. data/lib/phronomy/agent/{context_assembler.rb → context_preparation.rb} +157 -288
  156. data/lib/phronomy/agent/control_request.rb +21 -0
  157. data/lib/phronomy/agent/{agent_execution.rb → execution/agent_execution.rb} +8 -4
  158. data/lib/phronomy/agent/{agent_invocation.rb → execution/agent_invocation.rb} +32 -63
  159. data/lib/phronomy/agent/execution/approval_resume_commit.rb +108 -0
  160. data/lib/phronomy/agent/execution/dispatch_preparation.rb +195 -0
  161. data/lib/phronomy/agent/{exact_execution.rb → execution/exact_execution.rb} +43 -23
  162. data/lib/phronomy/agent/{execution_cancellation.rb → execution/execution_cancellation.rb} +8 -5
  163. data/lib/phronomy/agent/execution/execution_coordinator.rb +1744 -0
  164. data/lib/phronomy/agent/execution/execution_failure.rb +30 -0
  165. data/lib/phronomy/agent/execution/execution_metadata.rb +54 -0
  166. data/lib/phronomy/agent/execution/execution_outcome_committer.rb +288 -0
  167. data/lib/phronomy/agent/execution/execution_registry.rb +463 -0
  168. data/lib/phronomy/agent/execution/execution_session_runner.rb +113 -0
  169. data/lib/phronomy/agent/execution/initial_preparation.rb +323 -0
  170. data/lib/phronomy/agent/execution/invocation_actions.rb +368 -0
  171. data/lib/phronomy/agent/execution/invocation_transitions.rb +124 -0
  172. data/lib/phronomy/agent/execution/runtime_record_encoder.rb +210 -0
  173. data/lib/phronomy/agent/execution_change.rb +161 -0
  174. data/lib/phronomy/agent/execution_environment.rb +36 -0
  175. data/lib/phronomy/agent/execution_extension_state.rb +21 -0
  176. data/lib/phronomy/agent/execution_observation.rb +35 -0
  177. data/lib/phronomy/agent/filterable.rb +182 -0
  178. data/lib/phronomy/agent/{handoff_context.rb → handoff/transfer_context.rb} +6 -6
  179. data/lib/phronomy/agent/{handoff_projection.rb → handoff/transfer_projection.rb} +14 -33
  180. data/lib/phronomy/agent/handoff_error.rb +7 -0
  181. data/lib/phronomy/agent/{journal_projection.rb → journal/journal_projection.rb} +4 -0
  182. data/lib/phronomy/agent/{journal_record.rb → journal/journal_record.rb} +3 -3
  183. data/lib/phronomy/agent/{llm_call_record.rb → journal/llm_call_record.rb} +2 -2
  184. data/lib/phronomy/agent/knowledge_item.rb +13 -0
  185. data/lib/phronomy/agent/{agent_root.rb → lifecycle/agent_root.rb} +6 -2
  186. data/lib/phronomy/agent/lifecycle/default_persistence.rb +29 -0
  187. data/lib/phronomy/{engine/runtime/agent_ownership_registry.rb → agent/lifecycle/ownership_registry.rb} +24 -15
  188. data/lib/phronomy/agent/persistence/agent_repository.rb +69 -0
  189. data/lib/phronomy/agent/persistence/cancellation_repository.rb +51 -0
  190. data/lib/phronomy/agent/persistence/codec.rb +338 -0
  191. data/lib/phronomy/agent/persistence/execution_repository.rb +124 -0
  192. data/lib/phronomy/agent/persistence/journal_repository.rb +63 -0
  193. data/lib/phronomy/agent/persistence/records.rb +33 -0
  194. data/lib/phronomy/agent/persistence/retention_repository.rb +70 -0
  195. data/lib/phronomy/agent/persistence/storage_schema.rb +29 -0
  196. data/lib/phronomy/agent/persistence/watermark.rb +29 -0
  197. data/lib/phronomy/agent/recovery/invocation_restorer.rb +124 -0
  198. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/continuation.rb +28 -51
  199. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/installation.rb +50 -55
  200. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/resolution.rb +38 -43
  201. data/lib/phronomy/agent/{recovery_coordinator.rb → recovery/recovery_coordinator.rb} +26 -23
  202. data/lib/phronomy/agent/recovery/recovery_rules.rb +119 -0
  203. data/lib/phronomy/agent/recovery/recovery_support.rb +224 -0
  204. data/lib/phronomy/agent/reserved_execution.rb +16 -0
  205. data/lib/phronomy/agent/retention.rb +17 -0
  206. data/lib/phronomy/agent/runtime_binding/engine_environment.rb +60 -0
  207. data/lib/phronomy/agent/runtime_binding/engine_session_builder.rb +34 -0
  208. data/lib/phronomy/agent/runtime_binding/phase_machine_builder.rb +64 -0
  209. data/lib/phronomy/agent/runtime_binding/tool_session_builder.rb +34 -0
  210. data/lib/phronomy/agent/{ruby_llm_materializer.rb → runtime_input.rb} +19 -39
  211. data/lib/phronomy/agent/saved_context_reader.rb +55 -0
  212. data/lib/phronomy/agent/settings.rb +47 -0
  213. data/lib/phronomy/agent/state_writer.rb +166 -0
  214. data/lib/phronomy/agent/store.rb +253 -0
  215. data/lib/phronomy/{stream_callback_error.rb → agent/stream_callback_error.rb} +2 -0
  216. data/lib/phronomy/agent/{approval_evaluation_request.rb → tool_execution/approval_evaluation_request.rb} +1 -12
  217. data/lib/phronomy/agent/{tool_approval_request.rb → tool_execution/tool_approval_request.rb} +1 -10
  218. data/lib/phronomy/agent/tool_execution/tool_binding.rb +30 -0
  219. data/lib/phronomy/agent/{tool_invocation.rb → tool_execution/tool_invocation.rb} +114 -248
  220. data/lib/phronomy/agent/tool_execution/tool_invocation_actions.rb +107 -0
  221. data/lib/phronomy/agent/tool_execution/tool_invocation_transitions.rb +70 -0
  222. data/lib/phronomy/common/configuration_error.rb +7 -0
  223. data/lib/phronomy/common/error.rb +5 -0
  224. data/lib/phronomy/{agent → common/values}/immutable.rb +9 -1
  225. data/lib/phronomy/common/values/serializable.rb +32 -0
  226. data/lib/phronomy/configuration/runtime_settings.rb +49 -0
  227. data/lib/phronomy/content_store/storage_schema.rb +12 -0
  228. data/lib/phronomy/content_store/stored_contents.rb +44 -0
  229. data/lib/phronomy/context/assembly.rb +202 -0
  230. data/lib/phronomy/{context_budget_exceeded_error.rb → context/budget_exceeded_error.rb} +5 -1
  231. data/lib/phronomy/context/candidate.rb +51 -0
  232. data/lib/phronomy/context/constraint.rb +47 -0
  233. data/lib/phronomy/{agent → context}/context_plan.rb +2 -2
  234. data/lib/phronomy/{agent → context}/context_plan_validator.rb +3 -3
  235. data/lib/phronomy/{agent → context}/context_policy.rb +1 -1
  236. data/lib/phronomy/{agent → context}/context_policy_input.rb +7 -7
  237. data/lib/phronomy/{agent → context}/context_policy_input_builder.rb +1 -1
  238. data/lib/phronomy/context/default_policy.rb +140 -0
  239. data/lib/phronomy/context/final_budget_validator.rb +33 -0
  240. data/lib/phronomy/{agent → context}/llm_input_build_context.rb +2 -2
  241. data/lib/phronomy/{agent → context}/llm_input_manifest.rb +21 -21
  242. data/lib/phronomy/{agent → context}/llm_input_patch.rb +3 -3
  243. data/lib/phronomy/context/manifest_error.rb +10 -0
  244. data/lib/phronomy/context/prompt_template.rb +98 -0
  245. data/lib/phronomy/{vector_store → documents}/loader/base.rb +1 -1
  246. data/lib/phronomy/{vector_store → documents}/loader/csv_loader.rb +2 -2
  247. data/lib/phronomy/{vector_store → documents}/loader/markdown_loader.rb +3 -3
  248. data/lib/phronomy/{vector_store → documents}/loader/plain_text_loader.rb +2 -2
  249. data/lib/phronomy/{vector_store → documents}/splitter/base.rb +1 -1
  250. data/lib/phronomy/{vector_store → documents}/splitter/fixed_size_splitter.rb +2 -2
  251. data/lib/phronomy/{vector_store → documents}/splitter/recursive_splitter.rb +2 -2
  252. data/lib/phronomy/embeddings/async/async_client.rb +44 -0
  253. data/lib/phronomy/embeddings/backends/ruby_llm_embeddings.rb +48 -0
  254. data/lib/phronomy/embeddings/base.rb +43 -0
  255. data/lib/phronomy/embeddings/invalid_result_error.rb +11 -0
  256. data/lib/phronomy/embeddings/transport_error.rb +11 -0
  257. data/lib/phronomy/engine/concurrency/offload_pool.rb +39 -20
  258. data/lib/phronomy/engine/concurrency/pool_registry.rb +47 -12
  259. data/lib/phronomy/{diagnostics.rb → engine/diagnostics.rb} +1 -1
  260. data/lib/phronomy/engine/event_loop.rb +164 -630
  261. data/lib/phronomy/engine/execution_binding.rb +23 -0
  262. data/lib/phronomy/engine/execution_receiver_binding.rb +43 -0
  263. data/lib/phronomy/engine/fsm_protocol.rb +9 -0
  264. data/lib/phronomy/engine/fsm_session.rb +37 -30
  265. data/lib/phronomy/engine/metrics.rb +28 -0
  266. data/lib/phronomy/engine/runtime/timer_queue.rb +1 -1
  267. data/lib/phronomy/engine/runtime.rb +104 -95
  268. data/lib/phronomy/engine/terminal_decision.rb +7 -0
  269. data/lib/phronomy/execution/async_operation.rb +41 -0
  270. data/lib/phronomy/execution/backpressure_error.rb +7 -0
  271. data/lib/phronomy/{blocking.rb → execution/blocking.rb} +2 -2
  272. data/lib/phronomy/execution/cancellation_error.rb +7 -0
  273. data/lib/phronomy/{engine → execution}/concurrency/cancellation_token.rb +5 -1
  274. data/lib/phronomy/{engine → execution}/concurrency/deadline.rb +0 -17
  275. data/lib/phronomy/{engine → execution}/concurrency/physical_completion_task.rb +1 -1
  276. data/lib/phronomy/{engine → execution}/concurrency/result_collector.rb +1 -1
  277. data/lib/phronomy/{engine → execution}/concurrency/result_composition.rb +33 -12
  278. data/lib/phronomy/{engine/concurrency/subscriptions.rb → execution/concurrency/result_subscriptions.rb} +14 -18
  279. data/lib/phronomy/execution/concurrency/subscriptions.rb +25 -0
  280. data/lib/phronomy/execution/concurrency/worker_input_restricted.rb +14 -0
  281. data/lib/phronomy/execution/event_loop_reentrancy_error.rb +8 -0
  282. data/lib/phronomy/{execution.rb → execution/execution.rb} +57 -30
  283. data/lib/phronomy/{execution_cancellation_error.rb → execution/execution_cancellation_error.rb} +2 -0
  284. data/lib/phronomy/execution/execution_receiver.rb +78 -0
  285. data/lib/phronomy/execution/execution_scope_state.rb +35 -0
  286. data/lib/phronomy/{execution_timeout_error.rb → execution/execution_timeout_error.rb} +2 -0
  287. data/lib/phronomy/{invalid_async_entry_action_error.rb → execution/invalid_async_entry_action_error.rb} +2 -0
  288. data/lib/phronomy/{invalid_async_transition_action_error.rb → execution/invalid_async_transition_action_error.rb} +2 -0
  289. data/lib/phronomy/{invalid_async_workflow_action_error.rb → execution/invalid_async_workflow_action_error.rb} +2 -0
  290. data/lib/phronomy/{invocation_context.rb → execution/invocation_context.rb} +0 -15
  291. data/lib/phronomy/execution/invocation_controls.rb +73 -0
  292. data/lib/phronomy/execution/pool_shutdown_error.rb +7 -0
  293. data/lib/phronomy/execution/recursion_limit_error.rb +7 -0
  294. data/lib/phronomy/execution/runnable.rb +24 -0
  295. data/lib/phronomy/execution/runtime_shutdown_error.rb +7 -0
  296. data/lib/phronomy/execution/runtime_shutdown_reentrancy_error.rb +7 -0
  297. data/lib/phronomy/execution/scheduler_reentrancy_error.rb +9 -0
  298. data/lib/phronomy/{engine → execution}/task_result.rb +7 -3
  299. data/lib/phronomy/execution/timeout_error.rb +7 -0
  300. data/lib/phronomy/execution/wait_policy.rb +24 -0
  301. data/lib/phronomy/filter/filter_block_error.rb +14 -0
  302. data/lib/phronomy/generation/generator_verifier/agent_operation.rb +65 -0
  303. data/lib/phronomy/generation/generator_verifier/agent_result_receiver.rb +89 -0
  304. data/lib/phronomy/generation/generator_verifier/default_parser.rb +27 -0
  305. data/lib/phronomy/generation/generator_verifier/pipeline_state.rb +57 -0
  306. data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +112 -0
  307. data/lib/phronomy/generation/generator_verifier.rb +118 -0
  308. data/lib/phronomy/generation/low_confidence_error.rb +14 -0
  309. data/lib/phronomy/llm_adapter/async/async_client.rb +47 -0
  310. data/lib/phronomy/llm_adapter/authentication_error.rb +9 -0
  311. data/lib/phronomy/llm_adapter/backends/ruby_llm.rb +183 -0
  312. data/lib/phronomy/llm_adapter/base.rb +49 -68
  313. data/lib/phronomy/llm_adapter/context_length_error.rb +9 -0
  314. data/lib/phronomy/llm_adapter/invalid_result_error.rb +10 -0
  315. data/lib/phronomy/llm_adapter/message.rb +62 -0
  316. data/lib/phronomy/llm_adapter/rate_limit_error.rb +9 -0
  317. data/lib/phronomy/llm_adapter/request.rb +79 -0
  318. data/lib/phronomy/llm_adapter/response.rb +36 -0
  319. data/lib/phronomy/llm_adapter/stream_chunk.rb +18 -0
  320. data/lib/phronomy/llm_adapter/token_usage.rb +64 -0
  321. data/lib/phronomy/llm_adapter/transport_error.rb +9 -0
  322. data/lib/phronomy/llm_adapter.rb +3 -16
  323. data/lib/phronomy/llm_context_window/token_budget.rb +6 -7
  324. data/lib/phronomy/multi_agent/admission_registry.rb +15 -2
  325. data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +108 -84
  326. data/lib/phronomy/multi_agent/execution_environment.rb +29 -0
  327. data/lib/phronomy/{agent → multi_agent}/handoff.rb +2 -2
  328. data/lib/phronomy/{agent → multi_agent}/handoff_capability_factory.rb +21 -2
  329. data/lib/phronomy/multi_agent/handoff_participant.rb +79 -0
  330. data/lib/phronomy/{agent → multi_agent}/handoff_policy.rb +1 -1
  331. data/lib/phronomy/multi_agent/handoff_runner.rb +156 -0
  332. data/lib/phronomy/{agent → multi_agent}/handoff_state.rb +2 -2
  333. data/lib/phronomy/multi_agent/orchestrator.rb +61 -75
  334. data/lib/phronomy/multi_agent/persistence/codec.rb +75 -0
  335. data/lib/phronomy/multi_agent/persistence/handoff_state_repository.rb +77 -0
  336. data/lib/phronomy/multi_agent/persistence/records.rb +27 -0
  337. data/lib/phronomy/multi_agent/persistence/storage_schema.rb +21 -0
  338. data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +124 -0
  339. data/lib/phronomy/multi_agent/persistence/team_repository.rb +69 -0
  340. data/lib/phronomy/multi_agent/reserved_child_admission.rb +88 -0
  341. data/lib/phronomy/multi_agent/runtime_binding/engine_environment.rb +35 -0
  342. data/lib/phronomy/{agent → multi_agent}/shared_state.rb +56 -39
  343. data/lib/phronomy/multi_agent/store.rb +156 -0
  344. data/lib/phronomy/multi_agent/team_coordinator.rb +87 -75
  345. data/lib/phronomy/multi_agent/team_execution.rb +1 -1
  346. data/lib/phronomy/{engine/runtime → multi_agent}/team_ownership_registry.rb +5 -5
  347. data/lib/phronomy/multi_agent/team_root.rb +1 -1
  348. data/lib/phronomy/output_parser/parse_error.rb +7 -0
  349. data/lib/phronomy/persistence/content_repository.rb +26 -0
  350. data/lib/phronomy/persistence/errors.rb +16 -0
  351. data/lib/phronomy/persistence/migration/initial_format_migration.rb +21 -21
  352. data/lib/phronomy/persistence/persistence.rb +23 -0
  353. data/lib/phronomy/persistence/save_outcome.rb +24 -0
  354. data/lib/phronomy/persistence/snapshot_comparison.rb +81 -0
  355. data/lib/phronomy/persistence/storage_boundary.rb +32 -0
  356. data/lib/phronomy/persistence/transaction.rb +86 -0
  357. data/lib/phronomy/persistence_composition/storage_schema.rb +29 -0
  358. data/lib/phronomy/persistence_composition/stores.rb +34 -0
  359. data/lib/phronomy/persistence_composition/unit4_migration.rb +276 -0
  360. data/lib/phronomy/runtime_composition/agent_defaults.rb +11 -0
  361. data/lib/phronomy/runtime_composition/configuration.rb +146 -0
  362. data/lib/phronomy/runtime_composition/configuration_defaults.rb +9 -0
  363. data/lib/phronomy/runtime_composition/evaluation_defaults.rb +7 -0
  364. data/lib/phronomy/runtime_composition/execution_defaults.rb +5 -0
  365. data/lib/phronomy/runtime_composition/generation_defaults.rb +6 -0
  366. data/lib/phronomy/runtime_composition/global_configuration.rb +38 -0
  367. data/lib/phronomy/runtime_composition/global_runtime.rb +19 -0
  368. data/lib/phronomy/runtime_composition/multi_agent_defaults.rb +9 -0
  369. data/lib/phronomy/runtime_composition/workflow_defaults.rb +4 -0
  370. data/lib/phronomy/storage/async/async_client.rb +60 -0
  371. data/lib/phronomy/storage/backend.rb +107 -0
  372. data/lib/phronomy/storage/backends/in_memory.rb +157 -0
  373. data/lib/phronomy/storage/blob_conflict_error.rb +10 -0
  374. data/lib/phronomy/storage/blobs.rb +31 -0
  375. data/lib/phronomy/storage/condition.rb +25 -0
  376. data/lib/phronomy/storage/condition_failed_error.rb +16 -0
  377. data/lib/phronomy/storage/conflict_error.rb +9 -0
  378. data/lib/phronomy/{persistence → storage}/durable_record.rb +12 -12
  379. data/lib/phronomy/storage/entry.rb +33 -0
  380. data/lib/phronomy/storage/guard_ref.rb +13 -0
  381. data/lib/phronomy/storage/not_found_error.rb +9 -0
  382. data/lib/phronomy/storage/record_codec.rb +177 -0
  383. data/lib/phronomy/storage/records.rb +61 -0
  384. data/lib/phronomy/storage/resource.rb +126 -0
  385. data/lib/phronomy/storage/scope.rb +25 -0
  386. data/lib/phronomy/storage/serialization_error.rb +9 -0
  387. data/lib/phronomy/storage/streams.rb +44 -0
  388. data/lib/phronomy/storage/transaction_error.rb +10 -0
  389. data/lib/phronomy/storage/unique_constraint_error.rb +17 -0
  390. data/lib/phronomy/storage/unsupported_backend_error.rb +9 -0
  391. data/lib/phronomy/storage/validation.rb +53 -0
  392. data/lib/phronomy/storage/view.rb +131 -0
  393. data/lib/phronomy/testing/eval/metrics.rb +1 -1
  394. data/lib/phronomy/testing/eval/scorer/llm_judge.rb +19 -3
  395. data/lib/phronomy/testing/persistence_contract/a_content_store.rb +1 -1
  396. data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +2 -2
  397. data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +54 -48
  398. data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +1 -1
  399. data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +1 -1
  400. data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +5 -5
  401. data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +52 -46
  402. data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +263 -0
  403. data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +124 -0
  404. data/lib/phronomy/testing/persistence_contract.rb +4 -0
  405. data/lib/phronomy/tool/authorization.rb +77 -0
  406. data/lib/phronomy/tool/base.rb +549 -9
  407. data/lib/phronomy/tool/call_request.rb +40 -0
  408. data/lib/phronomy/{agent/tool_definition_set.rb → tool/definition_set.rb} +16 -12
  409. data/lib/phronomy/tool/operation.rb +88 -0
  410. data/lib/phronomy/tool/schema.rb +214 -0
  411. data/lib/phronomy/tool/settings.rb +31 -0
  412. data/lib/phronomy/tool/tool_error.rb +7 -0
  413. data/lib/phronomy/{agent → tool}/tool_executor.rb +10 -7
  414. data/lib/phronomy/tools/agent.rb +22 -90
  415. data/lib/phronomy/tools/mcp.rb +21 -14
  416. data/lib/phronomy/tools/vector_search.rb +4 -4
  417. data/lib/phronomy/tracing/automatic.rb +11 -6
  418. data/lib/phronomy/tracing/base.rb +1 -1
  419. data/lib/phronomy/tracing/observation.rb +37 -0
  420. data/lib/phronomy/tracing/settings.rb +36 -0
  421. data/lib/phronomy/vector_store/{async_backend.rb → async/async_client.rb} +35 -29
  422. data/lib/phronomy/vector_store/{in_memory.rb → backends/in_memory.rb} +12 -10
  423. data/lib/phronomy/vector_store/{pgvector.rb → backends/pgvector.rb} +44 -16
  424. data/lib/phronomy/vector_store/{redis_search.rb → backends/redis_search.rb} +74 -37
  425. data/lib/phronomy/vector_store/base.rb +118 -49
  426. data/lib/phronomy/vector_store/invalid_result_error.rb +12 -0
  427. data/lib/phronomy/vector_store.rb +2 -17
  428. data/lib/phronomy/version.rb +1 -1
  429. data/lib/phronomy/workflow/completion.rb +11 -0
  430. data/lib/phronomy/{workflow.rb → workflow/execution/workflow.rb} +10 -8
  431. data/lib/phronomy/workflow/execution/workflow_action_rules.rb +67 -0
  432. data/lib/phronomy/{workflow_context.rb → workflow/execution/workflow_context.rb} +5 -1
  433. data/lib/phronomy/workflow/execution/workflow_context_ownership_error.rb +7 -0
  434. data/lib/phronomy/workflow/execution/workflow_execution_environment.rb +27 -0
  435. data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +186 -0
  436. data/lib/phronomy/{workflow_runner.rb → workflow/execution/workflow_runner.rb} +145 -115
  437. data/lib/phronomy/workflow/execution/workflow_settings.rb +28 -0
  438. data/lib/phronomy/workflow/persistence/codec.rb +153 -0
  439. data/lib/phronomy/workflow/persistence/state_repository.rb +63 -0
  440. data/lib/phronomy/workflow/persistence/storage_schema.rb +13 -0
  441. data/lib/phronomy/workflow/runtime_binding/workflow_engine_environment.rb +38 -0
  442. data/lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb +156 -0
  443. data/lib/phronomy/workflow/runtime_binding/workflow_terminal_policy.rb +40 -0
  444. data/lib/phronomy.rb +97 -98
  445. data/scripts/api_snapshot.rb +36 -16
  446. data/scripts/storage_spi_snapshot.rb +36 -0
  447. data/sig/_private/phronomy/agent/settings.rbs +19 -0
  448. data/sig/_private/phronomy/configuration/runtime_settings.rbs +20 -0
  449. data/sig/_private/phronomy/llm_adapter/async/async_client.rbs +10 -0
  450. data/sig/_private/phronomy/multi_agent/store.rbs +8 -0
  451. data/sig/_private/phronomy/persistence/storage_boundary.rbs +7 -0
  452. data/sig/_private/phronomy/tool/settings.rbs +13 -0
  453. data/sig/_private/phronomy/tracing/settings.rbs +15 -0
  454. data/sig/_private/phronomy/workflow/execution/workflow_settings.rbs +13 -0
  455. data/sig/phronomy/agent/admission.rbs +19 -0
  456. data/sig/phronomy/agent/execution_contract.rbs +132 -0
  457. data/sig/phronomy/agent/execution_environment.rbs +20 -0
  458. data/sig/phronomy/agent/store.rbs +73 -0
  459. data/sig/phronomy/agent.rbs +14 -139
  460. data/sig/phronomy/common.rbs +7 -0
  461. data/sig/phronomy/content_store/contracts.rbs +13 -0
  462. data/sig/phronomy/context.rbs +152 -0
  463. data/sig/phronomy/embeddings/async/async_client.rbs +9 -0
  464. data/sig/phronomy/embeddings/base.rbs +17 -0
  465. data/sig/phronomy/engine/concurrency/timer_bindings.rbs +22 -0
  466. data/sig/phronomy/engine/control_bindings.rbs +11 -0
  467. data/sig/phronomy/engine/control_protocols.rbs +23 -0
  468. data/sig/phronomy/engine/execution_binding.rbs +6 -0
  469. data/sig/phronomy/engine/execution_receiver.rbs +15 -0
  470. data/sig/phronomy/engine/observability.rbs +13 -0
  471. data/sig/phronomy/execution/async_operation.rbs +7 -0
  472. data/sig/phronomy/execution/blocking.rbs +5 -0
  473. data/sig/phronomy/execution/concurrency/cancellation_token.rbs +13 -0
  474. data/sig/phronomy/execution/concurrency/deadline.rbs +10 -0
  475. data/sig/phronomy/execution/concurrency/result_composition.rbs +7 -0
  476. data/sig/phronomy/execution/control_protocols.rbs +18 -0
  477. data/sig/phronomy/execution/errors.rbs +7 -0
  478. data/sig/phronomy/execution/execution.rbs +41 -0
  479. data/sig/phronomy/execution/execution_receiver.rbs +40 -0
  480. data/sig/phronomy/execution/execution_scope_state.rbs +9 -0
  481. data/sig/phronomy/execution/invocation_context.rbs +18 -0
  482. data/sig/phronomy/execution/invocation_controls.rbs +13 -0
  483. data/sig/phronomy/execution/task_result.rbs +29 -0
  484. data/sig/phronomy/extensions.rbs +0 -21
  485. data/sig/phronomy/handoff.rbs +12 -4
  486. data/sig/phronomy/llm_adapter/backends/ruby_llm.rbs +6 -0
  487. data/sig/phronomy/llm_adapter/base.rbs +14 -0
  488. data/sig/phronomy/llm_adapter/usage.rbs +14 -0
  489. data/sig/phronomy/llm_adapter/values.rbs +43 -0
  490. data/sig/phronomy/llm_context_window.rbs +10 -0
  491. data/sig/phronomy/multi_agent/reserved_child_admission.rbs +8 -0
  492. data/sig/phronomy/multi_agent/store.rbs +54 -0
  493. data/sig/phronomy/multi_agent.rbs +24 -3
  494. data/sig/phronomy/persistence/errors.rbs +18 -0
  495. data/sig/phronomy/persistence/migration.rbs +13 -0
  496. data/sig/phronomy/persistence/transaction.rbs +19 -0
  497. data/sig/phronomy/persistence.rbs +3 -185
  498. data/sig/phronomy/persistence_composition.rbs +19 -0
  499. data/sig/phronomy/runtime_composition/configuration.rbs +36 -0
  500. data/sig/phronomy/storage/async/async_client.rbs +8 -0
  501. data/sig/phronomy/storage/backends/in_memory.rbs +9 -0
  502. data/sig/phronomy/storage/contracts.rbs +160 -0
  503. data/sig/phronomy/tool.rbs +58 -5
  504. data/sig/phronomy/tracing/observation.rbs +7 -0
  505. data/sig/phronomy/vector_store/async/async_client.rbs +13 -0
  506. data/sig/phronomy/vector_store/base.rbs +23 -0
  507. data/sig/phronomy/workflow/execution/workflow_execution_environment.rbs +14 -0
  508. data/sig/phronomy/workflow/runtime_binding/workflow_terminal_policy.rbs +7 -0
  509. data/sig/phronomy/workflow/store.rbs +8 -0
  510. data/sig/phronomy/workflow.rbs +1 -1
  511. data/sig/phronomy.rbs +0 -49
  512. metadata +462 -130
  513. data/lib/phronomy/agent/agent_invocation_session_builder.rb +0 -584
  514. data/lib/phronomy/agent/concerns/before_llm_input.rb +0 -66
  515. data/lib/phronomy/agent/concerns/error_translation.rb +0 -45
  516. data/lib/phronomy/agent/concerns/filterable.rb +0 -184
  517. data/lib/phronomy/agent/context/capability/base.rb +0 -507
  518. data/lib/phronomy/agent/context/instruction/prompt_template.rb +0 -102
  519. data/lib/phronomy/agent/context_parts/validators/final_budget_validator.rb +0 -37
  520. data/lib/phronomy/agent/context_policies/default.rb +0 -142
  521. data/lib/phronomy/agent/execution_coordinator.rb +0 -3151
  522. data/lib/phronomy/agent/handoff_execution_coordinator.rb +0 -143
  523. data/lib/phronomy/agent/handoff_request.rb +0 -45
  524. data/lib/phronomy/agent/handoff_runner.rb +0 -178
  525. data/lib/phronomy/agent/phase_machine_builder.rb +0 -152
  526. data/lib/phronomy/agent/provider_call_outcome.rb +0 -106
  527. data/lib/phronomy/agent/recovery_support.rb +0 -504
  528. data/lib/phronomy/agent/selection/candidate.rb +0 -53
  529. data/lib/phronomy/agent/selection/constraint.rb +0 -49
  530. data/lib/phronomy/agent/token_budget_resolver.rb +0 -70
  531. data/lib/phronomy/agent/tool_call_intercepted.rb +0 -29
  532. data/lib/phronomy/agent/tool_invocation_session_builder.rb +0 -276
  533. data/lib/phronomy/configuration.rb +0 -59
  534. data/lib/phronomy/engine/concurrency/cancellation_scope.rb +0 -124
  535. data/lib/phronomy/engine/concurrency/operation_binding.rb +0 -43
  536. data/lib/phronomy/generator_verifier.rb +0 -369
  537. data/lib/phronomy/invalid_context_budget_configuration_error.rb +0 -8
  538. data/lib/phronomy/llm_adapter/ruby_llm.rb +0 -47
  539. data/lib/phronomy/metrics.rb +0 -26
  540. data/lib/phronomy/multi_agent/parallel_tool_chat.rb +0 -116
  541. data/lib/phronomy/persistence/durable_codec.rb +0 -706
  542. data/lib/phronomy/persistence/in_memory.rb +0 -690
  543. data/lib/phronomy/persistence/repository_facades.rb +0 -535
  544. data/lib/phronomy/persistence.rb +0 -276
  545. data/lib/phronomy/recovery.rb +0 -186
  546. data/lib/phronomy/ruby_llm_patches.rb +0 -24
  547. data/lib/phronomy/runnable.rb +0 -54
  548. data/lib/phronomy/token_usage.rb +0 -85
  549. data/lib/phronomy/vector_store/embeddings/base.rb +0 -44
  550. data/lib/phronomy/vector_store/embeddings/ruby_llm_embeddings.rb +0 -47
  551. data/lib/phronomy/workflow/phase_machine_builder.rb +0 -205
  552. data/lib/phronomy/workflow_recovery.rb +0 -123
  553. data/sig/phronomy/llm_adapter.rbs +0 -11
  554. data/sig/phronomy/runtime.rbs +0 -76
  555. /data/lib/phronomy/agent/{llm_operation_result.rb → execution/llm_operation_result.rb} +0 -0
  556. /data/lib/phronomy/{canonical_json.rb → common/canonical_json.rb} +0 -0
  557. /data/lib/phronomy/{event.rb → engine/event.rb} +0 -0
@@ -1,15 +1,555 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../execution/concurrency/worker_input_restricted"
4
+ require_relative "../execution/cancellation_error"
5
+ require_relative "../configuration/runtime_settings"
6
+ require_relative "settings"
7
+ require_relative "tool_error"
8
+ require_relative "schema"
9
+
3
10
  module Phronomy
4
11
  module Tool
5
- # Public authoring façade for Phronomy Tools.
6
- #
7
- # This constant intentionally refers to the exact same Class object as
8
- # {Phronomy::Agent::Context::Capability::Base}. The implementation namespace
9
- # remains canonical internally so the existing Tool DSL state, inheritance,
10
- # built-in Tools, and compatibility surface are not duplicated.
11
- #
12
- # @api public
13
- Base = Phronomy::Agent::Context::Capability::Base
12
+ # Owns Tool declarations, argument validation and the execution template.
13
+ class Base
14
+ include Phronomy::Concurrency::WorkerInputRestricted
15
+
16
+ Parameter = Data.define(:name, :type, :description, :required)
17
+
18
+ class << self
19
+ # @api public
20
+ def parameter(name, type: :string, description: nil, required: true)
21
+ declared_parameters[name.to_sym] = Parameter.new(name: name.to_sym,
22
+ type: type, description: description, required: required)
23
+ end
24
+
25
+ # @api public
26
+ def tool_name(value = nil)
27
+ if value.nil?
28
+ return @tool_name if instance_variable_defined?(:@tool_name)
29
+ ascii = name.to_s.unicode_normalize(:nfkd).encode("ASCII", replace: "").gsub(/[^a-zA-Z0-9_-]/, "-")
30
+ return ascii.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z\d])([A-Z])/, '\1_\2').downcase.delete_suffix("_tool")
31
+ end
32
+
33
+ @tool_name = value.to_s
34
+ end
35
+
36
+ # Tool descriptions inherit through application and decorator classes.
37
+ # Preserve normal class inheritance semantics so Phronomy's anonymous
38
+ # decorator subclasses do not lose their parent's description.
39
+ # @api public
40
+ def description(text = nil)
41
+ unless text
42
+ return @description if instance_variable_defined?(:@description)
43
+ return superclass.description if superclass.respond_to?(:description)
44
+
45
+ return nil
46
+ end
47
+
48
+ @description = text
49
+ end
50
+ alias_method :desc, :description
51
+
52
+ # Phronomy declarations inherit through anonymous Tool decorators.
53
+ # The Tool contract owns schema construction and validation.
54
+ # @api public
55
+ def declared_parameters
56
+ return @declared_parameters if instance_variable_defined?(:@declared_parameters)
57
+
58
+ @declared_parameters = superclass.respond_to?(:declared_parameters) ? superclass.declared_parameters.dup : {}
59
+ end
60
+
61
+ # Preserve the parameter reader and accept an explicit schema or schema DSL.
62
+ # @api public
63
+ def parameters(schema = :__phronomy_read__, &block)
64
+ return declared_parameters if schema == :__phronomy_read__ && !block
65
+
66
+ @parameters_schema_definition = Schema.new((schema == :__phronomy_read__) ? nil : schema, &block)
67
+ self
68
+ end
69
+
70
+ # @api public
71
+ def parameters_schema_definition
72
+ return @parameters_schema_definition if instance_variable_defined?(:@parameters_schema_definition)
73
+
74
+ superclass.parameters_schema_definition if superclass.respond_to?(:parameters_schema_definition)
75
+ end
76
+
77
+ # @api public
78
+ def params_schema_definition
79
+ parameters_schema_definition
80
+ end
81
+
82
+ # @api public
83
+ def params(schema = nil, &block)
84
+ parameters(schema, &block)
85
+ end
86
+
87
+ # @api public
88
+ def provider_options(options = :__phronomy_read__)
89
+ unless options == :__phronomy_read__
90
+ raise ArgumentError, "provider_options must be a Hash" unless options.is_a?(Hash)
91
+ @provider_options = duplicate_configuration(options)
92
+ return self
93
+ end
94
+ return @provider_options if instance_variable_defined?(:@provider_options)
95
+
96
+ @provider_options = superclass.respond_to?(:provider_options) ? duplicate_configuration(superclass.provider_options) : {}
97
+ end
98
+
99
+ # @api public
100
+ def provider_params
101
+ provider_options
102
+ end
103
+
104
+ # @api public
105
+ def with_params(**options)
106
+ provider_options(options)
107
+ end
108
+
109
+ # @api public
110
+ def param(name, enum: nil, properties: nil, desc: nil, **options)
111
+ options[:description] = desc if desc
112
+ parameter(name, **options)
113
+ param_enums[name] = duplicate_configuration(enum) if enum
114
+ param_schemas[name] = normalize_nested_schema(properties) if properties
115
+ end
116
+
117
+ # @api public
118
+ def param_enums
119
+ return @param_enums if instance_variable_defined?(:@param_enums)
120
+
121
+ parent = superclass.respond_to?(:param_enums) ? superclass.param_enums : {}
122
+ @param_enums = duplicate_configuration(parent)
123
+ end
124
+
125
+ # @api public
126
+ def param_schemas
127
+ return @param_schemas if instance_variable_defined?(:@param_schemas)
128
+
129
+ parent = superclass.respond_to?(:param_schemas) ? superclass.param_schemas : {}
130
+ @param_schemas = duplicate_configuration(parent)
131
+ end
132
+
133
+ private
134
+
135
+ def duplicate_configuration(value)
136
+ case value
137
+ when Hash
138
+ value.to_h do |key, child|
139
+ [key, duplicate_configuration(child)]
140
+ end
141
+ when Array
142
+ value.map { |child| duplicate_configuration(child) }
143
+ else
144
+ value
145
+ end
146
+ end
147
+
148
+ def normalize_nested_schema(props)
149
+ props.transform_keys(&:to_sym).transform_values do |spec|
150
+ normalized = spec.transform_keys(&:to_sym)
151
+ normalized[:type] ||= :string
152
+ if normalized[:properties]
153
+ normalized[:properties] = normalize_nested_schema(normalized[:properties])
154
+ end
155
+ normalized
156
+ end
157
+ end
158
+
159
+ public
160
+
161
+ # Declares whether Tool work is safe to run cooperatively on the EventLoop
162
+ # or must be offloaded to a bounded worker pool.
163
+ #
164
+ # Phronomy does not classify the reason for offloading. Blocking I/O,
165
+ # CPU-bound synchronous work, and other long synchronous calls all use
166
+ # +:offloaded+. The application owns that workload classification.
167
+ # @api public
168
+ def execution_mode(value = nil)
169
+ if value.nil?
170
+ return @execution_mode if instance_variable_defined?(:@execution_mode)
171
+ return superclass.execution_mode if superclass.respond_to?(:execution_mode)
172
+
173
+ return :offloaded
174
+ end
175
+
176
+ valid = %i[cooperative offloaded]
177
+ unless valid.include?(value)
178
+ raise ArgumentError,
179
+ "execution_mode must be one of #{valid.inspect}, got #{value.inspect}"
180
+ end
181
+ @execution_mode = value
182
+ end
183
+
184
+ # Configures execution-error handling. Supported values are :raise
185
+ # and :suppress only.
186
+ # @api public
187
+ def on_error(behavior = nil)
188
+ if behavior.nil?
189
+ return @on_error if instance_variable_defined?(:@on_error)
190
+ return superclass.on_error if superclass.respond_to?(:on_error)
191
+
192
+ return :raise
193
+ end
194
+
195
+ valid = %i[raise suppress]
196
+ unless valid.include?(behavior)
197
+ raise ArgumentError,
198
+ "on_error must be one of #{valid.inspect}, got #{behavior.inspect}"
199
+ end
200
+ @on_error = behavior
201
+ end
202
+
203
+ # @api public
204
+ def on_schema_error(behavior = nil)
205
+ if behavior.nil?
206
+ return @on_schema_error if instance_variable_defined?(:@on_schema_error)
207
+ return superclass.on_schema_error if superclass.respond_to?(:on_schema_error)
208
+
209
+ return :return_error
210
+ end
211
+
212
+ @on_schema_error = behavior
213
+ end
214
+
215
+ # @api public
216
+ def requires_approval(value = :__unset__, &block)
217
+ if block
218
+ unless value == :__unset__
219
+ raise ArgumentError, "pass either a value or a block to requires_approval"
220
+ end
221
+ @requires_approval = block
222
+ elsif value == :__unset__
223
+ return @requires_approval unless @requires_approval.nil?
224
+ return superclass.requires_approval if superclass.respond_to?(:requires_approval)
225
+
226
+ false
227
+ else
228
+ unless value == true || value == false || value.respond_to?(:call)
229
+ raise ArgumentError, "requires_approval must be true, false, or callable"
230
+ end
231
+ @requires_approval = value
232
+ end
233
+ end
234
+
235
+ # @api public
236
+ def approval_facts(&block)
237
+ if block
238
+ @approval_facts = block
239
+ elsif instance_variable_defined?(:@approval_facts)
240
+ @approval_facts
241
+ elsif superclass.respond_to?(:approval_facts)
242
+ superclass.approval_facts
243
+ end
244
+ end
245
+
246
+ # @api public
247
+ def redact_params(*names)
248
+ if names.empty?
249
+ parent = superclass.respond_to?(:redact_params) ? superclass.redact_params : []
250
+ ((@redacted_params || []) + parent).uniq
251
+ else
252
+ @redacted_params = ((@redacted_params || []) + names.map(&:to_sym)).uniq
253
+ end
254
+ end
255
+
256
+ # @api public
257
+ def max_result_size(value = :__unset__)
258
+ if value == :__unset__
259
+ return @max_result_size if instance_variable_defined?(:@max_result_size)
260
+ return superclass.max_result_size if superclass.respond_to?(:max_result_size)
261
+
262
+ return nil
263
+ end
264
+
265
+ @max_result_size = value
266
+ end
267
+ end
268
+
269
+ def name
270
+ self.class.tool_name
271
+ end
272
+
273
+ def description
274
+ self.class.description
275
+ end
276
+
277
+ def provider_options
278
+ self.class.provider_options
279
+ end
280
+
281
+ def parameters_schema
282
+ definition = self.class.parameters_schema_definition
283
+ return definition.json_schema if definition
284
+
285
+ declarations = self.class.declared_parameters
286
+ if declarations.empty?
287
+ declarations = method(:execute).parameters.filter_map do |kind, name|
288
+ next unless %i[key keyreq].include?(kind)
289
+ next if %i[cancellation_token tool_call].include?(name)
290
+ [name, Parameter.new(name: name, type: :string, description: nil, required: kind == :keyreq)]
291
+ end.to_h
292
+ end
293
+ properties = declarations.to_h do |name, parameter|
294
+ type = parameter.type.to_s
295
+ type = "number" if %w[float double].include?(type)
296
+ type = "integer" if type == "int"
297
+ value = {"type" => type, "description" => parameter.description}.compact
298
+ # An unqualified array declaration permits any JSON item. Explicit
299
+ # item constraints belong to an explicit schema.
300
+ value["items"] = {} if type == "array"
301
+ value["type"] = [type, "null"] unless parameter.required
302
+ [name.to_s, value]
303
+ end
304
+ schema = {"type" => "object", "properties" => properties,
305
+ "required" => declarations.select { |_, parameter| parameter.required }.keys.map(&:to_s),
306
+ "additionalProperties" => false}
307
+
308
+ properties = schema.dig("properties") || schema.dig(:properties)
309
+ return schema unless properties
310
+
311
+ self.class.param_enums.each do |param_name, values|
312
+ key = properties.key?(param_name.to_s) ? param_name.to_s : param_name.to_sym
313
+ next unless properties[key]
314
+
315
+ param_type = Array(properties[key]["type"]).find { |type| type != "null" }
316
+ properties[key]["enum"] = values.map do |value|
317
+ case param_type
318
+ when "integer"
319
+ value.is_a?(Integer) ? value : Integer(value.to_s)
320
+ when "number"
321
+ value.is_a?(Numeric) ? value : Float(value.to_s)
322
+ when "boolean"
323
+ unless value == true || value == false
324
+ raise ArgumentError,
325
+ "boolean enum values must be true or false (got: #{value.inspect})"
326
+ end
327
+ value
328
+ else
329
+ value.to_s
330
+ end
331
+ end
332
+ properties[key]["enum"] << nil unless declarations.fetch(param_name.to_sym).required
333
+ end
334
+
335
+ self.class.param_schemas.each do |param_name, nested|
336
+ key = properties.key?(param_name.to_s) ? param_name.to_s : param_name.to_sym
337
+ next unless properties[key]
338
+ properties[key]["properties"] = nested_schema_to_json_schema(nested)
339
+ properties[key]["required"] = nested.select { |_, spec| spec[:required] }.keys.map(&:to_s)
340
+ properties[key]["additionalProperties"] = false
341
+ end
342
+
343
+ schema
344
+ end
345
+
346
+ # Phronomy retains these readers as part of its Tool contract.
347
+ # @api public
348
+ def params_schema
349
+ parameters_schema
350
+ end
351
+
352
+ # @api public
353
+ def provider_params
354
+ provider_options
355
+ end
356
+
357
+ # @api public
358
+ def parameters
359
+ self.class.declared_parameters
360
+ end
361
+
362
+ # Validate/coerce without executing, so approval and execution share inputs.
363
+ # @api public
364
+ def validate_arguments(args)
365
+ schema = parameters_schema
366
+ if !@validation_schema || @validation_schema.json_schema != schema
367
+ @validation_schema = Schema.new(schema)
368
+ end
369
+ values, error = @validation_schema.validate(args || {}, coerce: self.class.on_schema_error == :coerce)
370
+ if values && !self.class.parameters_schema_definition
371
+ # Legacy optional param nil means omission, preserving Ruby defaults.
372
+ self.class.declared_parameters.each do |name, parameter|
373
+ values.delete(name) if !parameter.required && values[name].nil?
374
+ end
375
+ end
376
+ [values, error]
377
+ end
378
+
379
+ # @api public
380
+ def call(args, cancellation_token: nil)
381
+ cancellation_token&.raise_if_cancelled!
382
+ validated_args, schema_error = validate_arguments(args)
383
+ if schema_error
384
+ case self.class.on_schema_error
385
+ when :raise
386
+ raise Phronomy::ToolError,
387
+ "#{self.class.name} schema error: #{schema_error}"
388
+ else
389
+ return "Schema validation failed: #{schema_error}"
390
+ end
391
+ end
392
+
393
+ if cancellation_token && execute_accepts_cancellation_token?
394
+ validated_args = validated_args.merge(cancellation_token: cancellation_token)
395
+ end
396
+ result = execute(**(validated_args || {}).transform_keys(&:to_sym))
397
+ truncate_result_if_needed(result)
398
+ rescue Phronomy::ToolError, Phronomy::CancellationError
399
+ raise
400
+ rescue => error
401
+ if self.class.on_error == :suppress
402
+ msg = "[Phronomy] Tool #{self.class.name} suppressed error: " \
403
+ "#{error.class}: #{error.message}"
404
+ if Phronomy::RuntimeSettings.current.logger
405
+ Phronomy::RuntimeSettings.current.logger.warn(msg)
406
+ else
407
+ warn msg
408
+ end
409
+ "Tool error suppressed: #{error.message}"
410
+ else
411
+ raise Phronomy::ToolError,
412
+ "#{self.class.name} execution failed: #{error.message}"
413
+ end
414
+ end
415
+
416
+ # @api public
417
+ def call_async(
418
+ args,
419
+ cancellation_token: nil,
420
+ config: {}
421
+ )
422
+ Phronomy::Tool::ToolExecutor.call_async(
423
+ tool: self,
424
+ args: args,
425
+ cancellation_token: cancellation_token,
426
+ config: config,
427
+ synchronous_call: Operation.synchronous_delegate(self)
428
+ )
429
+ end
430
+
431
+ def requires_approval
432
+ self.class.requires_approval
433
+ end
434
+
435
+ def requires_approval?
436
+ self.class.requires_approval
437
+ end
438
+
439
+ # @api public
440
+ def tool_origin
441
+ :local
442
+ end
443
+
444
+ # @api public
445
+ def approval_metadata
446
+ {}
447
+ end
448
+
449
+ # @api public
450
+ def execute(**_args)
451
+ raise NotImplementedError, "#{self.class}#execute is not implemented"
452
+ end
453
+
454
+ private
455
+
456
+ # Apply the Tool contract to an implementation that starts asynchronous
457
+ # work. The owner supplies a completion handle; this helper never waits or
458
+ # chooses the implementation, and preserves physical completion via C13.
459
+ def call_async_operation(args, cancellation_token:, operation_name:)
460
+ cancellation_token&.raise_if_cancelled!
461
+ validated_args, schema_error = validate_arguments(args)
462
+ if schema_error
463
+ return Phronomy::AsyncOperation.capture(name: "#{operation_name}-schema") do
464
+ if self.class.on_schema_error == :raise
465
+ raise Phronomy::ToolError, "#{self.class.name} schema error: #{schema_error}"
466
+ end
467
+ "Schema validation failed: #{schema_error}"
468
+ end
469
+ end
470
+ Phronomy::AsyncOperation.call(name: operation_name,
471
+ on_error: method(:async_error_value),
472
+ transform: method(:truncate_result_if_needed)) do
473
+ source = yield(validated_args || {})
474
+ unless source.respond_to?(:on_complete)
475
+ raise Phronomy::ToolError,
476
+ "#{self.class.name} asynchronous execution must return a completion handle"
477
+ end
478
+ source
479
+ end
480
+ rescue => error
481
+ Phronomy::AsyncOperation.capture(name: operation_name,
482
+ on_error: method(:async_error_value)) { raise error }
483
+ end
484
+
485
+ def async_error_value(error)
486
+ if error.is_a?(Phronomy::ToolError) || error.is_a?(Phronomy::CancellationError)
487
+ raise error
488
+ end
489
+
490
+ if self.class.on_error == :suppress
491
+ message = "[Phronomy] Tool #{self.class.name} suppressed error: " \
492
+ "#{error.class}: #{error.message}"
493
+ if Phronomy::RuntimeSettings.current.logger
494
+ Phronomy::RuntimeSettings.current.logger.warn(message)
495
+ else
496
+ warn message
497
+ end
498
+ "Tool error suppressed: #{error.message}"
499
+ else
500
+ wrapped = Phronomy::ToolError.new(
501
+ "#{self.class.name} execution failed: #{error.message}"
502
+ )
503
+ wrapped.set_backtrace(error.backtrace)
504
+ raise wrapped
505
+ end
506
+ end
507
+
508
+ def execute_accepts_cancellation_token?
509
+ method(:execute).parameters.any? do |type, name|
510
+ name == :cancellation_token && %i[key keyreq].include?(type)
511
+ end
512
+ end
513
+
514
+ def truncate_result_if_needed(result)
515
+ max = self.class.max_result_size || Settings.current.max_result_size
516
+ return result unless max && result.respond_to?(:length) && result.length > max
517
+
518
+ msg = "[Phronomy] Tool #{self.class.name} result truncated " \
519
+ "(#{result.length} chars > #{max} limit)"
520
+ if Phronomy::RuntimeSettings.current.logger
521
+ Phronomy::RuntimeSettings.current.logger.warn(msg)
522
+ else
523
+ warn msg
524
+ end
525
+ "#{result[0, max]}...[truncated]"
526
+ end
527
+
528
+ def redacted_args(args)
529
+ redacted = self.class.redact_params
530
+ return args if redacted.empty?
531
+
532
+ args.each_with_object({}) do |(key, value), result|
533
+ result[key] = redacted.include?(key.to_sym) ? "[REDACTED]" : value
534
+ end
535
+ end
536
+
537
+ def nested_schema_to_json_schema(nested)
538
+ nested.each_with_object({}) do |(prop_name, spec), result|
539
+ type = spec[:type].to_s
540
+ type = "number" if %w[float double].include?(type)
541
+ type = "integer" if type == "int"
542
+ entry = {"type" => spec[:required] ? type : [type, "null"]}
543
+ entry["description"] = spec[:desc] if spec[:desc]
544
+ entry["enum"] = spec[:required] ? spec[:enum] : (spec[:enum] + [nil]).uniq if spec[:enum]
545
+ if spec[:properties]
546
+ entry["properties"] = nested_schema_to_json_schema(spec[:properties])
547
+ entry["required"] = spec[:properties].select { |_, child| child[:required] }.keys.map(&:to_s)
548
+ entry["additionalProperties"] = false
549
+ end
550
+ result[prop_name.to_s] = entry
551
+ end
552
+ end
553
+ end
14
554
  end
15
555
  end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "schema"
4
+
5
+ module Phronomy
6
+ module Tool
7
+ # A request is a value, not an executable capability or an approval grant.
8
+ # @api public
9
+ class CallRequest
10
+ attr_reader :id, :name, :arguments, :metadata
11
+
12
+ # @api public
13
+ def initialize(id:, name:, arguments:, metadata: {})
14
+ unless id.is_a?(String) && !id.empty? && name.is_a?(String) && !name.empty?
15
+ raise ArgumentError, "Tool request requires non-empty String id and name"
16
+ end
17
+ unless arguments.is_a?(Hash) && metadata.is_a?(Hash)
18
+ raise ArgumentError, "Tool arguments and metadata must be Hashes"
19
+ end
20
+ @id = id.dup.freeze
21
+ @name = name.dup.freeze
22
+ @arguments = Phronomy::Values::Immutable.copy(Schema.json_value(arguments))
23
+ @metadata = Phronomy::Values::Immutable.copy(Schema.json_value(metadata))
24
+ freeze
25
+ end
26
+
27
+ # @api public
28
+ def self.from_h(value)
29
+ source = Schema.json_value(value)
30
+ new(id: source.fetch("id"), name: source.fetch("name"),
31
+ arguments: source.fetch("arguments"), metadata: source.fetch("metadata", {}))
32
+ end
33
+
34
+ # @api public
35
+ def to_h
36
+ {"id" => id, "name" => name, "arguments" => arguments, "metadata" => metadata}
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,23 +1,22 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Phronomy
4
- module Agent
5
- class ToolDefinitionSet
4
+ module Tool
5
+ class DefinitionSet
6
6
  attr_reader :runtime_tools, :definitions
7
7
 
8
- def self.build(agent, additional_tools: [])
9
- runtime_tools = (agent.class.tools + Array(additional_tools)).freeze
10
- definitions = runtime_tools.map do |tool_class|
11
- prepared = agent.send(:prepare_tool_class, tool_class)
8
+ def self.build(tools:, runtime_tools: tools)
9
+ runtime_tools = Array(runtime_tools).freeze
10
+ definitions = tools.map do |prepared|
12
11
  tool = prepared.is_a?(Class) ? prepared.new : prepared
13
12
  {
14
13
  "name" => tool.name.to_s,
15
14
  "description" => tool.description.to_s,
16
15
  "parameters_schema" => normalize(
17
- tool.respond_to?(:parameters_schema) ? tool.parameters_schema : {}
16
+ tool.parameters_schema
18
17
  ),
19
18
  "provider_options" => normalize(
20
- tool.respond_to?(:provider_options) ? tool.provider_options : {}
19
+ tool.provider_options
21
20
  )
22
21
  }
23
22
  end.freeze
@@ -47,13 +46,13 @@ module Phronomy
47
46
 
48
47
  def initialize(runtime_tools:, definitions:)
49
48
  @runtime_tools = Array(runtime_tools).freeze
50
- @definitions = Immutable.copy(definitions)
49
+ @definitions = Phronomy::Values::Immutable.copy(definitions)
51
50
  validate_unique_names!
52
51
  freeze
53
52
  end
54
53
 
55
54
  def select_definitions(expected_definitions)
56
- expected = Immutable.copy(Array(expected_definitions))
55
+ expected = Phronomy::Values::Immutable.copy(Array(expected_definitions))
57
56
  current_by_name = definitions.each_with_index.to_h do |definition, index|
58
57
  [definition.fetch("name"), [definition, runtime_tools.fetch(index)]]
59
58
  end
@@ -68,12 +67,17 @@ module Phronomy
68
67
 
69
68
  current_definition, runtime_tool = current_by_name.fetch(name) do
70
69
  raise Phronomy::ConfigurationError,
71
- "ContextPolicy selected Tool not present in current Agent configuration: #{name}"
70
+ "Phronomy::Context::ContextPolicy selected Tool not present in current Agent configuration: #{name}"
71
+ end
72
+ if definition["parameters_schema"] == {} && current_definition["parameters_schema"] != {}
73
+ raise Phronomy::ConfigurationError,
74
+ "Saved Tool definition lacks a verifiable parameters_schema: #{name}; " \
75
+ "finish this in-flight execution on its original version before upgrading"
72
76
  end
73
77
  unless Phronomy::CanonicalJSON.dump(current_definition) ==
74
78
  Phronomy::CanonicalJSON.dump(definition)
75
79
  raise Phronomy::ConfigurationError,
76
- "Agent Tool definition changed after ContextPolicy selection: #{name}"
80
+ "Agent Tool definition changed after Phronomy::Context::ContextPolicy selection: #{name}"
77
81
  end
78
82
 
79
83
  selected_tools << runtime_tool