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,10 +1,17 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../canonical_json"
4
+
3
5
  module Phronomy
4
- module Agent
6
+ module Values
7
+ # Shared value operations for snapshots, recovery facts, and result views.
8
+ # Hash, Array, and String trees are copied and frozen; other values retain
9
+ # their identity. Canonical JSON validation is a separate operation.
10
+ # @api private
5
11
  module Immutable
6
12
  module_function
7
13
 
14
+ # @api private
8
15
  def copy(value)
9
16
  case value
10
17
  when Hash
@@ -20,6 +27,7 @@ module Phronomy
20
27
  end
21
28
  end
22
29
 
30
+ # @api private
23
31
  def validate_canonical_json!(value, label: "value")
24
32
  Phronomy::CanonicalJSON.dump(value)
25
33
  true
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Values
5
+ # Prepares a Ruby value tree for JSON serialization. Hash keys and Symbol
6
+ # values become Strings; containers are rebuilt and scalar objects retained.
7
+ # This neither freezes values nor validates canonical JSON numbers/encoding.
8
+ # @api private
9
+ module Serializable
10
+ module_function
11
+
12
+ def convert(value, unsupported_message:)
13
+ case value
14
+ when Hash
15
+ value.to_h { |key, child| [key.to_s, convert(child, unsupported_message: unsupported_message)] }
16
+ when Array
17
+ value.map { |child| convert(child, unsupported_message: unsupported_message) }
18
+ when String, Integer, Float, TrueClass, FalseClass, NilClass
19
+ value
20
+ when Symbol
21
+ value.to_s
22
+ else
23
+ if value.respond_to?(:to_h)
24
+ convert(value.to_h, unsupported_message: unsupported_message)
25
+ else
26
+ raise ArgumentError, "#{unsupported_message}: #{value.class}"
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../common/configuration_error"
4
+
5
+ module Phronomy
6
+ # Neutral runtime values only. Application composition supplies the current
7
+ # settings object; Tool and Tracing own their domain-specific settings.
8
+ # @api private
9
+ class RuntimeSettings
10
+ # Bind the application's settings accessor without evaluating it at boot.
11
+ # @api private
12
+ def self.install_provider(&provider)
13
+ raise ArgumentError, "RuntimeSettings provider requires a block" unless provider
14
+
15
+ @provider = provider
16
+ nil
17
+ end
18
+
19
+ # Resolve on every read so configuration reset/scoped restoration is visible.
20
+ # @return [RuntimeSettings]
21
+ # @api private
22
+ def self.current
23
+ settings = @provider&.call
24
+ unless settings.is_a?(RuntimeSettings)
25
+ raise ConfigurationError, "RuntimeSettings provider must return RuntimeSettings"
26
+ end
27
+
28
+ settings
29
+ end
30
+
31
+ attr_accessor :logger
32
+ attr_accessor :event_loop_stop_grace_seconds
33
+ attr_accessor :event_loop_starvation_threshold_seconds
34
+ attr_accessor :event_loop_dispatch_threshold_seconds
35
+ attr_accessor :offload_pool_size, :offload_queue_size
36
+ attr_accessor :authorization_pool_size, :authorization_queue_size
37
+
38
+ # @api private
39
+ def initialize
40
+ @event_loop_stop_grace_seconds = 5
41
+ @event_loop_starvation_threshold_seconds = nil
42
+ @event_loop_dispatch_threshold_seconds = nil
43
+ @offload_pool_size = 10
44
+ @offload_queue_size = 100
45
+ @authorization_pool_size = 4
46
+ @authorization_queue_size = 100
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module ContentStore
5
+ # Resources owned by the content service and supplied to Storage composition.
6
+ # @api private
7
+ module StorageSchema
8
+ CONTENTS = Phronomy::Storage::Resource.new(id: "content.blobs", kind: :blobs,
9
+ attributes: {canonicalization_version: :integer})
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module ContentStore
5
+ # Content-addressed storage service over an injected Storage view.
6
+ # Content identity and digest validation belong here; Storage owns I/O.
7
+ # @api private
8
+ class StoredContents < Base
9
+ def initialize(view) = @view = view
10
+
11
+ def put(bytes, canonicalization_version:)
12
+ value = String(bytes).b
13
+ id = content_id_for(value)
14
+ @view.atomic do |bound|
15
+ blob = bound.blobs(StorageSchema::CONTENTS).put_if_absent(key: id, bytes: value,
16
+ attributes: {canonicalization_version: Integer(canonicalization_version)})
17
+ verify!(blob, id)
18
+ id
19
+ end
20
+ rescue Phronomy::Storage::BlobConflictError => error
21
+ raise IntegrityError, error.message
22
+ end
23
+
24
+ def fetch(content_id)
25
+ id = content_id.to_s
26
+ @view.atomic do |bound|
27
+ blob = bound.blobs(StorageSchema::CONTENTS).fetch(id)
28
+ verify!(blob, id)
29
+ blob.bytes.dup
30
+ end
31
+ end
32
+
33
+ def exist?(content_id) = @view.blobs(StorageSchema::CONTENTS).exist?(content_id.to_s)
34
+
35
+ private
36
+
37
+ def verify!(blob, id)
38
+ unless blob.key == id && content_id_for(blob.bytes) == id
39
+ raise IntegrityError, "content digest mismatch: #{id}"
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,202 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Context
5
+ # Context preparation framework: policy, plan invariants, canonical input and budget.
6
+ class Assembly
7
+ ASSEMBLY_POLICY_VERSION = 9
8
+ POLICY_ORIGIN_METADATA_KEY = "context_policy_origin"
9
+ POLICY_ITEM_ID_METADATA_KEY = "context_policy_item_id"
10
+ SEMANTIC_CATEGORY_METADATA_KEY = "context_policy_semantic_category"
11
+ CONTENT_FORMAT_METADATA_KEY = "context_policy_content_format"
12
+ CONVERSATION_GROUP_ID_METADATA_KEY = "context_policy_conversation_group_id"
13
+ TRUSTED_HANDOFF_METADATA_KEYS = %w[
14
+ handoff_policy_category
15
+ handoff_provenance
16
+ ].freeze
17
+ HANDOFF_POLICY_CATEGORY_METADATA_KEY = "handoff_policy_category"
18
+
19
+ Prepared = Data.define(:input, :plan, :model_config, :call_sequence, :call_mode, :adapter_identity)
20
+
21
+ def initialize(tracer: Phronomy::Tracing::Settings.current.tracer)
22
+ @tracer = tracer
23
+ end
24
+
25
+ # Executes policy once against an immutable snapshot, outside a save transaction.
26
+ # All policies share validation and canonicalization; callers never replay policy to recover.
27
+ def prepare(input:, policy:, adapter_identity:)
28
+ plan = invoke_policy(policy, input)
29
+ Phronomy::Context::ContextPlanValidator.new.validate!(input: input, plan: plan)
30
+ Prepared.new(
31
+ input: input, plan: plan, model_config: input.model_config,
32
+ call_sequence: input.call_sequence, call_mode: input.call_mode,
33
+ adapter_identity: Phronomy::Values::Immutable.copy(adapter_identity)
34
+ )
35
+ end
36
+
37
+ def store(prepared, contents:)
38
+ unless prepared.is_a?(Prepared)
39
+ raise ArgumentError, "Context::Assembly#store expected Context::Assembly::Prepared"
40
+ end
41
+
42
+ plan = Phronomy::Context::ContextPlanValidator.new.validate!(input: prepared.input, plan: prepared.plan)
43
+ segments = []
44
+ plan.instruction.each do |item|
45
+ segments << segment_from_content_item(
46
+ item,
47
+ contents: contents,
48
+ additional_metadata: {SEMANTIC_CATEGORY_METADATA_KEY => "instruction"}
49
+ )
50
+ end
51
+ plan.knowledge.each do |item|
52
+ segments << segment_from_content_item(
53
+ item,
54
+ contents: contents,
55
+ additional_metadata: {SEMANTIC_CATEGORY_METADATA_KEY => "knowledge"}
56
+ )
57
+ end
58
+ plan.conversation.each_with_index do |group, group_index|
59
+ group_metadata = {
60
+ SEMANTIC_CATEGORY_METADATA_KEY => "conversation",
61
+ CONVERSATION_GROUP_ID_METADATA_KEY =>
62
+ "conversation:#{prepared.call_sequence}:#{group_index}"
63
+ }
64
+ if tool_exchange_group?(group)
65
+ group_metadata[HANDOFF_POLICY_CATEGORY_METADATA_KEY] = "tool_exchanges"
66
+ end
67
+
68
+ group.each do |item|
69
+ segments << segment_from_content_item(
70
+ item,
71
+ contents: contents,
72
+ additional_metadata: group_metadata
73
+ )
74
+ end
75
+ end
76
+
77
+ selected_tool_definitions = plan.tools.map(&:definition)
78
+ Phronomy::Context::FinalBudgetValidator.new(
79
+ content_loader: lambda { |ref| fetch_content_from(contents, ref) }
80
+ ).validate!(
81
+ token_budget: prepared.input.token_budget,
82
+ segments: segments,
83
+ extra_values: [selected_tool_definitions]
84
+ )
85
+
86
+ store_manifest(
87
+ contents: contents,
88
+ call_sequence: prepared.call_sequence,
89
+ call_mode: prepared.call_mode,
90
+ segments: segments,
91
+ model_config_ref: contents.put_json(prepared.model_config),
92
+ tool_definitions_ref: contents.put_json(selected_tool_definitions),
93
+ adapter_identity: prepared.adapter_identity
94
+ )
95
+ end
96
+
97
+ private
98
+
99
+ def invoke_policy(policy, policy_input)
100
+ tracer = @tracer
101
+ span = tracer.start_span(
102
+ "context_policy",
103
+ agent_id: policy_input.agent_id,
104
+ execution_id: policy_input.execution_id,
105
+ call_sequence: policy_input.call_sequence,
106
+ policy_class: policy.class.name || policy.class.to_s,
107
+ instruction_count: policy_input.instruction.length,
108
+ knowledge_count: policy_input.knowledge.length,
109
+ tool_count: policy_input.tools.length,
110
+ conversation_group_count: policy_input.conversation.length
111
+ )
112
+ result = policy.call(policy_input)
113
+ tracer.finish_span(span)
114
+ result
115
+ rescue => error
116
+ tracer&.finish_span(span, error: error) if defined?(span) && span
117
+ raise
118
+ end
119
+
120
+ def store_manifest(
121
+ contents:,
122
+ call_sequence:,
123
+ call_mode:,
124
+ segments:,
125
+ model_config_ref:,
126
+ tool_definitions_ref:,
127
+ adapter_identity:
128
+ )
129
+ positioned = segments.each_with_index.map do |value, position|
130
+ Phronomy::Context::LLMInputManifest::Segment.new(**value.merge(position: position))
131
+ end
132
+ manifest = Phronomy::Context::LLMInputManifest.new(
133
+ call_sequence: call_sequence,
134
+ call_mode: call_mode,
135
+ segments: positioned,
136
+ model_config_ref: model_config_ref,
137
+ tool_definitions_ref: tool_definitions_ref,
138
+ assembly_policy_version: ASSEMBLY_POLICY_VERSION,
139
+ ruby_llm_version: adapter_identity["sdk_version"],
140
+ adapter_name: adapter_identity["adapter_name"]
141
+ )
142
+ [manifest, contents.put_json(manifest.to_h)]
143
+ end
144
+
145
+ def segment_from_content_item(item, contents:, additional_metadata: {})
146
+ content_ref = item.provenance.content_ref || store_item_content(item, contents)
147
+ metadata = sanitized_item_metadata(item).merge(additional_metadata).merge(
148
+ CONTENT_FORMAT_METADATA_KEY => item.content_format.to_s,
149
+ POLICY_ITEM_ID_METADATA_KEY => item.id,
150
+ POLICY_ORIGIN_METADATA_KEY => item.provenance.origin.to_s,
151
+ "journal_record_id" => item.provenance.record_id,
152
+ "source_agent_id" => item.provenance.agent_id,
153
+ "source_execution_id" => item.provenance.execution_id,
154
+ "llm_call_id" => item.provenance.llm_call_id
155
+ ).compact
156
+
157
+ {
158
+ category: item.kind,
159
+ role: item.role,
160
+ content_ref: content_ref,
161
+ delivery: item.respond_to?(:delivery) ? item.delivery : :chat_message,
162
+ tool_call_id: item.respond_to?(:tool_call_id) ? item.tool_call_id : nil,
163
+ metadata: metadata
164
+ }
165
+ end
166
+
167
+ def sanitized_item_metadata(item)
168
+ raw = item.metadata.to_h.transform_keys(&:to_s)
169
+ # :working origin is Phronomy-controlled (from InitialPreparation),
170
+ # so handoff routing metadata on working records is equally trusted.
171
+ trusted_handoff = if item.provenance.origin == :handoff || item.provenance.origin == :working
172
+ raw.slice(*TRUSTED_HANDOFF_METADATA_KEYS)
173
+ else
174
+ {}
175
+ end
176
+
177
+ raw.except(*Phronomy::Context::ContextPolicyInput::FRAMEWORK_METADATA_KEYS)
178
+ .merge(trusted_handoff)
179
+ end
180
+
181
+ def tool_exchange_group?(group)
182
+ Array(group).any? do |item|
183
+ item.kind == :assistant_message && !item.tool_call_ids.empty?
184
+ end && Array(group).any? { |item| item.kind == :tool_message }
185
+ end
186
+
187
+ def store_item_content(item, contents)
188
+ if item.content_format == :json
189
+ contents.put_json(item.content)
190
+ else
191
+ contents.put_text(item.content.to_s)
192
+ end
193
+ end
194
+
195
+ def fetch_content_from(contents, ref)
196
+ contents.fetch_text(ref)
197
+ rescue
198
+ contents.fetch(ref)
199
+ end
200
+ end
201
+ end
202
+ end
@@ -1,8 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "../common/error"
4
+
3
5
  module Phronomy
