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
@@ -2,29 +2,13 @@
2
2
 
3
3
  module Phronomy
4
4
  module Agent
5
- class ContextAssembler
6
- ASSEMBLY_POLICY_VERSION = 8
5
+ class ContextPreparation
7
6
  SEGMENT_ORIGIN_METADATA_KEY = "phronomy_origin"
8
7
  POLICY_ORIGIN_METADATA_KEY = "context_policy_origin"
9
8
  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
9
  BEFORE_LLM_INPUT_ORIGIN = "before_llm_input"
19
10
  HANDOFF_CONTEXT_ORIGIN = "handoff_context"
20
11
 
21
- Prepared = Data.define(:input, :plan, :model_config, :call_sequence, :call_mode) do
22
- def initialize(**values)
23
- super
24
- freeze
25
- end
26
- end
27
-
28
12
  def initialize(
29
13
  agent:,
30
14
  persistence:,
@@ -38,102 +22,51 @@ module Phronomy
38
22
  @candidate_resolver = candidate_resolver || ContextCandidateResolver.new(
39
23
  content_loader: method(:fetch_content)
40
24
  )
41
- @input_builder = ContextPolicyInputBuilder.new(
25
+ @input_builder = Phronomy::Context::ContextPolicyInputBuilder.new(
42
26
  content_loader: method(:fetch_content)
43
27
  )
44
28
  end
45
29
 
46
30
  # Builds one immutable Policy input snapshot and executes Application Policy.
47
31
  # This method must run outside a Phronomy Persistence transaction.
48
- def prepare_initial(input:, agent_root:, execution:, config: {}, patch: LLMInputPatch.empty)
32
+ def prepare_initial(input:, agent_root:, execution:, config: {}, patch: Phronomy::Context::LLMInputPatch.empty)
49
33
  projection = journal_projection(agent_root)
50
34
  model_cfg = effective_model_config(config, patch)
51
- tool_set = ToolDefinitionSet.build(
52
- @agent,
53
- additional_tools: handoff_tool_classes(config)
54
- )
35
+ tool_set = @agent.send(:tool_definition_set, additional_tools: handoff_tool_classes(config))
55
36
  system_text = build_system_text(input)
56
37
  hook_candidates = normalize_candidates(patch.segment_candidates)
57
38
  input_ref = execution.metadata.fetch("current_input_ref")
58
39
  current_input_content = @persistence.contents.fetch_text(input_ref)
59
40
  excluded = [execution.metadata["current_input_record_id"]].compact
60
- handoff_context = config[:phronomy_handoff_context]
41
+ handoff_context = config[:phronomy_transfer_context]
61
42
 
62
- instructions = []
63
- unless system_text.to_s.empty?
64
- instructions << ContextPolicyInput::InstructionItem.new(
65
- id: "instruction:agent:#{execution.execution_id}:1",
66
- kind: :instruction,
67
- role: :system,
68
- content: system_text.to_s,
69
- content_format: :text,
70
- estimated_tokens: estimate_value(system_text.to_s),
71
- required: true,
72
- provenance: ContextPolicyInput::Provenance.new(origin: :agent_configuration),
73
- metadata: {}
74
- )
75
- end
76
- if handoff_context
77
- instructions << ContextPolicyInput::InstructionItem.new(
78
- id: "instruction:handoff:#{execution.execution_id}:1",
79
- kind: :handoff_responsibility,
80
- role: :user,
81
- content: handoff_context.responsibility.to_s,
82
- content_format: :text,
83
- estimated_tokens: estimate_value(handoff_context.responsibility.to_s),
84
- required: true,
85
- provenance: ContextPolicyInput::Provenance.new(origin: :handoff_context),
86
- metadata: {SEGMENT_ORIGIN_METADATA_KEY => HANDOFF_CONTEXT_ORIGIN}
87
- )
88
- end
43
+ instructions = initial_instruction_items(
44
+ system_text,
45
+ handoff_context,
46
+ execution: execution
47
+ )
89
48
 
90
- generation = agent_root.transcript_generation
91
- eligible_working = Array(execution.working_records).select do |record|
92
- record.context_candidate && record.context_generation == generation
93
- end
94
- candidates = @candidate_resolver.resolve(
95
- prior_records: projection.context_records,
96
- working_records: eligible_working,
49
+ candidates = resolve_record_candidates(
50
+ projection,
51
+ agent_root: agent_root,
52
+ execution: execution,
97
53
  excluded_record_ids: excluded
98
54
  )
99
- candidates = merge_hook_candidates(
55
+ candidates = merge_context_candidates(
100
56
  candidates,
101
57
  hook_candidates,
58
+ handoff_context,
102
59
  agent_root: agent_root,
103
60
  execution: execution,
104
61
  call_sequence: 1
105
62
  )
106
- candidates = merge_handoff_candidates(
107
- candidates,
108
- handoff_context,
63
+ current_input = current_input_item(
64
+ current_input_content,
65
+ input_ref: input_ref,
66
+ candidates: candidates,
67
+ agent_root: agent_root,
109
68
  execution: execution
110
69
  )
111
- next_sequence = Array(candidates).filter_map(&:sequence).max.to_i + 1
112
- current_input = ContextPolicyInput::ConversationItem.new(
113
- id: "current-input:#{execution.execution_id}",
114
- kind: :current_input,
115
- role: :user,
116
- content: current_input_content,
117
- content_format: :text,
118
- sequence: next_sequence,
119
- estimated_tokens: estimate_value(current_input_content),
120
- required: true,
121
- provenance: ContextPolicyInput::Provenance.new(
122
- origin: :working,
123
- content_ref: input_ref,
124
- record_id: execution.metadata["current_input_record_id"],
125
- agent_id: agent_root.agent_id,
126
- execution_id: execution.execution_id
127
- ),
128
- tool_call_id: nil,
129
- tool_call_ids: [],
130
- delivery: :ask_argument,
131
- metadata: {
132
- "source_agent_id" => agent_root.agent_id,
133
- "source_execution_id" => execution.execution_id,
134
- "handoff_policy_category" => "current_request"
135
- }
136
- )
137
70
 
138
71
  prepare(
139
72
  agent_root: agent_root,
@@ -157,44 +90,31 @@ module Phronomy
157
90
  agent_root:,
158
91
  execution:,
159
92
  config: {},
160
- patch: LLMInputPatch.empty
93
+ patch: Phronomy::Context::LLMInputPatch.empty
161
94
  )
162
95
  projection = journal_projection(agent_root)
163
96
  model_cfg = effective_model_config(config, patch)
164
97
  hook_candidates = normalize_candidates(patch.segment_candidates)
165
- handoff_context = config[:phronomy_handoff_context]
166
- tool_set = ToolDefinitionSet.build(
167
- @agent,
168
- additional_tools: handoff_tool_classes(config)
169
- )
98
+ handoff_context = config[:phronomy_transfer_context]
99
+ tool_set = @agent.send(:tool_definition_set, additional_tools: handoff_tool_classes(config))
170
100
 
171
- instructions = base_manifest.segments.filter_map do |segment|
172
- next unless retained_base_instruction?(segment)
173
- instruction_item_from_manifest(segment)
174
- end
101
+ instructions = retained_instruction_items(base_manifest)
175
102
 
176
- generation = agent_root.transcript_generation
177
- eligible_working = Array(execution.working_records).select do |record|
178
- record.context_candidate && record.context_generation == generation
179
- end
180
- candidates = @candidate_resolver.resolve(
181
- prior_records: projection.context_records,
182
- working_records: eligible_working,
103
+ candidates = resolve_record_candidates(
104
+ projection,
105
+ agent_root: agent_root,
106
+ execution: execution,
183
107
  excluded_record_ids: []
184
108
  )
185
109
  call_sequence = execution.llm_calls.length + 1
186
- candidates = merge_hook_candidates(
110
+ candidates = merge_context_candidates(
187
111
  candidates,
188
112
  hook_candidates,
113
+ handoff_context,
189
114
  agent_root: agent_root,
190
115
  execution: execution,
191
116
  call_sequence: call_sequence
192
117
  )
193
- candidates = merge_handoff_candidates(
194
- candidates,
195
- handoff_context,
196
- execution: execution
197
- )
198
118
 
199
119
  prepare(
200
120
  agent_root: agent_root,
@@ -211,68 +131,113 @@ module Phronomy
211
131
  end
212
132
 
213
133
  # Validates and canonicalizes a previously prepared Policy decision.
214
- # This method contains no ContextPolicy invocation and is safe to execute in
134
+ # This method contains no Phronomy::Context::ContextPolicy invocation and is safe to execute in
215
135
  # the short commit transaction after the caller revalidates durable revision.
216
- def finalize(prepared, persistence: @persistence)
217
- unless prepared.is_a?(Prepared)
218
- raise ArgumentError, "ContextAssembler#finalize expected ContextAssembler::Prepared"
219
- end
220
136
 
221
- plan = ContextPlanValidator.new.validate!(input: prepared.input, plan: prepared.plan)
222
- segments = []
223
- plan.instruction.each do |item|
224
- segments << segment_from_content_item(
225
- item,
226
- persistence: persistence,
227
- additional_metadata: {SEMANTIC_CATEGORY_METADATA_KEY => "instruction"}
228
- )
137
+ def initial_instruction_items(system_text, handoff_context, execution:)
138
+ instructions = []
139
+ unless system_text.to_s.empty?
140
+ instructions << agent_instruction_item(system_text, execution: execution)
229
141
  end
230
- plan.knowledge.each do |item|
231
- segments << segment_from_content_item(
232
- item,
233
- persistence: persistence,
234
- additional_metadata: {SEMANTIC_CATEGORY_METADATA_KEY => "knowledge"}
235
- )
142
+ if handoff_context
143
+ instructions << handoff_instruction_item(handoff_context, execution: execution)
236
144
  end
237
- plan.conversation.each_with_index do |group, group_index|
238
- group_metadata = {
239
- SEMANTIC_CATEGORY_METADATA_KEY => "conversation",
240
- CONVERSATION_GROUP_ID_METADATA_KEY =>
241
- "conversation:#{prepared.call_sequence}:#{group_index}"
242
- }
243
- if tool_exchange_group?(group)
244
- group_metadata[HANDOFF_POLICY_CATEGORY_METADATA_KEY] = "tool_exchanges"
245
- end
145
+ instructions
146
+ end
246
147
 
247
- group.each do |item|
248
- segments << segment_from_content_item(
249
- item,
250
- persistence: persistence,
251
- additional_metadata: group_metadata
252
- )
253
- end
148
+ def agent_instruction_item(system_text, execution:)
149
+ Phronomy::Context::ContextPolicyInput::InstructionItem.new(
150
+ id: "instruction:agent:#{execution.execution_id}:1",
151
+ kind: :instruction,
152
+ role: :system,
153
+ content: system_text.to_s,
154
+ content_format: :text,
155
+ estimated_tokens: estimate_value(system_text.to_s),
156
+ required: true,
157
+ provenance: Phronomy::Context::ContextPolicyInput::Provenance.new(origin: :agent_configuration),
158
+ metadata: {}
159
+ )
160
+ end
161
+
162
+ def handoff_instruction_item(handoff_context, execution:)
163
+ Phronomy::Context::ContextPolicyInput::InstructionItem.new(
164
+ id: "instruction:handoff:#{execution.execution_id}:1",
165
+ kind: :handoff_responsibility,
166
+ role: :user,
167
+ content: handoff_context.responsibility.to_s,
168
+ content_format: :text,
169
+ estimated_tokens: estimate_value(handoff_context.responsibility.to_s),
170
+ required: true,
171
+ provenance: Phronomy::Context::ContextPolicyInput::Provenance.new(origin: :handoff_context),
172
+ metadata: {SEGMENT_ORIGIN_METADATA_KEY => HANDOFF_CONTEXT_ORIGIN}
173
+ )
174
+ end
175
+
176
+ def retained_instruction_items(base_manifest)
177
+ base_manifest.segments.filter_map do |segment|
178
+ next unless retained_base_instruction?(segment)
179
+ instruction_item_from_manifest(segment)
254
180
  end
181
+ end
255
182
 
256
- selected_tool_definitions = plan.tools.map(&:definition)
257
- ContextParts::Validators::FinalBudgetValidator.new(
258
- content_loader: lambda { |ref| fetch_content_from(persistence, ref) }
259
- ).validate!(
260
- token_budget: prepared.input.token_budget,
261
- segments: segments,
262
- extra_values: [selected_tool_definitions]
183
+ def resolve_record_candidates(projection, agent_root:, execution:, excluded_record_ids:)
184
+ generation = agent_root.transcript_generation
185
+ eligible_working = Array(execution.working_records).select do |record|
186
+ record.context_candidate && record.context_generation == generation
187
+ end
188
+ @candidate_resolver.resolve(
189
+ prior_records: projection.context_records,
190
+ working_records: eligible_working,
191
+ excluded_record_ids: excluded_record_ids
263
192
  )
193
+ end
264
194
 
265
- store_manifest(
266
- persistence: persistence,
267
- call_sequence: prepared.call_sequence,
268
- call_mode: prepared.call_mode,
269
- segments: segments,
270
- model_config_ref: persistence.contents.put_json(prepared.model_config),
271
- tool_definitions_ref: persistence.contents.put_json(selected_tool_definitions)
195
+ def merge_context_candidates(
196
+ candidates, hook_candidates, handoff_context,
197
+ agent_root:, execution:, call_sequence:
198
+ )
199
+ candidates = merge_hook_candidates(
200
+ candidates,
201
+ hook_candidates,
202
+ agent_root: agent_root,
203
+ execution: execution,
204
+ call_sequence: call_sequence
205
+ )
206
+ merge_handoff_candidates(
207
+ candidates,
208
+ handoff_context,
209
+ execution: execution
272
210
  )
273
211
  end
274
212
 
275
- private
213
+ def current_input_item(content, input_ref:, candidates:, agent_root:, execution:)
214
+ next_sequence = Array(candidates).filter_map(&:sequence).max.to_i + 1
215
+ Phronomy::Context::ContextPolicyInput::ConversationItem.new(
216
+ id: "current-input:#{execution.execution_id}",
217
+ kind: :current_input,
218
+ role: :user,
219
+ content: content,
220
+ content_format: :text,
221
+ sequence: next_sequence,
222
+ estimated_tokens: estimate_value(content),
223
+ required: true,
224
+ provenance: Phronomy::Context::ContextPolicyInput::Provenance.new(
225
+ origin: :working,
226
+ content_ref: input_ref,
227
+ record_id: execution.metadata["current_input_record_id"],
228
+ agent_id: agent_root.agent_id,
229
+ execution_id: execution.execution_id
230
+ ),
231
+ tool_call_id: nil,
232
+ tool_call_ids: [],
233
+ delivery: :ask_argument,
234
+ metadata: {
235
+ "source_agent_id" => agent_root.agent_id,
236
+ "source_execution_id" => execution.execution_id,
237
+ "handoff_policy_category" => "current_request"
238
+ }
239
+ )
240
+ end
276
241
 
277
242
  def prepare(
278
243
  agent_root:,
@@ -286,7 +251,7 @@ module Phronomy
286
251
  tools:,
287
252
  current_input:
288
253
  )
289
- token_budget = TokenBudgetResolver.new(agent: @agent).resolve(model_config)
254
+ token_budget = Phronomy::Agent::Settings.current.llm_adapter.input_budget(model_config)
290
255
  policy_input = @input_builder.build(
291
256
  agent_id: agent_root.agent_id,
292
257
  execution_id: execution.execution_id,
@@ -306,38 +271,12 @@ module Phronomy
306
271
  "execution_revision" => execution.execution_revision
307
272
  }
308
273
  )
309
- plan = invoke_policy(policy_input)
310
- ContextPlanValidator.new.validate!(input: policy_input, plan: plan)
311
- Prepared.new(
312
- input: policy_input,
313
- plan: plan,
314
- model_config: model_config,
315
- call_sequence: call_sequence,
316
- call_mode: call_mode
274
+ Phronomy::Context::Assembly.new.prepare(
275
+ input: policy_input, policy: @policy,
276
+ adapter_identity: Phronomy::Agent::Settings.current.llm_adapter.identity
317
277
  )
318
278
  end
319
279
 
320
- def invoke_policy(policy_input)
321
- tracer = Phronomy.configuration.tracer
322
- span = tracer.start_span(
323
- "context_policy",
324
- agent_id: policy_input.agent_id,
325
- execution_id: policy_input.execution_id,
326
- call_sequence: policy_input.call_sequence,
327
- policy_class: @policy.class.name || @policy.class.to_s,
328
- instruction_count: policy_input.instruction.length,
329
- knowledge_count: policy_input.knowledge.length,
330
- tool_count: policy_input.tools.length,
331
- conversation_group_count: policy_input.conversation.length
332
- )
333
- result = @policy.call(policy_input)
334
- tracer.finish_span(span)
335
- result
336
- rescue => error
337
- tracer&.finish_span(span, error: error) if defined?(span) && span
338
- raise
339
- end
340
-
341
280
  def journal_projection(agent_root)
342
281
  if @journal_records
343
282
  JournalProjection.new(agent_root: agent_root, records: @journal_records)
@@ -360,7 +299,7 @@ module Phronomy
360
299
  def instruction_item_from_manifest(segment)
361
300
  origin = segment.metadata[POLICY_ORIGIN_METADATA_KEY] ||
362
301
  ((segment.category.to_sym == :handoff_responsibility) ? "handoff_context" : "agent_configuration")
363
- ContextPolicyInput::InstructionItem.new(
302
+ Phronomy::Context::ContextPolicyInput::InstructionItem.new(
364
303
  id: segment.metadata[POLICY_ITEM_ID_METADATA_KEY] || "manifest-instruction:#{segment.position}",
365
304
  kind: segment.category,
366
305
  role: segment.role,
@@ -368,7 +307,7 @@ module Phronomy
368
307
  content_format: :text,
369
308
  estimated_tokens: estimate_value(fetch_content(segment.content_ref)),
370
309
  required: true,
371
- provenance: ContextPolicyInput::Provenance.new(
310
+ provenance: Phronomy::Context::ContextPolicyInput::Provenance.new(
372
311
  origin: origin.to_sym,
373
312
  content_ref: segment.content_ref
374
313
  ),
@@ -379,91 +318,17 @@ module Phronomy
379
318
  def tool_items(tool_set)
380
319
  tool_set.definitions.map do |definition|
381
320
  name = definition.fetch("name")
382
- ContextPolicyInput::ToolItem.new(
321
+ Phronomy::Context::ContextPolicyInput::ToolItem.new(
383
322
  id: "tool:#{name}",
384
323
  definition: definition,
385
324
  estimated_tokens: estimate_value(definition),
386
325
  required: false,
387
- provenance: ContextPolicyInput::Provenance.new(origin: :agent_configuration),
326
+ provenance: Phronomy::Context::ContextPolicyInput::Provenance.new(origin: :agent_configuration),
388
327
  metadata: {}
389
328
  )
390
329
  end.freeze
391
330
  end
392
331
 
393
- def store_manifest(
394
- persistence:,
395
- call_sequence:,
396
- call_mode:,
397
- segments:,
398
- model_config_ref:,
399
- tool_definitions_ref:
400
- )
401
- positioned = segments.each_with_index.map do |value, position|
402
- LLMInputManifest::Segment.new(**value.merge(position: position))
403
- end
404
- manifest = LLMInputManifest.new(
405
- call_sequence: call_sequence,
406
- call_mode: call_mode,
407
- segments: positioned,
408
- model_config_ref: model_config_ref,
409
- tool_definitions_ref: tool_definitions_ref,
410
- assembly_policy_version: ASSEMBLY_POLICY_VERSION,
411
- ruby_llm_version: defined?(RubyLLM::VERSION) ? RubyLLM::VERSION : nil,
412
- adapter_name: Phronomy.configuration.llm_adapter.class.name
413
- )
414
- [manifest, persistence.contents.put_json(manifest.to_h)]
415
- end
416
-
417
- def segment_from_content_item(item, persistence:, additional_metadata: {})
418
- content_ref = item.provenance.content_ref || store_item_content(item, persistence)
419
- metadata = sanitized_item_metadata(item).merge(additional_metadata).merge(
420
- CONTENT_FORMAT_METADATA_KEY => item.content_format.to_s,
421
- POLICY_ITEM_ID_METADATA_KEY => item.id,
422
- POLICY_ORIGIN_METADATA_KEY => item.provenance.origin.to_s,
423
- "journal_record_id" => item.provenance.record_id,
424
- "source_agent_id" => item.provenance.agent_id,
425
- "source_execution_id" => item.provenance.execution_id,
426
- "llm_call_id" => item.provenance.llm_call_id
427
- ).compact
428
-
429
- {
430
- category: item.kind,
431
- role: item.role,
432
- content_ref: content_ref,
433
- delivery: item.respond_to?(:delivery) ? item.delivery : :chat_message,
434
- tool_call_id: item.respond_to?(:tool_call_id) ? item.tool_call_id : nil,
435
- metadata: metadata
436
- }
437
- end
438
-
439
- def sanitized_item_metadata(item)
440
- raw = item.metadata.to_h.transform_keys(&:to_s)
441
- # :working origin is Phronomy-controlled (from perform_initial_preparation),
442
- # so handoff routing metadata on working records is equally trusted.
443
- trusted_handoff = if item.provenance.origin == :handoff || item.provenance.origin == :working
444
- raw.slice(*TRUSTED_HANDOFF_METADATA_KEYS)
445
- else
446
- {}
447
- end
448
-
449
- raw.except(*ContextPolicyInput::FRAMEWORK_METADATA_KEYS)
450
- .merge(trusted_handoff)
451
- end
452
-
453
- def tool_exchange_group?(group)
454
- Array(group).any? do |item|
455
- item.kind == :assistant_message && !item.tool_call_ids.empty?
456
- end && Array(group).any? { |item| item.kind == :tool_message }
457
- end
458
-
459
- def store_item_content(item, persistence)
460
- if item.content_format == :json
461
- persistence.contents.put_json(item.content)
462
- else
463
- persistence.contents.put_text(item.content.to_s)
464
- end
465
- end
466
-
467
332
  def effective_model_config(config, patch)
468
333
  apply_model_config_patch(model_config(config), patch.model_config_patch)
469
334
  end
@@ -474,15 +339,23 @@ module Phronomy
474
339
  "provider" => @agent.class.provider&.to_s,
475
340
  "temperature" => @agent.class.temperature,
476
341
  "max_output_tokens" => @agent.class.max_output_tokens,
477
- "context_window" => @agent.class.context_window,
478
- "cache_instructions" => !!@agent.class.cache_instructions,
479
- "parallel_tool_execution" => !!Phronomy.configuration.parallel_tool_execution
342
+ "cache_instructions" => !!@agent.class.cache_instructions
480
343
  }.compact
481
344
  end
482
345
 
483
346
  def apply_model_config_patch(base, patch)
484
347
  return base unless patch
485
- base.merge(patch.to_h.transform_keys(&:to_s)).compact
348
+ values = patch.to_h.transform_keys(&:to_s)
349
+ if values.key?("context_window")
350
+ raise Phronomy::ConfigurationError,
351
+ "context_window is model metadata; configure the LLM backend model metadata"
352
+ end
353
+ if values.key?("max_output_tokens") && !values["max_output_tokens"].nil?
354
+ cap = Integer(values["max_output_tokens"])
355
+ raise ArgumentError, "max_output_tokens must be positive" unless cap.positive?
356
+ values["max_output_tokens"] = cap
357
+ end
358
+ base.merge(values).compact
486
359
  end
487
360
 
488
361
  def normalize_candidates(candidates)
@@ -494,7 +367,7 @@ module Phronomy
494
367
  metadata = (hash[:metadata] || hash["metadata"] || {})
495
368
  .to_h
496
369
  .transform_keys(&:to_s)
497
- validate_application_metadata!(metadata, source: "LLMInputPatch segment candidate")
370
+ validate_application_metadata!(metadata, source: "Phronomy::Context::LLMInputPatch segment candidate")
498
371
  {
499
372
  content: content,
500
373
  category: category,
@@ -505,7 +378,7 @@ module Phronomy
505
378
  end
506
379
 
507
380
  def validate_application_metadata!(metadata, source:)
508
- conflicts = metadata.keys.map(&:to_s) & ContextPolicyInput::FRAMEWORK_METADATA_KEYS
381
+ conflicts = metadata.keys.map(&:to_s) & Phronomy::Context::ContextPolicyInput::FRAMEWORK_METADATA_KEYS
509
382
  return if conflicts.empty?
510
383
 
511
384
  raise ArgumentError,
@@ -527,7 +400,7 @@ module Phronomy
527
400
  "source_kind" => "hook",
528
401
  "content_format" => content.is_a?(String) ? "text" : "json"
529
402
  )
530
- Selection::Candidate.new(
403
+ Phronomy::Context::Candidate.new(
531
404
  candidate_id: "hook:#{execution.execution_id}:#{call_sequence}:#{index}",
532
405
  source_kind: :hook,
533
406
  category: hook.fetch(:category),
@@ -539,7 +412,7 @@ module Phronomy
539
412
  llm_call_id: nil,
540
413
  tool_call_id: nil,
541
414
  sequence: next_sequence + index + 1,
542
- constraint: Selection::Constraint.selectable(origin: :context_policy),
415
+ constraint: Phronomy::Context::Constraint.selectable(origin: :context_policy),
543
416
  priority: 0,
544
417
  metadata: metadata
545
418
  )
@@ -552,8 +425,8 @@ module Phronomy
552
425
  def merge_handoff_candidates(candidates, handoff_context, execution:)
553
426
  return Array(candidates).freeze unless handoff_context
554
427
 
555
- unless handoff_context.is_a?(Phronomy::Agent::HandoffContext)
556
- raise ArgumentError, "phronomy_handoff_context must be a HandoffContext"
428
+ unless handoff_context.is_a?(Phronomy::Agent::TransferContext)
429
+ raise ArgumentError, "phronomy_transfer_context must be a TransferContext"
557
430
  end
558
431
 
559
432
  next_sequence = Array(candidates).filter_map(&:sequence).max.to_i
@@ -572,7 +445,7 @@ module Phronomy
572
445
  "handoff_provenance" => item.provenance.to_h
573
446
  )
574
447
 
575
- Selection::Candidate.new(
448
+ Phronomy::Context::Candidate.new(
576
449
  candidate_id: "handoff:#{execution.execution_id}:#{index}:#{item.provenance.origin_record_id || item.provenance.origin_tool_call_id || index}",
577
450
  source_kind: :handoff,
578
451
  category: item.candidate_category,
@@ -584,7 +457,7 @@ module Phronomy
584
457
  llm_call_id: item.provenance.origin_llm_call_id,
585
458
  tool_call_id: item.tool_call_id,
586
459
  sequence: next_sequence + index + 1,
587
- constraint: Selection::Constraint.selectable(origin: :handoff_context),
460
+ constraint: Phronomy::Context::Constraint.selectable(origin: :handoff_context),
588
461
  priority: 50,
589
462
  metadata: metadata
590
463
  )
@@ -600,7 +473,7 @@ module Phronomy
600
473
  end
601
474
 
602
475
  def handoff_tool_classes(config)
603
- Array(config[:phronomy_handoff_bindings]).map(&:tool_class).freeze
476
+ Array(config[:phronomy_control_bindings]).map(&:tool_class).freeze
604
477
  end
605
478
 
606
479
  def estimate_value(value)
@@ -609,13 +482,9 @@ module Phronomy
609
482
  end
610
483
 
611
484
  def fetch_content(ref)
612
- fetch_content_from(@persistence, ref)
613
- end
614
-
615
- def fetch_content_from(persistence, ref)
616
- persistence.contents.fetch_text(ref)
485
+ @persistence.contents.fetch_text(ref)
617
486
  rescue
618
- persistence.contents.fetch(ref)
487
+ @persistence.contents.fetch(ref)
619
488
  end
620
489
 
621
490
  def build_system_text(input)
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Phronomy
4
+ module Agent
5
+ # Value returned by a control capability after its domain policy is applied.
6
+ # Agent validates exclusivity and Source ownership before accepting it.
7
+ # @api public
8
+ ControlRequest = Data.define(:target_agent_id, :responsibility, :selection, :llm_call_id, :tool_call_id) do
9
+ def initialize(target_agent_id:, responsibility:, selection:, llm_call_id: nil, tool_call_id: nil)
10
+ raise ArgumentError, "target_agent_id is required" if target_agent_id.to_s.empty?
11
+ raise ArgumentError, "responsibility is required" if responsibility.to_s.strip.empty?
12
+ normalized = selection.to_h.transform_keys(&:to_sym)
13
+ unless normalized.values.all? { |value| value == true || value == false }
14
+ raise ArgumentError, "context selection values must be boolean"
15
+ end
16
+ super(target_agent_id: target_agent_id.to_s.freeze, responsibility: responsibility.to_s.strip.freeze,
17
+ selection: normalized.freeze, llm_call_id: llm_call_id&.to_s&.freeze, tool_call_id: tool_call_id&.to_s&.freeze)
18
+ end
19
+ end
20
+ end
21
+ end