phronomy 0.26.0 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (557) hide show
  1. checksums.yaml +4 -4
  2. data/.mutant.yml +9 -9
  3. data/CHANGELOG.md +92 -0
  4. data/CONTRIBUTING.md +8 -8
  5. data/README.md +13 -3
  6. data/VERIFY.sh +27 -27
  7. data/benchmark/bench_agent_invoke.rb +26 -47
  8. data/benchmark/bench_context_assembler.rb +17 -15
  9. data/benchmark/bench_regression.rb +1 -1
  10. data/benchmark/bench_tool_schema.rb +1 -1
  11. data/docs/architecture/agent-chat-and-state-ownership.md +153 -0
  12. data/docs/architecture/agent-configuration-and-tool-binding.md +139 -0
  13. data/docs/architecture/agent-context.md +4 -2
  14. data/docs/architecture/agent-transition-ownership.md +13 -0
  15. data/docs/architecture/backend-groups.md +152 -0
  16. data/docs/architecture/before-llm-input.md +9 -3
  17. data/docs/architecture/context-management.md +31 -1
  18. data/docs/architecture/context-preparation-steps.md +90 -0
  19. data/docs/architecture/entry-action-and-team-wording.md +72 -0
  20. data/docs/architecture/execution-abstraction-review.md +88 -0
  21. data/docs/architecture/execution-boundaries.md +28 -0
  22. data/docs/architecture/execution-metadata-and-values.md +86 -0
  23. data/docs/architecture/generator-verifier-ownership.md +111 -0
  24. data/docs/architecture/multi-agent-handoff.md +11 -4
  25. data/docs/architecture/persistence-refactoring-plan.md +50 -0
  26. data/docs/architecture/persistence.md +45 -4
  27. data/docs/architecture/r8-unit1.md +104 -0
  28. data/docs/architecture/r8-unit10.md +72 -0
  29. data/docs/architecture/r8-unit11.md +71 -0
  30. data/docs/architecture/r8-unit12.md +67 -0
  31. data/docs/architecture/r8-unit13.md +61 -0
  32. data/docs/architecture/r8-unit14.md +72 -0
  33. data/docs/architecture/r8-unit15.md +65 -0
  34. data/docs/architecture/r8-unit16.md +54 -0
  35. data/docs/architecture/r8-unit17.md +73 -0
  36. data/docs/architecture/r8-unit18.md +92 -0
  37. data/docs/architecture/r8-unit19.md +61 -0
  38. data/docs/architecture/r8-unit2.md +55 -0
  39. data/docs/architecture/r8-unit20.md +51 -0
  40. data/docs/architecture/r8-unit21.md +79 -0
  41. data/docs/architecture/r8-unit22.md +74 -0
  42. data/docs/architecture/r8-unit23.md +81 -0
  43. data/docs/architecture/r8-unit3.md +130 -0
  44. data/docs/architecture/r8-unit4.md +79 -0
  45. data/docs/architecture/r8-unit5.md +115 -0
  46. data/docs/architecture/r8-unit6.md +71 -0
  47. data/docs/architecture/r8-unit7.md +70 -0
  48. data/docs/architecture/r8-unit8.md +78 -0
  49. data/docs/architecture/r8-unit9.md +73 -0
  50. data/docs/architecture/refactoring-closure.md +343 -0
  51. data/docs/architecture/remaining-refactoring-plan.md +390 -0
  52. data/docs/architecture/rubyllm-2-token-ownership.md +82 -0
  53. data/docs/architecture/storage-execution-boundaries.md +54 -0
  54. data/docs/architecture/tool-schema-recording-gap.md +56 -0
  55. data/docs/architecture/tracing.md +2 -2
  56. data/docs/architecture/workflow-terminal-ownership-design.md +104 -0
  57. data/docs/architecture.md +222 -0
  58. data/docs/decisions/014-unified-persistence-durable-state.md +11 -0
  59. data/docs/decisions/015-tool-public-facade-and-rbs-boundary.md +3 -3
  60. data/docs/decisions/024-event-loop-single-writer-agent-runtime.md +5 -1
  61. data/docs/decisions/025-process-local-agent-ownership-and-runtime-admission.md +13 -3
  62. data/docs/decisions/026-workflow-runtime-admission-and-durable-terminal-barrier.md +5 -1
  63. data/docs/decisions/027-llm-adapter-provider-boundary.md +2 -0
  64. data/docs/decisions/030-agent-handoff-domain-and-durable-responsibility.md +7 -0
  65. data/docs/decisions/031-durable-multi-agent-coordination.md +7 -0
  66. data/docs/decisions/032-storage-backend-composition.md +85 -0
  67. data/docs/decisions/033-domain-persistence-ownership.md +79 -0
  68. data/docs/decisions/034-handoff-runner-coordination-ownership.md +78 -0
  69. data/docs/decisions/035-tool-executor-capability-ownership.md +68 -0
  70. data/docs/decisions/036-context-contract-ownership.md +86 -0
  71. data/docs/decisions/037-common-definition-ownership.md +62 -0
  72. data/docs/decisions/038-responsibility-based-source-layout.md +145 -0
  73. data/docs/decisions/039-runtime-configuration-lifecycle-ownership.md +82 -0
  74. data/docs/decisions/040-configuration-default-composition.md +105 -0
  75. data/docs/decisions/041-feature-owned-identity-registries.md +100 -0
  76. data/docs/decisions/042-feature-owned-execution-state.md +106 -0
  77. data/docs/decisions/043-storage-execution-constraint-notifications.md +75 -0
  78. data/docs/decisions/044-agent-default-and-one-shot-composition.md +106 -0
  79. data/docs/decisions/045-worker-input-restriction-ownership.md +104 -0
  80. data/docs/decisions/046-agent-responsibility-layout-and-shared-records.md +110 -0
  81. data/docs/decisions/047-recovered-execution-continuation-contract.md +116 -0
  82. data/docs/decisions/048-dispatch-preparation-worker-ownership.md +120 -0
  83. data/docs/decisions/049-initial-preparation-worker-ownership.md +111 -0
  84. data/docs/decisions/050-approval-resume-snapshot-and-commit-ownership.md +98 -0
  85. data/docs/decisions/051-execution-outcome-worker-ownership.md +153 -0
  86. data/docs/decisions/052-tool-invocation-restoration-ownership.md +92 -0
  87. data/docs/decisions/053-shared-state-coordination-ownership.md +71 -0
  88. data/docs/decisions/054-workflow-terminal-save-single-owner.md +76 -0
  89. data/docs/decisions/055-terminal-observer-failure-settlement.md +60 -0
  90. data/docs/decisions/056-workflow-terminal-policy-ownership.md +87 -0
  91. data/docs/decisions/057-storage-transaction-boundaries.md +73 -0
  92. data/docs/decisions/058-neutral-storage-primitives.md +83 -0
  93. data/docs/decisions/059-backend-contracts-and-async-clients.md +74 -0
  94. data/docs/decisions/060-runtime-settings-and-application-configuration.md +86 -0
  95. data/docs/decisions/061-persistence-failure-contracts.md +89 -0
  96. data/docs/decisions/062-content-store-service.md +52 -0
  97. data/docs/decisions/063-execution-contracts-and-services.md +88 -0
  98. data/docs/decisions/064-agent-execution-and-multi-agent-responsibility.md +36 -0
  99. data/docs/decisions/065-vector-and-embeddings-common-operations.md +38 -0
  100. data/docs/decisions/066-llm-and-tool-operation-contracts.md +20 -0
  101. data/docs/decisions/067-agent-progress-and-engine-connection.md +29 -0
  102. data/docs/decisions/068-agent-tool-child-progress-and-tool-operation.md +36 -0
  103. data/docs/decisions/README.md +52 -6
  104. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_DESIGN_V2.md +6 -0
  105. data/docs/design/durable-semantic-coordination/IMPLEMENTATION_REPORT.md +6 -0
  106. data/docs/execution-boundaries-preview.md +45 -0
  107. data/docs/features.md +16 -5
  108. data/docs/getting-started.md +11 -8
  109. data/docs/migrations/0.15.md +5 -0
  110. data/docs/migrations/0.22.md +2 -2
  111. data/docs/migrations/durable-semantic-coordination-v2.md +8 -0
  112. data/docs/migrations/handoff-runner-multi-agent.md +37 -0
  113. data/docs/migrations/neutral-storage-spi.md +43 -0
  114. data/docs/migrations/parallel-tool-chat-removal.md +49 -0
  115. data/docs/migrations/persistence-failure-contracts.md +67 -0
  116. data/docs/migrations/r8-unit10.md +36 -0
  117. data/docs/migrations/r8-unit11.md +33 -0
  118. data/docs/migrations/r8-unit12.md +31 -0
  119. data/docs/migrations/r8-unit13.md +36 -0
  120. data/docs/migrations/r8-unit14.md +31 -0
  121. data/docs/migrations/r8-unit15.md +31 -0
  122. data/docs/migrations/r8-unit16.md +26 -0
  123. data/docs/migrations/r8-unit17.md +28 -0
  124. data/docs/migrations/r8-unit18.md +29 -0
  125. data/docs/migrations/r8-unit19.md +28 -0
  126. data/docs/migrations/r8-unit20.md +24 -0
  127. data/docs/migrations/r8-unit21.md +28 -0
  128. data/docs/migrations/r8-unit22.md +29 -0
  129. data/docs/migrations/r8-unit23.md +20 -0
  130. data/docs/migrations/r8-unit4.md +67 -0
  131. data/docs/migrations/r8-unit5.md +75 -0
  132. data/docs/migrations/r8-unit6.md +109 -0
  133. data/docs/migrations/r8-unit7.md +31 -0
  134. data/docs/migrations/r8-unit8.md +33 -0
  135. data/docs/migrations/r8-unit9.md +28 -0
  136. data/docs/migrations/shared-state-multi-agent.md +44 -0
  137. data/docs/migrations/storage-async-client.md +103 -0
  138. data/docs/migrations/storage-backend-composition.md +117 -0
  139. data/docs/migrations/storage-transaction-boundaries.md +74 -0
  140. data/docs/migrations/vector-async-clients.md +110 -0
  141. data/docs/persistence-backends.md +190 -215
  142. data/docs/runtime-and-concurrency.md +142 -32
  143. data/lib/phronomy/agent/admission.rb +78 -0
  144. data/lib/phronomy/{agent_already_exists_error.rb → agent/agent_already_exists_error.rb} +2 -0
  145. data/lib/phronomy/{agent_busy_error.rb → agent/agent_busy_error.rb} +2 -0
  146. data/lib/phronomy/{agent_purged_error.rb → agent/agent_purged_error.rb} +2 -0
  147. data/lib/phronomy/agent/api/agent.rb +17 -0
  148. data/lib/phronomy/{execution_rehydration_required_error.rb → agent/api/execution_rehydration_required_error.rb} +2 -0
  149. data/lib/phronomy/agent/async_event_api.rb +38 -33
  150. data/lib/phronomy/agent/base.rb +106 -294
  151. data/lib/phronomy/agent/before_llm_input.rb +64 -0
  152. data/lib/phronomy/{agent.rb → agent/composition/run_once.rb} +4 -13
  153. data/lib/phronomy/agent/context_candidate_resolver.rb +2 -2
  154. data/lib/phronomy/agent/context_importer.rb +2 -2
  155. data/lib/phronomy/agent/{context_assembler.rb → context_preparation.rb} +157 -288
  156. data/lib/phronomy/agent/control_request.rb +21 -0
  157. data/lib/phronomy/agent/{agent_execution.rb → execution/agent_execution.rb} +8 -4
  158. data/lib/phronomy/agent/{agent_invocation.rb → execution/agent_invocation.rb} +32 -63
  159. data/lib/phronomy/agent/execution/approval_resume_commit.rb +108 -0
  160. data/lib/phronomy/agent/execution/dispatch_preparation.rb +195 -0
  161. data/lib/phronomy/agent/{exact_execution.rb → execution/exact_execution.rb} +43 -23
  162. data/lib/phronomy/agent/{execution_cancellation.rb → execution/execution_cancellation.rb} +8 -5
  163. data/lib/phronomy/agent/execution/execution_coordinator.rb +1744 -0
  164. data/lib/phronomy/agent/execution/execution_failure.rb +30 -0
  165. data/lib/phronomy/agent/execution/execution_metadata.rb +54 -0
  166. data/lib/phronomy/agent/execution/execution_outcome_committer.rb +288 -0
  167. data/lib/phronomy/agent/execution/execution_registry.rb +463 -0
  168. data/lib/phronomy/agent/execution/execution_session_runner.rb +113 -0
  169. data/lib/phronomy/agent/execution/initial_preparation.rb +323 -0
  170. data/lib/phronomy/agent/execution/invocation_actions.rb +368 -0
  171. data/lib/phronomy/agent/execution/invocation_transitions.rb +124 -0
  172. data/lib/phronomy/agent/execution/runtime_record_encoder.rb +210 -0
  173. data/lib/phronomy/agent/execution_change.rb +161 -0
  174. data/lib/phronomy/agent/execution_environment.rb +36 -0
  175. data/lib/phronomy/agent/execution_extension_state.rb +21 -0
  176. data/lib/phronomy/agent/execution_observation.rb +35 -0
  177. data/lib/phronomy/agent/filterable.rb +182 -0
  178. data/lib/phronomy/agent/{handoff_context.rb → handoff/transfer_context.rb} +6 -6
  179. data/lib/phronomy/agent/{handoff_projection.rb → handoff/transfer_projection.rb} +14 -33
  180. data/lib/phronomy/agent/handoff_error.rb +7 -0
  181. data/lib/phronomy/agent/{journal_projection.rb → journal/journal_projection.rb} +4 -0
  182. data/lib/phronomy/agent/{journal_record.rb → journal/journal_record.rb} +3 -3
  183. data/lib/phronomy/agent/{llm_call_record.rb → journal/llm_call_record.rb} +2 -2
  184. data/lib/phronomy/agent/knowledge_item.rb +13 -0
  185. data/lib/phronomy/agent/{agent_root.rb → lifecycle/agent_root.rb} +6 -2
  186. data/lib/phronomy/agent/lifecycle/default_persistence.rb +29 -0
  187. data/lib/phronomy/{engine/runtime/agent_ownership_registry.rb → agent/lifecycle/ownership_registry.rb} +24 -15
  188. data/lib/phronomy/agent/persistence/agent_repository.rb +69 -0
  189. data/lib/phronomy/agent/persistence/cancellation_repository.rb +51 -0
  190. data/lib/phronomy/agent/persistence/codec.rb +338 -0
  191. data/lib/phronomy/agent/persistence/execution_repository.rb +124 -0
  192. data/lib/phronomy/agent/persistence/journal_repository.rb +63 -0
  193. data/lib/phronomy/agent/persistence/records.rb +33 -0
  194. data/lib/phronomy/agent/persistence/retention_repository.rb +70 -0
  195. data/lib/phronomy/agent/persistence/storage_schema.rb +29 -0
  196. data/lib/phronomy/agent/persistence/watermark.rb +29 -0
  197. data/lib/phronomy/agent/recovery/invocation_restorer.rb +124 -0
  198. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/continuation.rb +28 -51
  199. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/installation.rb +50 -55
  200. data/lib/phronomy/agent/{recovery_coordinator → recovery/recovery_coordinator}/resolution.rb +38 -43
  201. data/lib/phronomy/agent/{recovery_coordinator.rb → recovery/recovery_coordinator.rb} +26 -23
  202. data/lib/phronomy/agent/recovery/recovery_rules.rb +119 -0
  203. data/lib/phronomy/agent/recovery/recovery_support.rb +224 -0
  204. data/lib/phronomy/agent/reserved_execution.rb +16 -0
  205. data/lib/phronomy/agent/retention.rb +17 -0
  206. data/lib/phronomy/agent/runtime_binding/engine_environment.rb +60 -0
  207. data/lib/phronomy/agent/runtime_binding/engine_session_builder.rb +34 -0
  208. data/lib/phronomy/agent/runtime_binding/phase_machine_builder.rb +64 -0
  209. data/lib/phronomy/agent/runtime_binding/tool_session_builder.rb +34 -0
  210. data/lib/phronomy/agent/{ruby_llm_materializer.rb → runtime_input.rb} +19 -39
  211. data/lib/phronomy/agent/saved_context_reader.rb +55 -0
  212. data/lib/phronomy/agent/settings.rb +47 -0
  213. data/lib/phronomy/agent/state_writer.rb +166 -0
  214. data/lib/phronomy/agent/store.rb +253 -0
  215. data/lib/phronomy/{stream_callback_error.rb → agent/stream_callback_error.rb} +2 -0
  216. data/lib/phronomy/agent/{approval_evaluation_request.rb → tool_execution/approval_evaluation_request.rb} +1 -12
  217. data/lib/phronomy/agent/{tool_approval_request.rb → tool_execution/tool_approval_request.rb} +1 -10
  218. data/lib/phronomy/agent/tool_execution/tool_binding.rb +30 -0
  219. data/lib/phronomy/agent/{tool_invocation.rb → tool_execution/tool_invocation.rb} +114 -248
  220. data/lib/phronomy/agent/tool_execution/tool_invocation_actions.rb +107 -0
  221. data/lib/phronomy/agent/tool_execution/tool_invocation_transitions.rb +70 -0
  222. data/lib/phronomy/common/configuration_error.rb +7 -0
  223. data/lib/phronomy/common/error.rb +5 -0
  224. data/lib/phronomy/{agent → common/values}/immutable.rb +9 -1
  225. data/lib/phronomy/common/values/serializable.rb +32 -0
  226. data/lib/phronomy/configuration/runtime_settings.rb +49 -0
  227. data/lib/phronomy/content_store/storage_schema.rb +12 -0
  228. data/lib/phronomy/content_store/stored_contents.rb +44 -0
  229. data/lib/phronomy/context/assembly.rb +202 -0
  230. data/lib/phronomy/{context_budget_exceeded_error.rb → context/budget_exceeded_error.rb} +5 -1
  231. data/lib/phronomy/context/candidate.rb +51 -0
  232. data/lib/phronomy/context/constraint.rb +47 -0
  233. data/lib/phronomy/{agent → context}/context_plan.rb +2 -2
  234. data/lib/phronomy/{agent → context}/context_plan_validator.rb +3 -3
  235. data/lib/phronomy/{agent → context}/context_policy.rb +1 -1
  236. data/lib/phronomy/{agent → context}/context_policy_input.rb +7 -7
  237. data/lib/phronomy/{agent → context}/context_policy_input_builder.rb +1 -1
  238. data/lib/phronomy/context/default_policy.rb +140 -0
  239. data/lib/phronomy/context/final_budget_validator.rb +33 -0
  240. data/lib/phronomy/{agent → context}/llm_input_build_context.rb +2 -2
  241. data/lib/phronomy/{agent → context}/llm_input_manifest.rb +21 -21
  242. data/lib/phronomy/{agent → context}/llm_input_patch.rb +3 -3
  243. data/lib/phronomy/context/manifest_error.rb +10 -0
  244. data/lib/phronomy/context/prompt_template.rb +98 -0
  245. data/lib/phronomy/{vector_store → documents}/loader/base.rb +1 -1
  246. data/lib/phronomy/{vector_store → documents}/loader/csv_loader.rb +2 -2
  247. data/lib/phronomy/{vector_store → documents}/loader/markdown_loader.rb +3 -3
  248. data/lib/phronomy/{vector_store → documents}/loader/plain_text_loader.rb +2 -2
  249. data/lib/phronomy/{vector_store → documents}/splitter/base.rb +1 -1
  250. data/lib/phronomy/{vector_store → documents}/splitter/fixed_size_splitter.rb +2 -2
  251. data/lib/phronomy/{vector_store → documents}/splitter/recursive_splitter.rb +2 -2
  252. data/lib/phronomy/embeddings/async/async_client.rb +44 -0
  253. data/lib/phronomy/embeddings/backends/ruby_llm_embeddings.rb +48 -0
  254. data/lib/phronomy/embeddings/base.rb +43 -0
  255. data/lib/phronomy/embeddings/invalid_result_error.rb +11 -0
  256. data/lib/phronomy/embeddings/transport_error.rb +11 -0
  257. data/lib/phronomy/engine/concurrency/offload_pool.rb +39 -20
  258. data/lib/phronomy/engine/concurrency/pool_registry.rb +47 -12
  259. data/lib/phronomy/{diagnostics.rb → engine/diagnostics.rb} +1 -1
  260. data/lib/phronomy/engine/event_loop.rb +164 -630
  261. data/lib/phronomy/engine/execution_binding.rb +23 -0
  262. data/lib/phronomy/engine/execution_receiver_binding.rb +43 -0
  263. data/lib/phronomy/engine/fsm_protocol.rb +9 -0
  264. data/lib/phronomy/engine/fsm_session.rb +37 -30
  265. data/lib/phronomy/engine/metrics.rb +28 -0
  266. data/lib/phronomy/engine/runtime/timer_queue.rb +1 -1
  267. data/lib/phronomy/engine/runtime.rb +104 -95
  268. data/lib/phronomy/engine/terminal_decision.rb +7 -0
  269. data/lib/phronomy/execution/async_operation.rb +41 -0
  270. data/lib/phronomy/execution/backpressure_error.rb +7 -0
  271. data/lib/phronomy/{blocking.rb → execution/blocking.rb} +2 -2
  272. data/lib/phronomy/execution/cancellation_error.rb +7 -0
  273. data/lib/phronomy/{engine → execution}/concurrency/cancellation_token.rb +5 -1
  274. data/lib/phronomy/{engine → execution}/concurrency/deadline.rb +0 -17
  275. data/lib/phronomy/{engine → execution}/concurrency/physical_completion_task.rb +1 -1
  276. data/lib/phronomy/{engine → execution}/concurrency/result_collector.rb +1 -1
  277. data/lib/phronomy/{engine → execution}/concurrency/result_composition.rb +33 -12
  278. data/lib/phronomy/{engine/concurrency/subscriptions.rb → execution/concurrency/result_subscriptions.rb} +14 -18
  279. data/lib/phronomy/execution/concurrency/subscriptions.rb +25 -0
  280. data/lib/phronomy/execution/concurrency/worker_input_restricted.rb +14 -0
  281. data/lib/phronomy/execution/event_loop_reentrancy_error.rb +8 -0
  282. data/lib/phronomy/{execution.rb → execution/execution.rb} +57 -30
  283. data/lib/phronomy/{execution_cancellation_error.rb → execution/execution_cancellation_error.rb} +2 -0
  284. data/lib/phronomy/execution/execution_receiver.rb +78 -0
  285. data/lib/phronomy/execution/execution_scope_state.rb +35 -0
  286. data/lib/phronomy/{execution_timeout_error.rb → execution/execution_timeout_error.rb} +2 -0
  287. data/lib/phronomy/{invalid_async_entry_action_error.rb → execution/invalid_async_entry_action_error.rb} +2 -0
  288. data/lib/phronomy/{invalid_async_transition_action_error.rb → execution/invalid_async_transition_action_error.rb} +2 -0
  289. data/lib/phronomy/{invalid_async_workflow_action_error.rb → execution/invalid_async_workflow_action_error.rb} +2 -0
  290. data/lib/phronomy/{invocation_context.rb → execution/invocation_context.rb} +0 -15
  291. data/lib/phronomy/execution/invocation_controls.rb +73 -0
  292. data/lib/phronomy/execution/pool_shutdown_error.rb +7 -0
  293. data/lib/phronomy/execution/recursion_limit_error.rb +7 -0
  294. data/lib/phronomy/execution/runnable.rb +24 -0
  295. data/lib/phronomy/execution/runtime_shutdown_error.rb +7 -0
  296. data/lib/phronomy/execution/runtime_shutdown_reentrancy_error.rb +7 -0
  297. data/lib/phronomy/execution/scheduler_reentrancy_error.rb +9 -0
  298. data/lib/phronomy/{engine → execution}/task_result.rb +7 -3
  299. data/lib/phronomy/execution/timeout_error.rb +7 -0
  300. data/lib/phronomy/execution/wait_policy.rb +24 -0
  301. data/lib/phronomy/filter/filter_block_error.rb +14 -0
  302. data/lib/phronomy/generation/generator_verifier/agent_operation.rb +65 -0
  303. data/lib/phronomy/generation/generator_verifier/agent_result_receiver.rb +89 -0
  304. data/lib/phronomy/generation/generator_verifier/default_parser.rb +27 -0
  305. data/lib/phronomy/generation/generator_verifier/pipeline_state.rb +57 -0
  306. data/lib/phronomy/generation/generator_verifier/workflow_builder.rb +112 -0
  307. data/lib/phronomy/generation/generator_verifier.rb +118 -0
  308. data/lib/phronomy/generation/low_confidence_error.rb +14 -0
  309. data/lib/phronomy/llm_adapter/async/async_client.rb +47 -0
  310. data/lib/phronomy/llm_adapter/authentication_error.rb +9 -0
  311. data/lib/phronomy/llm_adapter/backends/ruby_llm.rb +183 -0
  312. data/lib/phronomy/llm_adapter/base.rb +49 -68
  313. data/lib/phronomy/llm_adapter/context_length_error.rb +9 -0
  314. data/lib/phronomy/llm_adapter/invalid_result_error.rb +10 -0
  315. data/lib/phronomy/llm_adapter/message.rb +62 -0
  316. data/lib/phronomy/llm_adapter/rate_limit_error.rb +9 -0
  317. data/lib/phronomy/llm_adapter/request.rb +79 -0
  318. data/lib/phronomy/llm_adapter/response.rb +36 -0
  319. data/lib/phronomy/llm_adapter/stream_chunk.rb +18 -0
  320. data/lib/phronomy/llm_adapter/token_usage.rb +64 -0
  321. data/lib/phronomy/llm_adapter/transport_error.rb +9 -0
  322. data/lib/phronomy/llm_adapter.rb +3 -16
  323. data/lib/phronomy/llm_context_window/token_budget.rb +6 -7
  324. data/lib/phronomy/multi_agent/admission_registry.rb +15 -2
  325. data/lib/phronomy/multi_agent/durable_subagent_coordinator.rb +108 -84
  326. data/lib/phronomy/multi_agent/execution_environment.rb +29 -0
  327. data/lib/phronomy/{agent → multi_agent}/handoff.rb +2 -2
  328. data/lib/phronomy/{agent → multi_agent}/handoff_capability_factory.rb +21 -2
  329. data/lib/phronomy/multi_agent/handoff_participant.rb +79 -0
  330. data/lib/phronomy/{agent → multi_agent}/handoff_policy.rb +1 -1
  331. data/lib/phronomy/multi_agent/handoff_runner.rb +156 -0
  332. data/lib/phronomy/{agent → multi_agent}/handoff_state.rb +2 -2
  333. data/lib/phronomy/multi_agent/orchestrator.rb +61 -75
  334. data/lib/phronomy/multi_agent/persistence/codec.rb +75 -0
  335. data/lib/phronomy/multi_agent/persistence/handoff_state_repository.rb +77 -0
  336. data/lib/phronomy/multi_agent/persistence/records.rb +27 -0
  337. data/lib/phronomy/multi_agent/persistence/storage_schema.rb +21 -0
  338. data/lib/phronomy/multi_agent/persistence/team_execution_repository.rb +124 -0
  339. data/lib/phronomy/multi_agent/persistence/team_repository.rb +69 -0
  340. data/lib/phronomy/multi_agent/reserved_child_admission.rb +88 -0
  341. data/lib/phronomy/multi_agent/runtime_binding/engine_environment.rb +35 -0
  342. data/lib/phronomy/{agent → multi_agent}/shared_state.rb +56 -39
  343. data/lib/phronomy/multi_agent/store.rb +156 -0
  344. data/lib/phronomy/multi_agent/team_coordinator.rb +87 -75
  345. data/lib/phronomy/multi_agent/team_execution.rb +1 -1
  346. data/lib/phronomy/{engine/runtime → multi_agent}/team_ownership_registry.rb +5 -5
  347. data/lib/phronomy/multi_agent/team_root.rb +1 -1
  348. data/lib/phronomy/output_parser/parse_error.rb +7 -0
  349. data/lib/phronomy/persistence/content_repository.rb +26 -0
  350. data/lib/phronomy/persistence/errors.rb +16 -0
  351. data/lib/phronomy/persistence/migration/initial_format_migration.rb +21 -21
  352. data/lib/phronomy/persistence/persistence.rb +23 -0
  353. data/lib/phronomy/persistence/save_outcome.rb +24 -0
  354. data/lib/phronomy/persistence/snapshot_comparison.rb +81 -0
  355. data/lib/phronomy/persistence/storage_boundary.rb +32 -0
  356. data/lib/phronomy/persistence/transaction.rb +86 -0
  357. data/lib/phronomy/persistence_composition/storage_schema.rb +29 -0
  358. data/lib/phronomy/persistence_composition/stores.rb +34 -0
  359. data/lib/phronomy/persistence_composition/unit4_migration.rb +276 -0
  360. data/lib/phronomy/runtime_composition/agent_defaults.rb +11 -0
  361. data/lib/phronomy/runtime_composition/configuration.rb +146 -0
  362. data/lib/phronomy/runtime_composition/configuration_defaults.rb +9 -0
  363. data/lib/phronomy/runtime_composition/evaluation_defaults.rb +7 -0
  364. data/lib/phronomy/runtime_composition/execution_defaults.rb +5 -0
  365. data/lib/phronomy/runtime_composition/generation_defaults.rb +6 -0
  366. data/lib/phronomy/runtime_composition/global_configuration.rb +38 -0
  367. data/lib/phronomy/runtime_composition/global_runtime.rb +19 -0
  368. data/lib/phronomy/runtime_composition/multi_agent_defaults.rb +9 -0
  369. data/lib/phronomy/runtime_composition/workflow_defaults.rb +4 -0
  370. data/lib/phronomy/storage/async/async_client.rb +60 -0
  371. data/lib/phronomy/storage/backend.rb +107 -0
  372. data/lib/phronomy/storage/backends/in_memory.rb +157 -0
  373. data/lib/phronomy/storage/blob_conflict_error.rb +10 -0
  374. data/lib/phronomy/storage/blobs.rb +31 -0
  375. data/lib/phronomy/storage/condition.rb +25 -0
  376. data/lib/phronomy/storage/condition_failed_error.rb +16 -0
  377. data/lib/phronomy/storage/conflict_error.rb +9 -0
  378. data/lib/phronomy/{persistence → storage}/durable_record.rb +12 -12
  379. data/lib/phronomy/storage/entry.rb +33 -0
  380. data/lib/phronomy/storage/guard_ref.rb +13 -0
  381. data/lib/phronomy/storage/not_found_error.rb +9 -0
  382. data/lib/phronomy/storage/record_codec.rb +177 -0
  383. data/lib/phronomy/storage/records.rb +61 -0
  384. data/lib/phronomy/storage/resource.rb +126 -0
  385. data/lib/phronomy/storage/scope.rb +25 -0
  386. data/lib/phronomy/storage/serialization_error.rb +9 -0
  387. data/lib/phronomy/storage/streams.rb +44 -0
  388. data/lib/phronomy/storage/transaction_error.rb +10 -0
  389. data/lib/phronomy/storage/unique_constraint_error.rb +17 -0
  390. data/lib/phronomy/storage/unsupported_backend_error.rb +9 -0
  391. data/lib/phronomy/storage/validation.rb +53 -0
  392. data/lib/phronomy/storage/view.rb +131 -0
  393. data/lib/phronomy/testing/eval/metrics.rb +1 -1
  394. data/lib/phronomy/testing/eval/scorer/llm_judge.rb +19 -3
  395. data/lib/phronomy/testing/persistence_contract/a_content_store.rb +1 -1
  396. data/lib/phronomy/testing/persistence_contract/a_journal_repository.rb +2 -2
  397. data/lib/phronomy/testing/persistence_contract/a_persistence_backend.rb +54 -48
  398. data/lib/phronomy/testing/persistence_contract/a_workflow_state_repository.rb +1 -1
  399. data/lib/phronomy/testing/persistence_contract/an_agent_repository.rb +1 -1
  400. data/lib/phronomy/testing/persistence_contract/an_execution_repository.rb +5 -5
  401. data/lib/phronomy/testing/persistence_contract/coordination_repositories.rb +52 -46
  402. data/lib/phronomy/testing/persistence_contract/neutral_storage_primitives.rb +263 -0
  403. data/lib/phronomy/testing/persistence_contract/storage_transaction_boundaries.rb +124 -0
  404. data/lib/phronomy/testing/persistence_contract.rb +4 -0
  405. data/lib/phronomy/tool/authorization.rb +77 -0
  406. data/lib/phronomy/tool/base.rb +549 -9
  407. data/lib/phronomy/tool/call_request.rb +40 -0
  408. data/lib/phronomy/{agent/tool_definition_set.rb → tool/definition_set.rb} +16 -12
  409. data/lib/phronomy/tool/operation.rb +88 -0
  410. data/lib/phronomy/tool/schema.rb +214 -0
  411. data/lib/phronomy/tool/settings.rb +31 -0
  412. data/lib/phronomy/tool/tool_error.rb +7 -0
  413. data/lib/phronomy/{agent → tool}/tool_executor.rb +10 -7
  414. data/lib/phronomy/tools/agent.rb +22 -90
  415. data/lib/phronomy/tools/mcp.rb +21 -14
  416. data/lib/phronomy/tools/vector_search.rb +4 -4
  417. data/lib/phronomy/tracing/automatic.rb +11 -6
  418. data/lib/phronomy/tracing/base.rb +1 -1
  419. data/lib/phronomy/tracing/observation.rb +37 -0
  420. data/lib/phronomy/tracing/settings.rb +36 -0
  421. data/lib/phronomy/vector_store/{async_backend.rb → async/async_client.rb} +35 -29
  422. data/lib/phronomy/vector_store/{in_memory.rb → backends/in_memory.rb} +12 -10
  423. data/lib/phronomy/vector_store/{pgvector.rb → backends/pgvector.rb} +44 -16
  424. data/lib/phronomy/vector_store/{redis_search.rb → backends/redis_search.rb} +74 -37
  425. data/lib/phronomy/vector_store/base.rb +118 -49
  426. data/lib/phronomy/vector_store/invalid_result_error.rb +12 -0
  427. data/lib/phronomy/vector_store.rb +2 -17
  428. data/lib/phronomy/version.rb +1 -1
  429. data/lib/phronomy/workflow/completion.rb +11 -0
  430. data/lib/phronomy/{workflow.rb → workflow/execution/workflow.rb} +10 -8
  431. data/lib/phronomy/workflow/execution/workflow_action_rules.rb +67 -0
  432. data/lib/phronomy/{workflow_context.rb → workflow/execution/workflow_context.rb} +5 -1
  433. data/lib/phronomy/workflow/execution/workflow_context_ownership_error.rb +7 -0
  434. data/lib/phronomy/workflow/execution/workflow_execution_environment.rb +27 -0
  435. data/lib/phronomy/workflow/execution/workflow_execution_registry.rb +186 -0
  436. data/lib/phronomy/{workflow_runner.rb → workflow/execution/workflow_runner.rb} +145 -115
  437. data/lib/phronomy/workflow/execution/workflow_settings.rb +28 -0
  438. data/lib/phronomy/workflow/persistence/codec.rb +153 -0
  439. data/lib/phronomy/workflow/persistence/state_repository.rb +63 -0
  440. data/lib/phronomy/workflow/persistence/storage_schema.rb +13 -0
  441. data/lib/phronomy/workflow/runtime_binding/workflow_engine_environment.rb +38 -0
  442. data/lib/phronomy/workflow/runtime_binding/workflow_phase_machine_builder.rb +156 -0
  443. data/lib/phronomy/workflow/runtime_binding/workflow_terminal_policy.rb +40 -0
  444. data/lib/phronomy.rb +97 -98
  445. data/scripts/api_snapshot.rb +36 -16
  446. data/scripts/storage_spi_snapshot.rb +36 -0
  447. data/sig/_private/phronomy/agent/settings.rbs +19 -0
  448. data/sig/_private/phronomy/configuration/runtime_settings.rbs +20 -0
  449. data/sig/_private/phronomy/llm_adapter/async/async_client.rbs +10 -0
  450. data/sig/_private/phronomy/multi_agent/store.rbs +8 -0
  451. data/sig/_private/phronomy/persistence/storage_boundary.rbs +7 -0
  452. data/sig/_private/phronomy/tool/settings.rbs +13 -0
  453. data/sig/_private/phronomy/tracing/settings.rbs +15 -0
  454. data/sig/_private/phronomy/workflow/execution/workflow_settings.rbs +13 -0
  455. data/sig/phronomy/agent/admission.rbs +19 -0
  456. data/sig/phronomy/agent/execution_contract.rbs +132 -0
  457. data/sig/phronomy/agent/execution_environment.rbs +20 -0
  458. data/sig/phronomy/agent/store.rbs +73 -0
  459. data/sig/phronomy/agent.rbs +14 -139
  460. data/sig/phronomy/common.rbs +7 -0
  461. data/sig/phronomy/content_store/contracts.rbs +13 -0
  462. data/sig/phronomy/context.rbs +152 -0
  463. data/sig/phronomy/embeddings/async/async_client.rbs +9 -0
  464. data/sig/phronomy/embeddings/base.rbs +17 -0
  465. data/sig/phronomy/engine/concurrency/timer_bindings.rbs +22 -0
  466. data/sig/phronomy/engine/control_bindings.rbs +11 -0
  467. data/sig/phronomy/engine/control_protocols.rbs +23 -0
  468. data/sig/phronomy/engine/execution_binding.rbs +6 -0
  469. data/sig/phronomy/engine/execution_receiver.rbs +15 -0
  470. data/sig/phronomy/engine/observability.rbs +13 -0
  471. data/sig/phronomy/execution/async_operation.rbs +7 -0
  472. data/sig/phronomy/execution/blocking.rbs +5 -0
  473. data/sig/phronomy/execution/concurrency/cancellation_token.rbs +13 -0
  474. data/sig/phronomy/execution/concurrency/deadline.rbs +10 -0
  475. data/sig/phronomy/execution/concurrency/result_composition.rbs +7 -0
  476. data/sig/phronomy/execution/control_protocols.rbs +18 -0
  477. data/sig/phronomy/execution/errors.rbs +7 -0
  478. data/sig/phronomy/execution/execution.rbs +41 -0
  479. data/sig/phronomy/execution/execution_receiver.rbs +40 -0
  480. data/sig/phronomy/execution/execution_scope_state.rbs +9 -0
  481. data/sig/phronomy/execution/invocation_context.rbs +18 -0
  482. data/sig/phronomy/execution/invocation_controls.rbs +13 -0
  483. data/sig/phronomy/execution/task_result.rbs +29 -0
  484. data/sig/phronomy/extensions.rbs +0 -21
  485. data/sig/phronomy/handoff.rbs +12 -4
  486. data/sig/phronomy/llm_adapter/backends/ruby_llm.rbs +6 -0
  487. data/sig/phronomy/llm_adapter/base.rbs +14 -0
  488. data/sig/phronomy/llm_adapter/usage.rbs +14 -0
  489. data/sig/phronomy/llm_adapter/values.rbs +43 -0
  490. data/sig/phronomy/llm_context_window.rbs +10 -0
  491. data/sig/phronomy/multi_agent/reserved_child_admission.rbs +8 -0
  492. data/sig/phronomy/multi_agent/store.rbs +54 -0
  493. data/sig/phronomy/multi_agent.rbs +24 -3
  494. data/sig/phronomy/persistence/errors.rbs +18 -0
  495. data/sig/phronomy/persistence/migration.rbs +13 -0
  496. data/sig/phronomy/persistence/transaction.rbs +19 -0
  497. data/sig/phronomy/persistence.rbs +3 -185
  498. data/sig/phronomy/persistence_composition.rbs +19 -0
  499. data/sig/phronomy/runtime_composition/configuration.rbs +36 -0
  500. data/sig/phronomy/storage/async/async_client.rbs +8 -0
  501. data/sig/phronomy/storage/backends/in_memory.rbs +9 -0
  502. data/sig/phronomy/storage/contracts.rbs +160 -0
  503. data/sig/phronomy/tool.rbs +58 -5
  504. data/sig/phronomy/tracing/observation.rbs +7 -0
  505. data/sig/phronomy/vector_store/async/async_client.rbs +13 -0
  506. data/sig/phronomy/vector_store/base.rbs +23 -0
  507. data/sig/phronomy/workflow/execution/workflow_execution_environment.rbs +14 -0
  508. data/sig/phronomy/workflow/runtime_binding/workflow_terminal_policy.rbs +7 -0
  509. data/sig/phronomy/workflow/store.rbs +8 -0
  510. data/sig/phronomy/workflow.rbs +1 -1
  511. data/sig/phronomy.rbs +0 -49
  512. metadata +462 -130
  513. data/lib/phronomy/agent/agent_invocation_session_builder.rb +0 -584
  514. data/lib/phronomy/agent/concerns/before_llm_input.rb +0 -66
  515. data/lib/phronomy/agent/concerns/error_translation.rb +0 -45
  516. data/lib/phronomy/agent/concerns/filterable.rb +0 -184
  517. data/lib/phronomy/agent/context/capability/base.rb +0 -507
  518. data/lib/phronomy/agent/context/instruction/prompt_template.rb +0 -102
  519. data/lib/phronomy/agent/context_parts/validators/final_budget_validator.rb +0 -37
  520. data/lib/phronomy/agent/context_policies/default.rb +0 -142
  521. data/lib/phronomy/agent/execution_coordinator.rb +0 -3151
  522. data/lib/phronomy/agent/handoff_execution_coordinator.rb +0 -143
  523. data/lib/phronomy/agent/handoff_request.rb +0 -45
  524. data/lib/phronomy/agent/handoff_runner.rb +0 -178
  525. data/lib/phronomy/agent/phase_machine_builder.rb +0 -152
  526. data/lib/phronomy/agent/provider_call_outcome.rb +0 -106
  527. data/lib/phronomy/agent/recovery_support.rb +0 -504
  528. data/lib/phronomy/agent/selection/candidate.rb +0 -53
  529. data/lib/phronomy/agent/selection/constraint.rb +0 -49
  530. data/lib/phronomy/agent/token_budget_resolver.rb +0 -70
  531. data/lib/phronomy/agent/tool_call_intercepted.rb +0 -29
  532. data/lib/phronomy/agent/tool_invocation_session_builder.rb +0 -276
  533. data/lib/phronomy/configuration.rb +0 -59
  534. data/lib/phronomy/engine/concurrency/cancellation_scope.rb +0 -124
  535. data/lib/phronomy/engine/concurrency/operation_binding.rb +0 -43
  536. data/lib/phronomy/generator_verifier.rb +0 -369
  537. data/lib/phronomy/invalid_context_budget_configuration_error.rb +0 -8
  538. data/lib/phronomy/llm_adapter/ruby_llm.rb +0 -47
  539. data/lib/phronomy/metrics.rb +0 -26
  540. data/lib/phronomy/multi_agent/parallel_tool_chat.rb +0 -116
  541. data/lib/phronomy/persistence/durable_codec.rb +0 -706
  542. data/lib/phronomy/persistence/in_memory.rb +0 -690
  543. data/lib/phronomy/persistence/repository_facades.rb +0 -535
  544. data/lib/phronomy/persistence.rb +0 -276
  545. data/lib/phronomy/recovery.rb +0 -186
  546. data/lib/phronomy/ruby_llm_patches.rb +0 -24
  547. data/lib/phronomy/runnable.rb +0 -54
  548. data/lib/phronomy/token_usage.rb +0 -85
  549. data/lib/phronomy/vector_store/embeddings/base.rb +0 -44
  550. data/lib/phronomy/vector_store/embeddings/ruby_llm_embeddings.rb +0 -47
  551. data/lib/phronomy/workflow/phase_machine_builder.rb +0 -205
  552. data/lib/phronomy/workflow_recovery.rb +0 -123
  553. data/sig/phronomy/llm_adapter.rbs +0 -11
  554. data/sig/phronomy/runtime.rbs +0 -76
  555. /data/lib/phronomy/agent/{llm_operation_result.rb → execution/llm_operation_result.rb} +0 -0
  556. /data/lib/phronomy/{canonical_json.rb → common/canonical_json.rb} +0 -0
  557. /data/lib/phronomy/{event.rb → engine/event.rb} +0 -0
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Agent
5
+ module Persistence
6
+ # Owns Agent root encoding and validates returned storage metadata.
7
+ # @api private
8
+ class AgentRepository
9
+ def initialize(view) = @view = view
10
+
11
+ def create(root)
12
+ Phronomy::Persistence::StorageBoundary.call do
13
+ record = Codec.encode_agent_root(root)
14
+ @view.atomic do |bound|
15
+ entry = bound.records(StorageSchema::ROOTS).insert(key: root.agent_id.to_s,
16
+ revision: Integer(root.agent_revision), attributes: {}, record: record)
17
+ decode(entry, root.agent_id, revision: root.agent_revision)
18
+ end
19
+ end
20
+ end
21
+
22
+ def load(agent_id)
23
+ Phronomy::Persistence::StorageBoundary.call do
24
+ value = @view.atomic do |bound|
25
+ entry = bound.records(StorageSchema::ROOTS).read(agent_id.to_s)
26
+ entry && decode(entry, agent_id)
27
+ end
28
+ value || raise(Phronomy::Storage::NotFoundError, "Agent not found: #{agent_id}")
29
+ end
30
+ end
31
+
32
+ def save(agent_id, expected_revision:, root:)
33
+ Phronomy::Persistence::StorageBoundary.call do
34
+ expected = Integer(expected_revision)
35
+ revision = Integer(root.agent_revision)
36
+ raise Phronomy::Storage::ConflictError, "root save must advance revision exactly once" unless revision == expected + 1
37
+ unless root.agent_id.to_s == agent_id.to_s
38
+ raise Phronomy::Storage::SerializationError, "Agent root identity mismatch"
39
+ end
40
+ record = Codec.encode_agent_root(root)
41
+ @view.atomic do |bound|
42
+ entry = bound.records(StorageSchema::ROOTS).replace(key: agent_id.to_s,
43
+ expected_revision: expected, next_revision: revision, attributes: {}, record: record)
44
+ decode(entry, agent_id, revision: revision)
45
+ end
46
+ end
47
+ end
48
+
49
+ def delete(agent_id)
50
+ Phronomy::Persistence::StorageBoundary.call do
51
+ @view.records(StorageSchema::ROOTS).delete(key: agent_id.to_s)
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def decode(entry, agent_id, revision: nil)
58
+ root = Codec.decode_agent_root(entry.record)
59
+ valid = entry.key == agent_id.to_s && root.agent_id == entry.key &&
60
+ root.agent_revision == entry.revision && (!revision || revision == entry.revision)
61
+ unless valid
62
+ raise Phronomy::Storage::SerializationError, "backend returned another Agent identity/revision"
63
+ end
64
+ root
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Agent
5
+ module Persistence
6
+ # Cancellation is monotonic and independent of the in-flight execution
7
+ # revision. Recording intent cannot invalidate a worker's captured base.
8
+ # @api private
9
+ class CancellationRepository
10
+ include Phronomy::Storage::RecordCodec
11
+
12
+ def initialize(view) = @view = view
13
+
14
+ def requested?(agent_id, execution_id)
15
+ Phronomy::Persistence::StorageBoundary.call do
16
+ @view.atomic do |view|
17
+ entry = view.records(StorageSchema::CANCELLATIONS).read(execution_id)
18
+ next false unless entry
19
+ payload = current_payload!(entry.record, record_type: "phronomy.agent_cancellation", format_version: "0.1",
20
+ keys: %w[agent_id execution_id], label: "Agent cancellation")
21
+ unless payload == {"agent_id" => agent_id, "execution_id" => execution_id} && entry.attributes == {owner: agent_id}
22
+ raise Phronomy::Storage::SerializationError, "Cancellation owner mismatch"
23
+ end
24
+ true
25
+ end
26
+ end
27
+ end
28
+
29
+ def request(agent_id, execution_id)
30
+ Phronomy::Persistence::StorageBoundary.call do
31
+ @view.atomic do |view|
32
+ view.check!(guards: [Phronomy::Storage::GuardRef.new(resource: StorageSchema::ROOTS, key: agent_id)], conditions: [])
33
+ unless self.class.new(view).requested?(agent_id, execution_id)
34
+ view.records(StorageSchema::CANCELLATIONS).insert(key: execution_id, revision: 0,
35
+ attributes: {owner: agent_id}, record: build_record("phronomy.agent_cancellation", "0.1",
36
+ {"agent_id" => agent_id, "execution_id" => execution_id}))
37
+ end
38
+ end
39
+ end
40
+ true
41
+ end
42
+
43
+ def delete_for_agent(agent_id)
44
+ Phronomy::Persistence::StorageBoundary.call do
45
+ @view.records(StorageSchema::CANCELLATIONS).delete_matching(index: :owner, equals: {owner: agent_id})
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,338 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Agent
5
+ module Persistence
6
+ # Current-format record schemas and conversion for this domain.
7
+ # @api private
8
+ module Codec
9
+ extend Phronomy::Storage::RecordCodec
10
+
11
+ AGENT_ROOT_RECORD_TYPE = "phronomy.agent_root"
12
+ AGENT_ROOT_FORMAT_VERSION = "0.1"
13
+ AGENT_EXECUTION_RECORD_TYPE = "phronomy.agent_execution"
14
+ AGENT_EXECUTION_FORMAT_VERSION = "0.2"
15
+ JOURNAL_RECORD_TYPE = "phronomy.journal_record"
16
+ JOURNAL_FORMAT_VERSION = "0.1"
17
+ AGENT_ROOT_KEYS = %w[
18
+ agent_id agent_definition_id agent_definition_version agent_revision
19
+ context_revision journal_position lifecycle_status transcript_generation
20
+ created_at updated_at metadata
21
+ ].freeze
22
+
23
+ AGENT_EXECUTION_KEYS = %w[
24
+ execution_id agent_id execution_revision status phase
25
+ base_agent_revision base_context_revision base_journal_position
26
+ working_records llm_calls approval_request result_ref error_ref
27
+ created_at updated_at terminal_reason metadata
28
+ ].freeze
29
+
30
+ JOURNAL_RECORD_KEYS = %w[
31
+ record_id agent_id sequence execution_id llm_call_id kind channel role
32
+ content_ref parent_id causation_id visibility context_generation
33
+ context_candidate occurred_at metadata
34
+ ].freeze
35
+
36
+ LLM_CALL_RECORD_KEYS = %w[
37
+ llm_call_id execution_id sequence status manifest_ref output_ref
38
+ error_ref usage_ref started_at completed_at metadata
39
+ ].freeze
40
+
41
+ APPROVAL_REQUEST_KEYS = %w[id execution_id items created_at].freeze
42
+
43
+ APPROVAL_REQUEST_OPTIONAL_KEYS = %w[approved].freeze
44
+
45
+ APPROVAL_ITEM_KEYS = %w[
46
+ tool_invocation_id tool_call_id tool_name arguments facts reason origin metadata
47
+ ].freeze
48
+
49
+ module_function
50
+
51
+ def encode_agent_root(root)
52
+ payload = top_level_string_keys(root.to_h, label: "AgentRoot payload")
53
+ payload["lifecycle_status"] = root.lifecycle_status.to_s
54
+ validate_agent_root_payload!(payload)
55
+ build_record(AGENT_ROOT_RECORD_TYPE, AGENT_ROOT_FORMAT_VERSION, payload)
56
+ rescue Phronomy::Storage::SerializationError
57
+ raise
58
+ rescue => error
59
+ serialization_error("cannot encode AgentRoot", error)
60
+ end
61
+
62
+ def decode_agent_root(record)
63
+ payload = current_payload!(
64
+ record,
65
+ record_type: AGENT_ROOT_RECORD_TYPE,
66
+ format_version: AGENT_ROOT_FORMAT_VERSION,
67
+ keys: AGENT_ROOT_KEYS,
68
+ label: "AgentRoot payload"
69
+ )
70
+ validate_agent_root_payload!(payload)
71
+ Phronomy::Agent::AgentRoot.from_h(payload)
72
+ rescue Phronomy::Storage::SerializationError
73
+ raise
74
+ rescue => error
75
+ serialization_error("cannot decode AgentRoot", error)
76
+ end
77
+
78
+ def encode_agent_execution(execution)
79
+ payload = top_level_string_keys(execution.to_h, label: "AgentExecution payload")
80
+ payload["status"] = execution.status.to_s
81
+ payload["phase"] = execution.phase.to_s
82
+ payload["working_records"] = execution.working_records.map do |record|
83
+ journal_payload(record, require_sequence: false)
84
+ end
85
+ payload["llm_calls"] = execution.llm_calls.map do |call|
86
+ llm_call_payload(call)
87
+ end
88
+ validate_agent_execution_payload!(payload)
89
+ build_record(
90
+ AGENT_EXECUTION_RECORD_TYPE,
91
+ AGENT_EXECUTION_FORMAT_VERSION,
92
+ payload
93
+ )
94
+ rescue Phronomy::Storage::SerializationError
95
+ raise
96
+ rescue => error
97
+ serialization_error("cannot encode AgentExecution", error)
98
+ end
99
+
100
+ def decode_agent_execution(record)
101
+ payload = current_payload!(
102
+ record,
103
+ record_type: AGENT_EXECUTION_RECORD_TYPE,
104
+ format_version: AGENT_EXECUTION_FORMAT_VERSION,
105
+ keys: AGENT_EXECUTION_KEYS,
106
+ label: "AgentExecution payload"
107
+ )
108
+ validate_agent_execution_payload!(payload)
109
+ Phronomy::Agent::AgentExecution.from_h(payload)
110
+ rescue Phronomy::Storage::SerializationError
111
+ raise
112
+ rescue => error
113
+ serialization_error("cannot decode AgentExecution", error)
114
+ end
115
+
116
+ def encode_journal_record(journal_record)
117
+ payload = journal_payload(journal_record, require_sequence: true)
118
+ build_record(JOURNAL_RECORD_TYPE, JOURNAL_FORMAT_VERSION, payload)
119
+ rescue Phronomy::Storage::SerializationError
120
+ raise
121
+ rescue => error
122
+ serialization_error("cannot encode JournalRecord", error)
123
+ end
124
+
125
+ def decode_journal_record(record)
126
+ payload = current_payload!(
127
+ record,
128
+ record_type: JOURNAL_RECORD_TYPE,
129
+ format_version: JOURNAL_FORMAT_VERSION,
130
+ keys: JOURNAL_RECORD_KEYS,
131
+ label: "JournalRecord payload"
132
+ )
133
+ validate_journal_payload!(payload, label: "JournalRecord payload", require_sequence: true)
134
+ Phronomy::Agent::JournalRecord.from_h(payload)
135
+ rescue Phronomy::Storage::SerializationError
136
+ raise
137
+ rescue => error
138
+ serialization_error("cannot decode JournalRecord", error)
139
+ end
140
+
141
+ def validate_agent_root_payload!(payload)
142
+ validate_exact_keys!(payload, AGENT_ROOT_KEYS, label: "AgentRoot payload")
143
+ require_nonempty_string!(payload, "agent_id", label: "AgentRoot payload")
144
+ require_nonempty_string!(payload, "agent_definition_id", label: "AgentRoot payload")
145
+ require_integer!(payload, "agent_definition_version", label: "AgentRoot payload")
146
+ require_nonnegative_integer!(payload, "agent_revision", label: "AgentRoot payload")
147
+ require_nonnegative_integer!(payload, "context_revision", label: "AgentRoot payload")
148
+ require_nonnegative_integer!(payload, "journal_position", label: "AgentRoot payload")
149
+ require_enum_string!(
150
+ payload,
151
+ "lifecycle_status",
152
+ Phronomy::Agent::AgentRoot::LIFECYCLE_STATUSES.map(&:to_s),
153
+ label: "AgentRoot payload"
154
+ )
155
+ require_nonnegative_integer!(payload, "transcript_generation", label: "AgentRoot payload")
156
+ require_nonempty_string!(payload, "created_at", label: "AgentRoot payload")
157
+ require_nonempty_string!(payload, "updated_at", label: "AgentRoot payload")
158
+ require_canonical_hash!(payload, "metadata", label: "AgentRoot payload")
159
+ payload
160
+ end
161
+
162
+ def validate_agent_execution_payload!(payload)
163
+ validate_exact_keys!(payload, AGENT_EXECUTION_KEYS, label: "AgentExecution payload")
164
+ require_nonempty_string!(payload, "execution_id", label: "AgentExecution payload")
165
+ require_nonempty_string!(payload, "agent_id", label: "AgentExecution payload")
166
+ require_nonnegative_integer!(payload, "execution_revision", label: "AgentExecution payload")
167
+ require_enum_string!(
168
+ payload,
169
+ "status",
170
+ Phronomy::Agent::AgentExecution::TRANSITIONS.keys.map(&:to_s),
171
+ label: "AgentExecution payload"
172
+ )
173
+ require_nonempty_string!(payload, "phase", label: "AgentExecution payload")
174
+ require_nonnegative_integer!(payload, "base_agent_revision", label: "AgentExecution payload")
175
+ require_nonnegative_integer!(payload, "base_context_revision", label: "AgentExecution payload")
176
+ require_nonnegative_integer!(payload, "base_journal_position", label: "AgentExecution payload")
177
+ require_optional_string!(payload, "result_ref", label: "AgentExecution payload")
178
+ require_optional_string!(payload, "error_ref", label: "AgentExecution payload")
179
+ require_nonempty_string!(payload, "created_at", label: "AgentExecution payload")
180
+ require_nonempty_string!(payload, "updated_at", label: "AgentExecution payload")
181
+ require_optional_string!(payload, "terminal_reason", label: "AgentExecution payload")
182
+ require_canonical_hash!(payload, "metadata", label: "AgentExecution payload")
183
+
184
+ working_records = payload.fetch("working_records")
185
+ unless working_records.is_a?(Array)
186
+ raise Phronomy::Storage::SerializationError,
187
+ "AgentExecution payload working_records must be an Array"
188
+ end
189
+ working_records.each_with_index do |record, index|
190
+ validate_journal_payload!(
191
+ record,
192
+ label: "AgentExecution working_records[#{index}]",
193
+ require_sequence: false
194
+ )
195
+ record_agent_id = record.fetch("agent_id")
196
+ unless record_agent_id == payload.fetch("agent_id")
197
+ raise Phronomy::Storage::SerializationError,
198
+ "AgentExecution working_records[#{index}] agent_id mismatch"
199
+ end
200
+ record_execution_id = record.fetch("execution_id")
201
+ if record_execution_id && record_execution_id != payload.fetch("execution_id")
202
+ raise Phronomy::Storage::SerializationError,
203
+ "AgentExecution working_records[#{index}] execution_id mismatch"
204
+ end
205
+ end
206
+
207
+ llm_calls = payload.fetch("llm_calls")
208
+ unless llm_calls.is_a?(Array)
209
+ raise Phronomy::Storage::SerializationError,
210
+ "AgentExecution payload llm_calls must be an Array"
211
+ end
212
+ llm_calls.each_with_index do |call, index|
213
+ validate_llm_call_payload!(call, label: "AgentExecution llm_calls[#{index}]")
214
+ unless call.fetch("execution_id") == payload.fetch("execution_id")
215
+ raise Phronomy::Storage::SerializationError,
216
+ "AgentExecution llm_calls[#{index}] execution_id mismatch"
217
+ end
218
+ end
219
+
220
+ validate_approval_request!(
221
+ payload.fetch("approval_request"),
222
+ execution_id: payload.fetch("execution_id")
223
+ )
224
+ payload
225
+ end
226
+
227
+ def validate_journal_payload!(payload, label:, require_sequence:)
228
+ validate_exact_keys!(payload, JOURNAL_RECORD_KEYS, label: label)
229
+ require_nonempty_string!(payload, "record_id", label: label)
230
+ require_nonempty_string!(payload, "agent_id", label: label)
231
+ sequence = payload.fetch("sequence")
232
+ if require_sequence
233
+ unless sequence.is_a?(Integer) && sequence.positive?
234
+ raise Phronomy::Storage::SerializationError,
235
+ "#{label} sequence must be a positive Integer"
236
+ end
237
+ elsif !(sequence.nil? || (sequence.is_a?(Integer) && sequence.positive?))
238
+ raise Phronomy::Storage::SerializationError,
239
+ "#{label} sequence must be nil or a positive Integer"
240
+ end
241
+ require_optional_string!(payload, "execution_id", label: label)
242
+ require_optional_string!(payload, "llm_call_id", label: label)
243
+ require_nonempty_string!(payload, "kind", label: label)
244
+ require_nonempty_string!(payload, "channel", label: label)
245
+ require_optional_string!(payload, "role", label: label)
246
+ require_optional_string!(payload, "content_ref", label: label)
247
+ require_optional_string!(payload, "parent_id", label: label)
248
+ require_optional_string!(payload, "causation_id", label: label)
249
+ require_nonempty_string!(payload, "visibility", label: label)
250
+ require_nonnegative_integer!(payload, "context_generation", label: label)
251
+ require_boolean!(payload, "context_candidate", label: label)
252
+ require_nonempty_string!(payload, "occurred_at", label: label)
253
+ require_canonical_hash!(payload, "metadata", label: label)
254
+ payload
255
+ end
256
+
257
+ def validate_llm_call_payload!(payload, label:)
258
+ validate_exact_keys!(payload, LLM_CALL_RECORD_KEYS, label: label)
259
+ require_nonempty_string!(payload, "llm_call_id", label: label)
260
+ require_nonempty_string!(payload, "execution_id", label: label)
261
+ require_positive_integer!(payload, "sequence", label: label)
262
+ require_enum_string!(
263
+ payload,
264
+ "status",
265
+ Phronomy::Agent::LLMCallRecord::STATUSES.map(&:to_s),
266
+ label: label
267
+ )
268
+ require_nonempty_string!(payload, "manifest_ref", label: label)
269
+ require_optional_string!(payload, "output_ref", label: label)
270
+ require_optional_string!(payload, "error_ref", label: label)
271
+ require_optional_string!(payload, "usage_ref", label: label)
272
+ require_nonempty_string!(payload, "started_at", label: label)
273
+ require_optional_string!(payload, "completed_at", label: label)
274
+ require_canonical_hash!(payload, "metadata", label: label)
275
+ payload
276
+ end
277
+
278
+ def validate_approval_request!(request, execution_id:)
279
+ return if request.nil?
280
+
281
+ validate_allowed_keys!(
282
+ request,
283
+ required_keys: APPROVAL_REQUEST_KEYS,
284
+ optional_keys: APPROVAL_REQUEST_OPTIONAL_KEYS,
285
+ label: "approval_request"
286
+ )
287
+ require_nonempty_string!(request, "id", label: "approval_request")
288
+ require_nonempty_string!(request, "execution_id", label: "approval_request")
289
+ unless request.fetch("execution_id") == execution_id
290
+ raise Phronomy::Storage::SerializationError,
291
+ "approval_request execution_id mismatch"
292
+ end
293
+ require_nonempty_string!(request, "created_at", label: "approval_request")
294
+ if request.key?("approved") && !boolean?(request.fetch("approved"))
295
+ raise Phronomy::Storage::SerializationError,
296
+ "approval_request approved must be true or false"
297
+ end
298
+
299
+ items = request.fetch("items")
300
+ unless items.is_a?(Array) && !items.empty?
301
+ raise Phronomy::Storage::SerializationError,
302
+ "approval_request items must be a non-empty Array"
303
+ end
304
+ items.each_with_index do |item, index|
305
+ item_label = "approval_request items[#{index}]"
306
+ validate_exact_keys!(item, APPROVAL_ITEM_KEYS, label: item_label)
307
+ require_nonempty_string!(item, "tool_invocation_id", label: item_label)
308
+ require_optional_string!(item, "tool_call_id", label: item_label)
309
+ require_nonempty_string!(item, "tool_name", label: item_label)
310
+ require_canonical_hash!(item, "arguments", label: item_label)
311
+ require_canonical_hash!(item, "facts", label: item_label)
312
+ require_optional_string!(item, "reason", label: item_label)
313
+ require_nonempty_string!(item, "origin", label: item_label)
314
+ require_canonical_hash!(item, "metadata", label: item_label)
315
+ end
316
+ request
317
+ end
318
+
319
+ def journal_payload(record, require_sequence:)
320
+ payload = top_level_string_keys(record.to_h, label: "JournalRecord payload")
321
+ %w[kind channel role visibility].each do |key|
322
+ value = payload[key]
323
+ payload[key] = value.to_s if value
324
+ end
325
+ validate_journal_payload!(payload, label: "JournalRecord payload", require_sequence: require_sequence)
326
+ payload
327
+ end
328
+
329
+ def llm_call_payload(call)
330
+ payload = top_level_string_keys(call.to_h, label: "LLMCallRecord payload")
331
+ payload["status"] = call.status.to_s
332
+ validate_llm_call_payload!(payload, label: "LLMCallRecord payload")
333
+ payload
334
+ end
335
+ end
336
+ end
337
+ end
338
+ end
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Agent
5
+ module Persistence
6
+ # Owns execution activity, ownership, admission and durable record encoding.
7
+ # @api private
8
+ class ExecutionRepository
9
+ def initialize(view) = @view = view
10
+
11
+ def create_active(execution)
12
+ Phronomy::Persistence::StorageBoundary.call do
13
+ raise Phronomy::Storage::SerializationError, "create_active requires an active AgentExecution" unless execution.active?
14
+ record = Codec.encode_agent_execution(execution)
15
+ admission do
16
+ @view.atomic do |bound|
17
+ entry = bound.records(StorageSchema::EXECUTIONS).insert(key: execution.execution_id.to_s,
18
+ revision: Integer(execution.execution_revision), attributes: attributes(execution), record: record)
19
+ decode(entry, execution.execution_id, owner: execution.agent_id,
20
+ revision: execution.execution_revision, active: true)
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ def load(execution_id)
27
+ Phronomy::Persistence::StorageBoundary.call do
28
+ value = @view.atomic do |bound|
29
+ entry = bound.records(StorageSchema::EXECUTIONS).read(execution_id.to_s)
30
+ entry && decode(entry, execution_id)
31
+ end
32
+ value || raise(Phronomy::Storage::NotFoundError, "Execution not found: #{execution_id}")
33
+ end
34
+ end
35
+
36
+ def save(execution_id, expected_revision:, execution:)
37
+ Phronomy::Persistence::StorageBoundary.call do
38
+ expected = Integer(expected_revision)
39
+ revision = Integer(execution.execution_revision)
40
+ raise Phronomy::Storage::ConflictError, "execution save must advance revision exactly once" unless revision == expected + 1
41
+ unless execution.execution_id.to_s == execution_id.to_s
42
+ raise Phronomy::Storage::SerializationError, "Execution identity mismatch"
43
+ end
44
+ record = Codec.encode_agent_execution(execution)
45
+ admission do
46
+ @view.atomic do |bound|
47
+ entry = bound.records(StorageSchema::EXECUTIONS).replace(key: execution_id.to_s,
48
+ expected_revision: expected, next_revision: revision, attributes: attributes(execution),
49
+ expected_attributes: {}, record: record)
50
+ decode(entry, execution_id, owner: execution.agent_id, revision: revision, active: execution.active?)
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ def list_active(agent_id)
57
+ Phronomy::Persistence::StorageBoundary.call do
58
+ @view.atomic do |bound|
59
+ bound.records(StorageSchema::EXECUTIONS).scan(index: :owner_active,
60
+ equals: {owner: agent_id.to_s, active: true}).map { |entry| decode(entry, entry.key, owner: agent_id, active: true) }.freeze
61
+ end
62
+ end
63
+ end
64
+
65
+ def list(agent_id, after: nil, limit: 100)
66
+ Phronomy::Persistence::StorageBoundary.call do
67
+ raise ArgumentError, "limit must be a positive Integer" unless limit.is_a?(Integer) && limit.positive?
68
+ @view.atomic do |bound|
69
+ bound.records(StorageSchema::EXECUTIONS).scan(index: :owner, equals: {owner: agent_id.to_s},
70
+ after: after&.to_s, limit: limit).map { |entry| decode(entry, entry.key, owner: agent_id) }.freeze
71
+ end
72
+ end
73
+ end
74
+
75
+ def delete(execution_id)
76
+ Phronomy::Persistence::StorageBoundary.call do
77
+ @view.records(StorageSchema::EXECUTIONS).delete(key: execution_id.to_s)
78
+ end
79
+ end
80
+
81
+ def delete_for_agent(agent_id)
82
+ Phronomy::Persistence::StorageBoundary.call do
83
+ @view.records(StorageSchema::EXECUTIONS).delete_matching(index: :owner, equals: {owner: agent_id.to_s})
84
+ end
85
+ end
86
+
87
+ def assert_idle!(agent_id)
88
+ Phronomy::Persistence::StorageBoundary.call do
89
+ condition = Phronomy::Storage::Condition::NoRows.new(resource: StorageSchema::EXECUTIONS,
90
+ index: :owner_active, equals: {owner: agent_id.to_s, active: true})
91
+ @view.check!(guards: [Phronomy::Storage::GuardRef.new(resource: StorageSchema::ROOTS, key: agent_id.to_s)],
92
+ conditions: [condition])
93
+ rescue Phronomy::Storage::ConditionFailedError => error
94
+ raise unless error.condition.equal?(condition)
95
+ raise Phronomy::AgentBusyError, error.message
96
+ end
97
+ end
98
+
99
+ private
100
+
101
+ def attributes(execution) = {owner: execution.agent_id.to_s, active: execution.active?}
102
+
103
+ def admission
104
+ yield
105
+ rescue Phronomy::Storage::UniqueConstraintError => error
106
+ raise unless error.resource == StorageSchema::EXECUTIONS.id && error.constraint == :one_active_owner
107
+ raise Phronomy::AgentBusyError, error.message
108
+ end
109
+
110
+ def decode(entry, execution_id, owner: nil, revision: nil, active: nil)
111
+ execution = Codec.decode_agent_execution(entry.record)
112
+ valid = entry.key == execution_id.to_s && execution.execution_id == entry.key &&
113
+ execution.execution_revision == entry.revision && attributes(execution) == entry.attributes &&
114
+ (!owner || execution.agent_id == owner.to_s) && (!revision || execution.execution_revision == revision) &&
115
+ (active.nil? || execution.active? == active)
116
+ unless valid
117
+ raise Phronomy::Storage::SerializationError, "backend returned another Agent Execution identity/metadata"
118
+ end
119
+ execution
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Agent
5
+ module Persistence
6
+ # Assigns domain sequence numbers and validates opaque stream entries.
7
+ # @api private
8
+ class JournalRepository
9
+ def initialize(view) = @view = view
10
+
11
+ def append(agent_id, expected_position:, records:)
12
+ Phronomy::Persistence::StorageBoundary.call do
13
+ expected = Integer(expected_position)
14
+ entries = Array(records).each_with_index.map do |record, index|
15
+ unless record.agent_id.to_s == agent_id.to_s
16
+ raise Phronomy::Storage::SerializationError, "Journal record Agent mismatch"
17
+ end
18
+ sequenced = record.with_sequence(expected + index + 1)
19
+ Phronomy::Storage::Entry::Append.new(id: sequenced.record_id.to_s, record: Codec.encode_journal_record(sequenced))
20
+ end
21
+ @view.atomic do |bound|
22
+ stored = bound.streams(StorageSchema::JOURNAL).append(stream: agent_id.to_s,
23
+ expected_head: expected, entries: entries)
24
+ decode(stored, agent_id, after: expected)
25
+ end
26
+ end
27
+ end
28
+
29
+ def read(agent_id, after: nil, limit: nil)
30
+ Phronomy::Persistence::StorageBoundary.call do
31
+ position = after.nil? ? 0 : Integer(after)
32
+ count = limit.nil? ? nil : Integer(limit)
33
+ @view.atomic do |bound|
34
+ stored = (count == 0) ? [] : bound.streams(StorageSchema::JOURNAL).read(stream: agent_id.to_s, after: position, limit: count)
35
+ decode(stored, agent_id, after: position)
36
+ end
37
+ end
38
+ end
39
+
40
+ def head(agent_id)
41
+ Phronomy::Persistence::StorageBoundary.call { @view.streams(StorageSchema::JOURNAL).head(stream: agent_id.to_s) }
42
+ end
43
+
44
+ def delete(agent_id)
45
+ Phronomy::Persistence::StorageBoundary.call { @view.streams(StorageSchema::JOURNAL).delete(stream: agent_id.to_s) }
46
+ end
47
+
48
+ private
49
+
50
+ def decode(entries, agent_id, after:)
51
+ entries.each_with_index.map do |entry, index|
52
+ record = Codec.decode_journal_record(entry.record)
53
+ unless record.agent_id == agent_id.to_s && record.record_id == entry.id &&
54
+ record.sequence == entry.position && entry.position == after + index + 1
55
+ raise Phronomy::Storage::SerializationError, "backend returned another Journal identity/sequence"
56
+ end
57
+ record
58
+ end.freeze
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Agent
5
+ module Persistence
6
+ # Storage implementation of Agent's own record operations.
7
+ # @api private
8
+ class Records
9
+ attr_reader :contents, :agents, :journals, :executions, :retentions, :cancellations
10
+
11
+ def initialize(view)
12
+ @view = view
13
+ @contents = Phronomy::Persistence::ContentRepository.new(Phronomy::ContentStore::StoredContents.new(view))
14
+ @agents = AgentRepository.new(view)
15
+ @journals = JournalRepository.new(view)
16
+ @executions = ExecutionRepository.new(view)
17
+ @retentions = RetentionRepository.new(view)
18
+ @cancellations = CancellationRepository.new(view)
19
+ end
20
+
21
+ def assert_agent_watermark!(agent_id:, agent_revision:, journal_position:)
22
+ Watermark.new(@view).check!(agent_id: agent_id, agent_revision: agent_revision, journal_position: journal_position)
23
+ end
24
+
25
+ def guard_agent!(agent_id)
26
+ Phronomy::Persistence::StorageBoundary.call do
27
+ @view.check!(guards: [Phronomy::Storage::GuardRef.new(resource: StorageSchema::ROOTS, key: agent_id)], conditions: [])
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end