4
6
  # Raised when mandatory context content (system instructions, tool definitions,
5
7
  # current input) exhausts the model's context window, leaving no room for
6
8
  # prior conversation history.
7
- class ContextBudgetExceededError < Error; end
9
+ module Context
10
+ class BudgetExceededError < Phronomy::Error; end
11
+ end
8
12
  end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Context
5
+ Candidate = Data.define(
6
+ :candidate_id,
7
+ :source_kind,
8
+ :category,
9
+ :role,
10
+ :content_ref,
11
+ :record_id,
12
+ :agent_id,
13
+ :execution_id,
14
+ :llm_call_id,
15
+ :tool_call_id,
16
+ :sequence,
17
+ :constraint,
18
+ :priority,
19
+ :metadata
20
+ ) do
21
+ def initialize(**values)
22
+ effective_constraint = values[:constraint] ||
23
+ Constraint.selectable(origin: :context_policy)
24
+ unless effective_constraint.is_a?(Constraint)
25
+ raise ArgumentError,
26
+ "Candidate constraint must be Constraint"
27
+ end
28
+
29
+ normalized = values.merge(
30
+ candidate_id: values.fetch(:candidate_id).to_s.freeze,
31
+ source_kind: values.fetch(:source_kind).to_sym,
32
+ category: values.fetch(:category).to_sym,
33
+ role: values[:role]&.to_sym,
34
+ content_ref: values.fetch(:content_ref).to_s.freeze,
35
+ record_id: values[:record_id]&.to_s&.freeze,
36
+ agent_id: values[:agent_id]&.to_s&.freeze,
37
+ execution_id: values[:execution_id]&.to_s&.freeze,
38
+ llm_call_id: values[:llm_call_id]&.to_s&.freeze,
39
+ tool_call_id: values[:tool_call_id]&.to_s&.freeze,
40
+ sequence: values[:sequence] && Integer(values[:sequence]),
41
+ constraint: effective_constraint,
42
+ priority: Integer(values[:priority] || 0),
43
+ metadata: Phronomy::Values::Immutable.copy(values[:metadata] || {})
44
+ )
45
+
46
+ super(**normalized)
47
+ freeze
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Context
5
+ CONSTRAINT_MODES = %i[required selectable forbidden].freeze
6
+
7
+ Constraint = Data.define(:mode, :origin, :reason) do
8
+ def initialize(mode:, origin:, reason: nil)
9
+ normalized_mode = mode.to_sym
10
+ unless CONSTRAINT_MODES.include?(normalized_mode)
11
+ raise ArgumentError, "unknown Selection constraint mode: #{mode.inspect}"
12
+ end
13
+
14
+ super(
15
+ mode: normalized_mode,
16
+ origin: origin.to_sym,
17
+ reason: reason&.to_s&.freeze
18
+ )
19
+ freeze
20
+ end
21
+
22
+ def required?
23
+ mode == :required
24
+ end
25
+
26
+ def selectable?
27
+ mode == :selectable
28
+ end
29
+
30
+ def forbidden?
31
+ mode == :forbidden
32
+ end
33
+
34
+ def self.required(origin:, reason: nil)
35
+ new(mode: :required, origin: origin, reason: reason)
36
+ end
37
+
38
+ def self.selectable(origin:, reason: nil)
39
+ new(mode: :selectable, origin: origin, reason: reason)
40
+ end
41
+
42
+ def self.forbidden(origin:, reason: nil)
43
+ new(mode: :forbidden, origin: origin, reason: reason)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Phronomy
4
- module Agent
4
+ module Context
5
5
  ContextPlan = Data.define(
6
6
  :instruction,
7
7
  :knowledge,
@@ -21,7 +21,7 @@ module Phronomy
21
21
  knowledge: Array(knowledge).freeze,
22
22
  tools: Array(tools).freeze,
23
23
  conversation: Array(conversation).map { |group| Array(group).freeze }.freeze,
24
- metadata: Immutable.copy(metadata || {})
24
+ metadata: Phronomy::Values::Immutable.copy(metadata || {})
25
25
  )
