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,276 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phronomy
4
- class Persistence
5
- class ConflictError < Phronomy::Error; end
6
- class NotFoundError < Phronomy::Error; end
7
- class UnsupportedBackendError < Phronomy::Error; end
8
- class SerializationError < Phronomy::Error; end
9
-
10
- REQUIRED_CAPABILITIES = {
11
- atomic_all: true,
12
- atomic_admission: true,
13
- optimistic_revision: true
14
- }.freeze
15
-
16
- # Content-addressed immutable content repository. ContentStore has its own
17
- # codec/canonicalization boundary and is not wrapped in DurableRecord.
18
- #
19
- # @return [Object]
20
- # @api public
21
- attr_reader :contents
22
-
23
- # Runtime/domain-facing AgentRoot repository. The backend repository supplied
24
- # to #initialize is record-oriented; this facade owns encode/decode.
25
- #
26
- # @return [Object]
27
- # @api public
28
- attr_reader :agents
29
-
30
- # Runtime/domain-facing append-only Agent Journal repository.
31
- #
32
- # @return [Object]
33
- # @api public
34
- attr_reader :journals
35
-
36
- # Runtime/domain-facing AgentExecution repository.
37
- #
38
- # @return [Object]
39
- # @api public
40
- attr_reader :executions
41
-
42
- # Runtime/domain-facing durable Workflow snapshot repository.
43
- #
44
- # @return [Object]
45
- # @api public
46
- attr_reader :workflow_states
47
-
48
- # Purpose-specific durable Handoff and Team repositories in atomic_all.
49
- # @api public
50
- attr_reader :handoff_states, :teams, :team_executions
51
-
52
- # Read-only exact Agent execution result; never creates a live Agent owner.
53
- # Result is nil until a result_ref exists; errors remain canonical data.
54
- # @api public
55
- def execution_result(execution_id)
56
- assert_observation_thread!
57
- execution = executions.load(execution_id)
58
- {
59
- execution_id: execution.execution_id, agent_id: execution.agent_id,
60
- status: execution.status, phase: execution.phase,
61
- result_ref: execution.result_ref, error_ref: execution.error_ref,
62
- result: execution.result_ref && contents.fetch_text(execution.result_ref),
63
- error: execution.error_ref && contents.fetch_json(execution.error_ref)
64
- }.then { |value| Phronomy::Agent::Immutable.copy(value) }
65
- end
66
-
67
- # Follows one exact Handoff turn without loading any Agent or graph.
68
- # A committed absent Target reservation remains active; failed reads raise.
69
- # @api public
70
- def handoff_result(execution_id, main_agent_id: nil)
71
- assert_observation_thread!
72
- anchor = main_agent_id&.to_s
73
- seen = {}
74
- current = execution_id.to_s
75
- reserved_agent_id = nil
76
- loop do
77
- raise Phronomy::Persistence::SerializationError, "Cyclic durable Handoff chain" if seen[current]
78
- seen[current] = true
79
- begin
80
- execution = executions.load(current)
81
- rescue Phronomy::Persistence::NotFoundError
82
- routing = handoff_states.load(anchor)
83
- if routing && Array(routing.metadata["cancelled_execution_ids"]).include?(current)
84
- return {execution_id: current, agent_id: reserved_agent_id, status: :cancelled, result: nil, error: nil}.freeze
85
- end
86
- if reserved_agent_id && routing && routing.phase != "stable" && routing.pending_target_execution_id == current
87
- return {execution_id: current, agent_id: reserved_agent_id, status: :active,
88
- phase: :target_pending, reserved: true, result: nil, error: nil}.freeze
89
- end
90
- raise
91
- end
92
- anchor ||= execution.metadata.dig("coordination", "main_agent_id")
93
- unless anchor && execution.metadata.dig("coordination", "main_agent_id") == anchor
94
- raise Phronomy::Persistence::ConflictError, "Execution does not belong to this Handoff anchor"
95
- end
96
- return execution_result(current) unless execution.status == :handed_off
97
- reserved_agent_id = execution.metadata.fetch("handoff_target_agent_id")
98
- current = execution.metadata.fetch("handoff_target_execution_id")
99
- end
100
- end
101
-
102
- # Lists retained active and terminal executions in lexical ID order.
103
- # after is an exclusive ID cursor; discovery does not imply request dedup.
104
- # @api public
105
- def list_executions(agent_id, after: nil, limit: 100)
106
- assert_observation_thread!
107
- executions.list(agent_id, after: after, limit: limit)
108
- end
109
-
110
- # Read-only exact Team result, independent of current Team class/Proc wiring.
111
- # @api public
112
- def team_execution_result(team_execution_id)
113
- assert_observation_thread!
114
- execution = team_executions.load(team_execution_id)
115
- {
116
- team_execution_id: execution.team_execution_id, team_id: execution.team_id,
117
- status: execution.status, phase: execution.phase,
118
- result_ref: execution.result_ref, error_ref: execution.error_ref,
119
- result: execution.result_ref && contents.fetch_json(execution.result_ref),
120
- error: execution.error_ref && contents.fetch_json(execution.error_ref)
121
- }.then { |value| Phronomy::Agent::Immutable.copy(value) }
122
- end
123
-
124
- # Lists retained Team runs; no continuation or callback delivery occurs.
125
- # @api public
126
- def list_team_executions(team_id, after: nil, limit: 100)
127
- assert_observation_thread!
128
- team_executions.list(team_id, after: after, limit: limit)
129
- end
130
-
131
- # Initializes a Persistence backend from record-oriented storage
132
- # repositories.
133
- #
134
- # Except for +contents+, backend repositories exchange
135
- # {Phronomy::Persistence::DurableRecord} values. Phronomy's repository
136
- # facades own current-format validation and domain-object encode/decode.
137
- #
138
- # Identity, revision, admission, and index metadata needed by a Backend is
139
- # passed explicitly as repository arguments. A Backend must not inspect
140
- # DurableRecord#payload to rediscover Phronomy domain semantics.
141
- #
142
- # Required raw repository shapes:
143
- # - agents:
144
- # create(agent_id:, agent_revision:, record:), load(id),
145
- # save(id, expected_revision:, next_revision:, record:), delete(id)
146
- # - journals:
147
- # append(id, expected_position:, records:, record_ids:), read/head/delete
148
- # - executions:
149
- # create_active(execution_id:, agent_id:, execution_revision:, record:),
150
- # load(id), save(id, expected_revision:, next_revision:, agent_id:,
151
- # active:, record:), list_active/delete/delete_for_agent/assert_idle!
152
- # - workflow_states:
153
- # load(id), save(id, expected_revision:, next_revision:, record:), delete
154
- #
155
- # Subclasses normally construct backend-specific raw repository objects and
156
- # call +super+. Construction fails fast when required capabilities are not
157
- # advertised.
158
- #
159
- # @api public
160
- def initialize(contents:, agents:, journals:, executions:, workflow_states:, handoff_states:, teams:, team_executions:)
161
- @contents = contents
162
- @agents = RepositoryFacades::Agents.new(agents)
163
- @journals = RepositoryFacades::Journals.new(journals)
164
- @executions = RepositoryFacades::Executions.new(executions)
165
- @workflow_states = RepositoryFacades::WorkflowStates.new(workflow_states)
166
- @handoff_states = RepositoryFacades::HandoffStates.new(handoff_states)
167
- @teams = RepositoryFacades::Teams.new(teams)
168
- @team_executions = RepositoryFacades::TeamExecutions.new(team_executions)
169
- validate_capabilities!
170
- end
171
-
172
- # Declares storage semantics provided by this backend.
173
- #
174
- # Required meanings:
175
- # - +atomic_all+: all durable repositories can participate in one atomic
176
- # transaction domain.
177
- # - +atomic_admission+: Agent execution admission is atomic; at most one
178
- # active/suspended execution may be admitted for one Agent. This does not
179
- # mean cross-process Workflow admission or distributed locking.
180
- # - +optimistic_revision+: Agent, Execution, Workflow revision checks and
181
- # Journal position checks provide compare-and-swap conflict detection.
182
- #
183
- # @return [Hash{Symbol => Boolean}]
184
- # @api public
185
- def capabilities
186
- {
187
- atomic_all: false,
188
- atomic_admission: false,
189
- optimistic_revision: false
190
- }.freeze
191
- end
192
-
193
- # Executes one atomic durable transaction.
194
- #
195
- # The yielded object is a transaction-scoped Persistence view exposing the
196
- # same domain-facing repository facades. Its underlying backend repositories
197
- # remain DurableRecord-oriented.
198
- #
199
- # If the block raises, mutations made through the transaction view must not
200
- # be committed. Storage failures whose commit outcome is fundamentally
201
- # unknown remain backend/database failures; Phronomy does not claim
202
- # exactly-once semantics for such failures.
203
- #
204
- # @yieldparam transaction_view [Object]
205
- # @return [Object] the block result
206
- # @raise [UnsupportedBackendError] when atomic transactions are unavailable
207
- # @api public
208
- def transaction
209
- raise UnsupportedBackendError, "#{self.class} does not provide atomic_all"
210
- end
211
-
212
- # Backend SPI helper for transaction implementations whose transaction-scoped
213
- # raw repositories differ from the root repository objects.
214
- #
215
- # +watermark+ is a transaction-scoped object responding to
216
- # +assert_agent_watermark!+. The returned view owns the same Phronomy codec
217
- # facades as the root Persistence instance, so backend authors never need to
218
- # instantiate RepositoryFacades directly.
219
- #
220
- # Backends whose repository objects are already transaction-scoped may yield
221
- # +self+ and need not call this helper.
222
- #
223
- # @api public
224
- def build_transaction_view(
225
- contents:,
226
- agents:,
227
- journals:,
228
- executions:,
229
- workflow_states:,
230
- handoff_states:, teams:, team_executions:,
231
- watermark:
232
- )
233
- RepositoryFacades::View.new(
234
- contents: contents,
235
- agents: agents,
236
- journals: journals,
237
- executions: executions,
238
- workflow_states: workflow_states,
239
- handoff_states: handoff_states, teams: teams, team_executions: team_executions,
240
- watermark: watermark
241
- )
242
- end
243
-
244
- # Verifies that a live Agent still owns the durable base it hydrated.
245
- #
246
- # This is a Backend SPI operation invoked by Phronomy at durable barriers.
247
- # Ordinary application code should not call it directly. The backend must
248
- # compare the stored Agent revision and current Journal position against the
249
- # supplied watermark in the same storage consistency view used by subsequent
250
- # writes in the surrounding transaction.
251
- #
252
- # @api public
253
- def assert_agent_watermark!(agent_id:, agent_revision:, journal_position:)
254
- raise UnsupportedBackendError,
255
- "#{self.class} does not provide Agent durable-watermark checks"
256
- end
257
-
258
- private
259
-
260
- def assert_observation_thread!
261
- if Phronomy::Runtime.in_event_loop_context?
262
- raise Phronomy::EventLoopReentrancyError, "Persistence observation cannot block EventLoop"
263
- end
264
- end
265
-
266
- def validate_capabilities!
267
- missing = REQUIRED_CAPABILITIES.reject do |key, value|
268
- capabilities[key] == value
269
- end
270
- return if missing.empty?
271
-
272
- raise UnsupportedBackendError,
273
- "Persistence backend lacks required capabilities: #{missing.keys.join(", ")}"
274
- end
275
- end
276
- end
@@ -1,186 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phronomy
4
- # Small private Recovery semantic/primitives layer shared by Agent and
5
- # Workflow recovery. Entity-specific orchestration remains outside this module.
6
- # @api private
7
- module Recovery
8
- RESUMABLE = :resumable
9
- RECONCILABLE = :reconcilable
10
- RESOLUTION_REQUIRED = :resolution_required
11
- DISPOSITIONS = [RESUMABLE, RECONCILABLE, RESOLUTION_REQUIRED].freeze
12
-
13
- ALLOWED_OUTCOMES = %i[succeeded failed not_performed].freeze
14
- # Internal shorthand used by the Agent Recovery implementation. This is not
15
- # a public API constant; both names refer to the same frozen value.
16
- OUTCOMES = ALLOWED_OUTCOMES
17
-
18
- MISSING = Object.new.freeze
19
-
20
- Classification = Data.define(
21
- :disposition, :reason, :subject, :allowed_outcomes, :facts
22
- ) do
23
- def initialize(
24
- disposition:,
25
- reason:,
26
- subject: nil,
27
- allowed_outcomes: [],
28
- facts: {}
29
- )
30
- normalized = disposition.to_sym
31
- unless Phronomy::Recovery::DISPOSITIONS.include?(normalized)
32
- raise ArgumentError, "unknown Recovery disposition: #{disposition.inspect}"
33
- end
34
- super(
35
- disposition: normalized,
36
- reason: reason.to_sym,
37
- subject: subject && Phronomy::Recovery.normalize_subject(subject),
38
- allowed_outcomes: Array(allowed_outcomes).map(&:to_sym).freeze,
39
- facts: Phronomy::Agent::Immutable.copy(facts || {})
40
- )
41
- end
42
- end
43
-
44
- module_function
45
-
46
- def normalize_outcome(outcome)
47
- normalized = outcome.to_sym
48
- unless OUTCOMES.include?(normalized)
49
- raise ArgumentError, "unsupported Recovery outcome: #{outcome.inspect}"
50
- end
51
- normalized
52
- end
53
-
54
- def validate_resolution_material!(outcome:, result_present:, error_present:)
55
- normalized = normalize_outcome(outcome)
56
- case normalized
57
- when :succeeded
58
- raise ArgumentError, "Recovery :succeeded requires result:" unless result_present
59
- raise ArgumentError, "Recovery :succeeded forbids error:" if error_present
60
- when :failed
61
- raise ArgumentError, "Recovery :failed requires error:" unless error_present
62
- raise ArgumentError, "Recovery :failed forbids result:" if result_present
63
- when :not_performed
64
- if result_present || error_present
65
- raise ArgumentError, "Recovery :not_performed accepts neither result: nor error:"
66
- end
67
- end
68
- true
69
- end
70
-
71
- def normalize_subject(subject)
72
- hash = subject.to_h.transform_keys(&:to_sym)
73
- type = hash.fetch(:type).to_sym
74
- normalized = {type: type}
75
- case type
76
- when :llm_call
77
- normalized[:llm_call_id] = required_id(hash, :llm_call_id)
78
- when :tool_invocation
79
- normalized[:tool_invocation_id] = required_id(hash, :tool_invocation_id)
80
- when :persistence_operation
81
- # Persistence operation references are structural only. Do not introduce
82
- # a generic durable operation/recovery identity here.
83
- normalized[:entity] = hash.fetch(:entity).to_sym
84
- else
85
- raise ArgumentError, "unsupported Recovery subject type: #{type.inspect}"
86
- end
87
- normalized.freeze
88
- end
89
-
90
- def subject_key(subject)
91
- normalized = normalize_subject(subject)
92
- case normalized.fetch(:type)
93
- when :llm_call
94
- "llm_call:#{normalized.fetch(:llm_call_id)}"
95
- when :tool_invocation
96
- "tool_invocation:#{normalized.fetch(:tool_invocation_id)}"
97
- when :persistence_operation
98
- "persistence_operation:#{normalized.fetch(:entity)}"
99
- end
100
- end
101
-
102
- def subject_equal?(left, right)
103
- normalize_subject(left) == normalize_subject(right)
104
- rescue ArgumentError, KeyError
105
- false
106
- end
107
-
108
- # Compare authoritative current state with a known pre/post revision pair.
109
- # This is deliberately small: callers still validate entity-specific content.
110
- def compare_revisions(current_revision:, expected_pre_revision:, intended_post_revision:)
111
- current = current_revision
112
- return :post_state if current == intended_post_revision
113
- return :pre_state if current == expected_pre_revision
114
-
115
- :conflict
116
- end
117
-
118
- # Generic F1 helper for revisioned snapshot repositories such as Workflow.
119
- # Some repositories return a newly allocated revision only after save, so
120
- # callers may not know the intended post revision in advance. In that case
121
- # the intended snapshot plus a revision different from the expected pre
122
- # revision identifies the post-state.
123
- def compare_revisioned_snapshot(
124
- record:,
125
- expected_pre_revision:,
126
- intended_snapshot:,
127
- intended_post_revision: nil
128
- )
129
- return :pre_state if record.nil? && expected_pre_revision.nil?
130
- return :conflict if record.nil?
131
-
132
- revision = fetch_value(record, :revision)
133
- snapshot = fetch_value(record, :snapshot)
134
- normalized_snapshot = normalize_value(snapshot)
135
- normalized_intended = normalize_value(intended_snapshot)
136
-
137
- if intended_post_revision
138
- if revision == intended_post_revision &&
139
- normalized_snapshot == normalized_intended
140
- return :post_state
141
- end
142
- elsif normalized_snapshot == normalized_intended &&
143
- revision != expected_pre_revision
144
- return :post_state
145
- end
146
-
147
- return :pre_state if revision == expected_pre_revision
148
-
149
- :conflict
150
- end
151
-
152
- def normalize_value(value)
153
- case value
154
- when Hash
155
- value.each_with_object({}) do |(key, child), result|
156
- result[key.to_s] = normalize_value(child)
157
- end
158
- when Array
159
- value.map { |child| normalize_value(child) }
160
- when Symbol
161
- value.to_s
162
- else
163
- value
164
- end
165
- end
166
-
167
- def fetch_value(record, key)
168
- return nil unless record
169
- return record.public_send(key) if record.respond_to?(key)
170
- return record[key] if record.respond_to?(:key?) && record.key?(key)
171
- string_key = key.to_s
172
- return record[string_key] if record.respond_to?(:key?) && record.key?(string_key)
173
-
174
- nil
175
- end
176
-
177
- def required_id(hash, key)
178
- value = hash.fetch(key)
179
- text = value.to_s
180
- raise ArgumentError, "#{key} must not be empty" if text.empty?
181
-
182
- text.freeze
183
- end
184
- private_class_method :required_id
185
- end
186
- end
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Patches for upstream ruby_llm bugs that have not yet been released.
4
- # Remove each patch once the fix is available in a published gem version.
5
-
6
- # Guard: apply monkey-patches only to affected ruby_llm versions so that
7
- # upgrading the gem does not silently keep dead overrides in place.
8
- if Gem::Version.new(RubyLLM::VERSION) <= Gem::Version.new("1.15.0")
9
- module RubyLLM
10
- module Streaming
11
- private
12
-
13
- # Upstream ruby_llm <= 1.15.0 assumes the SSE error chunk always has two
14
- # lines ("event: error\ndata: {...}") and uses a fixed index [1], which
15
- # raises NoMethodError when some providers (e.g. Qwen) return a single-line
16
- # chunk ("data: {...}"). This patch finds the data line by content instead.
17
- def handle_error_chunk(chunk, env)
18
- data_line = chunk.split("\n").find { |l| l.start_with?("data: ") } || chunk.split("\n")[0]
19
- error_data = data_line.delete_prefix("data: ")
20
- parse_error_from_json(error_data, env, "Failed to parse error chunk")
21
- end
22
- end
23
- end
24
- end
@@ -1,54 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phronomy
4
- # Base interface for executable graph components.
5
- # Provides invoke / stream / batch.
6
- module Runnable
7
- # Synchronous execution. Must be overridden in subclasses.
8
- def invoke(input, config: {})
9
- raise NotImplementedError, "#{self.class}#invoke is not implemented"
10
- end
11
-
12
- # Streaming execution. Default yields the invoke result as a single chunk.
13
- def stream(input, config: {}, &block)
14
- result = invoke(input, config: config)
15
- yield result if block
16
- result
17
- end
18
-
19
- # Batch execution. Default calls invoke sequentially.
20
- def batch(inputs, config: {})
21
- inputs.map { |input| invoke(input, config: config) }
22
- end
23
-
24
- # Convenience wrapper that delegates to the global tracer.
25
- # Yields a span; the block must return [result, usage] where usage is a
26
- # Phronomy::TokenUsage or nil. Returns only the result value.
27
- #
28
- # When +trace_pii+ is disabled, both the input and the output (LLM response,
29
- # tool result) are replaced with the literal string "[REDACTED]" before being
30
- # forwarded to the tracing backend. The actual result is still returned to
31
- # the caller — only the copy sent to the tracer is redacted.
32
- #
33
- # @example
34
- # trace("my_chain", input: input) { [invoke(input), nil] }
35
- # @api public
36
- def trace(name, input: nil, **meta, &block)
37
- traced_input = Phronomy.configuration.trace_pii ? input : "[REDACTED]"
38
-
39
- if Phronomy.configuration.trace_pii
40
- # PII recording is allowed: pass through unchanged.
41
- Phronomy.configuration.tracer.trace(name, input: traced_input, **meta, &block)
42
- else
43
- # Redact both input (above) and output before forwarding to the tracer.
44
- # Capture the real result so callers receive the unredacted value.
45
- real_result = nil
46
- Phronomy.configuration.tracer.trace(name, input: traced_input, **meta) do |span|
47
- real_result, usage = block.call(span)
48
- ["[REDACTED]", usage]
49
- end
50
- real_result
51
- end
52
- end
53
- end
54
- end
@@ -1,85 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phronomy
4
- # Immutable value object that holds token usage counts for a single LLM call
5
- # or an accumulated total across multiple calls.
6
- #
7
- # Fields mirror the values reported by RubyLLM::Tokens:
8
- #
9
- # input - tokens consumed by the input (prompt)
10
- # output - tokens produced by the model (completion)
11
- # cached - input tokens served from the provider's prompt cache
12
- # cache_creation - input tokens written into the prompt cache (Anthropic)
13
- #
14
- # All fields are Integer or nil (nil means the provider did not report that
15
- # value for this call).
16
- #
17
- # @example Accumulate usage across a ReAct loop
18
- # total = Phronomy::TokenUsage.zero
19
- # chat.messages.each do |msg|
20
- # total += Phronomy::TokenUsage.from_tokens(msg.tokens)
21
- # end
22
- class TokenUsage
23
- attr_reader :input, :output, :cached, :cache_creation
24
-
25
- def initialize(input: nil, output: nil, cached: nil, cache_creation: nil)
26
- @input = input
27
- @output = output
28
- @cached = cached
29
- @cache_creation = cache_creation
30
- end
31
-
32
- # Returns a zero-valued TokenUsage suitable as an accumulator seed.
33
- def self.zero
34
- new(input: 0, output: 0, cached: 0, cache_creation: 0)
35
- end
36
-
37
- # Builds a TokenUsage from a RubyLLM::Tokens object. Returns zero
38
- # when tokens is nil (provider did not report usage).
39
- # nil fields within the tokens object are coerced to 0.
40
- def self.from_tokens(tokens)
41
- return zero if tokens.nil?
42
-
43
- new(
44
- input: tokens.input || 0,
45
- output: tokens.output || 0,
46
- cached: tokens.cached || 0,
47
- cache_creation: tokens.cache_creation || 0
48
- )
49
- end
50
-
51
- # Adds two TokenUsage instances. nil fields are treated as 0 so that
52
- # partially-reported usage accumulates correctly.
53
- # When other is nil, returns self unchanged.
54
- def +(other)
55
- return self if other.nil?
56
-
57
- self.class.new(
58
- input: _add(input, other.input),
59
- output: _add(output, other.output),
60
- cached: _add(cached, other.cached),
61
- cache_creation: _add(cache_creation, other.cache_creation)
62
- )
63
- end
64
-
65
- def ==(other)
66
- other.is_a?(TokenUsage) &&
67
- input == other.input &&
68
- output == other.output &&
69
- cached == other.cached &&
70
- cache_creation == other.cache_creation
71
- end
72
-
73
- def to_h
74
- {input: input, output: output, cached: cached, cache_creation: cache_creation}
75
- end
76
-
77
- private
78
-
79
- def _add(a, b)
80
- return nil if a.nil? && b.nil?
81
-
82
- (a || 0) + (b || 0)
83
- end
84
- end
85
- end
@@ -1,44 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Phronomy
4
- module VectorStore
5
- module Embeddings
6
- # Public extension SPI for embedding adapters.
7
- #
8
- # Concrete implementations override {#embed}. Phronomy owns the async
9
- # bridge: {#embed_async} routes the synchronous implementation through the
10
- # bounded OffloadPool and returns a {Phronomy::TaskResult}.
11
- #
12
- # @api public
13
- class Base
14
- # Embed the given text and return a vector representation.
15
- #
16
- # @param text [String] the text to embed
17
- # @param cancellation_token [Phronomy::Concurrency::CancellationToken, nil]
18
- # @return [Array<Float>] the embedding vector
19
- # @api public
20
- def embed(text, cancellation_token = nil)
21
- cancellation_token&.raise_if_cancelled!
22
- raise NotImplementedError, "#{self.class}#embed is not implemented"
23
- end
24
-
25
- # Submits an {#embed} call to {OffloadPool}.
26
- #
27
- # @param text [String]
28
- # @param cancellation_token [Phronomy::Concurrency::CancellationToken, nil]
29
- # @param timeout [Numeric, nil] operation-wide submit timeout
30
- # @return [Phronomy::TaskResult]
31
- # @api public
32
- def embed_async(text, cancellation_token = nil, timeout: nil)
33
- Phronomy::Runtime.instance.offload.submit(
34
- timeout: timeout,
35
- cancellation_token: cancellation_token,
36
- on_full: :raise
37
- ) do
38
- embed(text, cancellation_token)
39
- end
40
- end
41
- end
42
- end
43
- end
44
- end