26
26
  freeze
27
27
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Phronomy
4
- module Agent
4
+ module Context
5
5
  class ContextPlanValidator
6
6
  RESERVED_KIND_CATEGORIES = {
7
7
  instruction: :instruction,
@@ -82,7 +82,7 @@ module Phronomy
82
82
  end
83
83
  return if missing.empty?
84
84
 
85
- raise Phronomy::ContextBudgetExceededError,
85
+ raise Phronomy::Context::BudgetExceededError,
86
86
  "ContextPlan omitted required #{label} item(s): #{missing.map(&:id).inspect}"
87
87
  end
88
88
 
@@ -130,7 +130,7 @@ module Phronomy
130
130
  end
131
131
  return if missing_groups.empty?
132
132
 
133
- raise Phronomy::ContextBudgetExceededError,
133
+ raise Phronomy::Context::BudgetExceededError,
134
134
  "ContextPlan omitted required conversation group(s): " \
135
135
  "#{missing_groups.map { |group| group.map(&:id) }.inspect}"
136
136
  end
@@ -3,7 +3,7 @@
3
3
  require "securerandom"
4
4
 
5
5
  module Phronomy
6
- module Agent
6
+ module Context
7
7
  class ContextPolicy
8
8
  def call(_input)
9
9
  raise NotImplementedError, "#{self.class}#call is not implemented"
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Phronomy
4
- module Agent
4
+ module Context
5
5
  ContextPolicyInput = Data.define(
6
6
  :agent_id,
7
7
  :execution_id,
@@ -50,8 +50,8 @@ module Phronomy
50
50
  knowledge: normalized_knowledge,
51
51
  tools: normalized_tools,
52
52
  conversation: normalized_conversation,
53
- model_config: Immutable.copy(values[:model_config] || {}),
54
- metadata: Immutable.copy(values[:metadata] || {})
53
+ model_config: Phronomy::Values::Immutable.copy(values[:model_config] || {}),
54
+ metadata: Phronomy::Values::Immutable.copy(values[:metadata] || {})
55
55
  ))
56
56
  freeze
57
57
  end
@@ -145,7 +145,7 @@ module Phronomy
145
145
  ) do
146
146
  def initialize(**values)
147
147
  provenance = ContextPolicyInput.send(:normalize_provenance, values[:provenance])
148
- definition = Immutable.copy(values.fetch(:definition))
148
+ definition = Phronomy::Values::Immutable.copy(values.fetch(:definition))
149
149
  unless definition.is_a?(Hash) && definition["name"].to_s != ""
150
150
  raise ArgumentError, "ContextPolicyInput ToolItem definition requires a non-empty name"
151
151
  end
@@ -156,7 +156,7 @@ module Phronomy
156
156
  estimated_tokens: ContextPolicyInput.send(:normalize_estimated_tokens, values[:estimated_tokens]),
157
157
  required: !!values[:required],
158
158
  provenance: provenance,
159
- metadata: Immutable.copy(values[:metadata] || {})
159
+ metadata: Phronomy::Values::Immutable.copy(values[:metadata] || {})
160
160
  )
161
161
  raise ArgumentError, "ContextPolicyInput ToolItem id must not be empty" if id.empty?
162
162
  freeze
@@ -212,12 +212,12 @@ module Phronomy
212
212
  id: id,
213
213
  kind: kind,
214
214
  role: role,
215
- content: Immutable.copy(values[:content]),
215
+ content: Phronomy::Values::Immutable.copy(values[:content]),
216
216
  content_format: content_format,
217
217
  estimated_tokens: normalize_estimated_tokens(values[:estimated_tokens]),
218
218
  required: !!values[:required],
219
219
  provenance: normalize_provenance(values[:provenance]),
220
- metadata: Immutable.copy(values[:metadata] || {})
220
+ metadata: Phronomy::Values::Immutable.copy(values[:metadata] || {})
221
221
  }
222
222
  end
223
223
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Phronomy
4
- module Agent
4
+ module Context
5
5
  class ContextPolicyInputBuilder
6
6
  KNOWLEDGE_KINDS = %i[knowledge memory summary structured_state].freeze
7
7
  CONVERSATION_KINDS = %i[external_message assistant_message tool_message current_input current_request].freeze