agentevolve-optimizer 0.5.0__py3-none-any.whl

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 (414) hide show
  1. agent_evolve/__init__.py +722 -0
  2. agent_evolve/agentic.py +2800 -0
  3. agent_evolve/api.py +767 -0
  4. agent_evolve/application/__init__.py +1580 -0
  5. agent_evolve/application/action_allocation.py +744 -0
  6. agent_evolve/application/action_allocation_frame.py +347 -0
  7. agent_evolve/application/action_allocation_frame_commit.py +185 -0
  8. agent_evolve/application/action_allocation_frame_commit_v3.py +184 -0
  9. agent_evolve/application/action_allocation_frame_v3.py +338 -0
  10. agent_evolve/application/action_archive_value.py +497 -0
  11. agent_evolve/application/action_evidence_consistency.py +455 -0
  12. agent_evolve/application/action_forecast_partitioning.py +1471 -0
  13. agent_evolve/application/action_metric_projection.py +211 -0
  14. agent_evolve/application/action_role_value.py +680 -0
  15. agent_evolve/application/action_score_authorities.py +363 -0
  16. agent_evolve/application/action_structural_signature.py +116 -0
  17. agent_evolve/application/action_target_realization.py +402 -0
  18. agent_evolve/application/agentic_evolution.py +7734 -0
  19. agent_evolve/application/agentic_portfolio_residual_expert.py +835 -0
  20. agent_evolve/application/anchor_residual_identification.py +463 -0
  21. agent_evolve/application/archive_conditioned_action_target.py +208 -0
  22. agent_evolve/application/artifact_journal.py +246 -0
  23. agent_evolve/application/artifact_replay.py +347 -0
  24. agent_evolve/application/budgeted_optimizer.py +1828 -0
  25. agent_evolve/application/calibrated_campaign.py +485 -0
  26. agent_evolve/application/calibrated_current_prefix_forecast_opportunity.py +322 -0
  27. agent_evolve/application/calibrated_positive_gain_opportunity.py +1581 -0
  28. agent_evolve/application/campaign_capacity_recourse.py +254 -0
  29. agent_evolve/application/campaign_contextual_outcomes.py +119 -0
  30. agent_evolve/application/campaign_diagnostic_blocks.py +930 -0
  31. agent_evolve/application/campaign_evidence_registry.py +262 -0
  32. agent_evolve/application/campaign_execution.py +2537 -0
  33. agent_evolve/application/campaign_generation_audit.py +942 -0
  34. agent_evolve/application/campaign_learning.py +1812 -0
  35. agent_evolve/application/campaign_learning_runtime.py +1977 -0
  36. agent_evolve/application/campaign_search_phase.py +227 -0
  37. agent_evolve/application/campaign_selector_context_extension.py +220 -0
  38. agent_evolve/application/campaign_variation_envelope.py +649 -0
  39. agent_evolve/application/campaign_variation_trace.py +451 -0
  40. agent_evolve/application/candidate_archive_consequence.py +128 -0
  41. agent_evolve/application/causal_opportunity_portfolio_gate.py +385 -0
  42. agent_evolve/application/composite_outcome_updater.py +145 -0
  43. agent_evolve/application/composition_portfolio_selection.py +363 -0
  44. agent_evolve/application/concurrent_stage.py +144 -0
  45. agent_evolve/application/contextual_action_allocation.py +181 -0
  46. agent_evolve/application/contextual_campaign_outcomes.py +267 -0
  47. agent_evolve/application/contextual_campaign_planning.py +1366 -0
  48. agent_evolve/application/contextual_delayed_credit.py +651 -0
  49. agent_evolve/application/contextual_search_controller.py +2374 -0
  50. agent_evolve/application/current_prefix_forecast_opportunity.py +714 -0
  51. agent_evolve/application/decision_metric_projection.py +112 -0
  52. agent_evolve/application/derived_action_semantics.py +129 -0
  53. agent_evolve/application/detailed_evaluation.py +449 -0
  54. agent_evolve/application/earned_lineage.py +1011 -0
  55. agent_evolve/application/effective_choice_audit.py +484 -0
  56. agent_evolve/application/empirical_consequence_calibration.py +908 -0
  57. agent_evolve/application/evaluation_accounting.py +325 -0
  58. agent_evolve/application/evaluation_cache.py +199 -0
  59. agent_evolve/application/evaluation_escrow.py +547 -0
  60. agent_evolve/application/evaluation_recourse.py +253 -0
  61. agent_evolve/application/event_recorder.py +151 -0
  62. agent_evolve/application/evolution_campaign.py +1840 -0
  63. agent_evolve/application/executable_hypothesis.py +323 -0
  64. agent_evolve/application/factorial_branch_pilot.py +772 -0
  65. agent_evolve/application/finite_acquisition_capacity_recourse.py +672 -0
  66. agent_evolve/application/finite_acquisition_residual_expert.py +373 -0
  67. agent_evolve/application/finite_acquisition_variation_envelope.py +802 -0
  68. agent_evolve/application/finite_action_hypothesis_semantics.py +446 -0
  69. agent_evolve/application/finite_action_selection.py +188 -0
  70. agent_evolve/application/finite_action_set.py +306 -0
  71. agent_evolve/application/finite_action_transition.py +537 -0
  72. agent_evolve/application/finite_variation_eligibility.py +296 -0
  73. agent_evolve/application/forecast_geometry_portfolio.py +799 -0
  74. agent_evolve/application/forecast_opportunity_shadow_calibration.py +316 -0
  75. agent_evolve/application/front_proximity_admission.py +311 -0
  76. agent_evolve/application/front_proximity_parent_basis.py +458 -0
  77. agent_evolve/application/frozen_hurdle_score.py +659 -0
  78. agent_evolve/application/g3_causal_screen.py +2257 -0
  79. agent_evolve/application/g3_causal_validation.py +1046 -0
  80. agent_evolve/application/g3_postseal_curation.py +818 -0
  81. agent_evolve/application/gated_agentic_generator.py +205 -0
  82. agent_evolve/application/generation_feedback.py +293 -0
  83. agent_evolve/application/generative_proposal_journal.py +185 -0
  84. agent_evolve/application/geometry_conditional_elasticity.py +453 -0
  85. agent_evolve/application/global_wave_action_allocation.py +1151 -0
  86. agent_evolve/application/head_mass_conditional_seat.py +268 -0
  87. agent_evolve/application/identifiable_reflection_evidence.py +1147 -0
  88. agent_evolve/application/identifiable_reflection_learning.py +395 -0
  89. agent_evolve/application/identifiable_reflection_request.py +364 -0
  90. agent_evolve/application/in_memory_residual_archive.py +341 -0
  91. agent_evolve/application/insight_memory.py +1804 -0
  92. agent_evolve/application/live_runtime_manifest.py +758 -0
  93. agent_evolve/application/llm_task_queue.py +769 -0
  94. agent_evolve/application/matched_finite_action_block.py +409 -0
  95. agent_evolve/application/materialized_action_broker.py +2328 -0
  96. agent_evolve/application/materialized_action_constraints.py +83 -0
  97. agent_evolve/application/materialized_variation.py +211 -0
  98. agent_evolve/application/multi_option_evolution.py +1536 -0
  99. agent_evolve/application/outcome_adaptive_action_racing.py +2827 -0
  100. agent_evolve/application/outcome_adaptive_residual_campaign_runtime.py +580 -0
  101. agent_evolve/application/outcome_adaptive_residual_portfolio_evolution.py +3671 -0
  102. agent_evolve/application/outcome_conditioned_portfolio_selection.py +1374 -0
  103. agent_evolve/application/outcome_relation.py +193 -0
  104. agent_evolve/application/paired_allocation_comparison.py +241 -0
  105. agent_evolve/application/paired_block_schedule.py +127 -0
  106. agent_evolve/application/parent_measurement.py +226 -0
  107. agent_evolve/application/pareto_archive.py +811 -0
  108. agent_evolve/application/portfolio_campaign_runtime.py +4739 -0
  109. agent_evolve/application/portfolio_evolution.py +2950 -0
  110. agent_evolve/application/portfolio_hypothesis_observations.py +814 -0
  111. agent_evolve/application/portfolio_memory_attribution.py +581 -0
  112. agent_evolve/application/portfolio_memory_dose.py +788 -0
  113. agent_evolve/application/portfolio_memory_matched_control.py +938 -0
  114. agent_evolve/application/portfolio_memory_transfer.py +297 -0
  115. agent_evolve/application/portfolio_optimization_memory.py +363 -0
  116. agent_evolve/application/portfolio_outcome_feedback.py +1613 -0
  117. agent_evolve/application/portfolio_projection.py +335 -0
  118. agent_evolve/application/portfolio_recombination.py +2032 -0
  119. agent_evolve/application/post_evolution_reflection.py +834 -0
  120. agent_evolve/application/postcommit_rank_authority.py +245 -0
  121. agent_evolve/application/precommitted_portfolio_racing.py +2762 -0
  122. agent_evolve/application/prequential_archive_opportunity_calibration.py +1154 -0
  123. agent_evolve/application/prequential_residual_exploration.py +343 -0
  124. agent_evolve/application/prequential_score_portfolio.py +954 -0
  125. agent_evolve/application/projections.py +292 -0
  126. agent_evolve/application/protected_action_committee.py +1027 -0
  127. agent_evolve/application/protected_branch_pilot.py +376 -0
  128. agent_evolve/application/protected_current_prefix_forecast_opportunity.py +552 -0
  129. agent_evolve/application/provider_replay.py +910 -0
  130. agent_evolve/application/rank_balanced_causal_pilot.py +1372 -0
  131. agent_evolve/application/recombination_residual_expert.py +403 -0
  132. agent_evolve/application/reflection_workflow.py +571 -0
  133. agent_evolve/application/region_conditional_credit.py +911 -0
  134. agent_evolve/application/residual_campaign_runtime.py +531 -0
  135. agent_evolve/application/residual_headroom_campaign_runtime.py +459 -0
  136. agent_evolve/application/residual_headroom_ledger.py +1544 -0
  137. agent_evolve/application/residual_learning_transaction.py +396 -0
  138. agent_evolve/application/residual_portfolio_evolution.py +1228 -0
  139. agent_evolve/application/residual_reachability.py +749 -0
  140. agent_evolve/application/residual_stage_credit.py +499 -0
  141. agent_evolve/application/same_prefix_paired_audit.py +1580 -0
  142. agent_evolve/application/semantic_coverage_score_portfolio.py +838 -0
  143. agent_evolve/application/sequential_lineage_allocation.py +1017 -0
  144. agent_evolve/application/sequential_market_replay.py +1395 -0
  145. agent_evolve/application/sequential_residual_campaign_runtime.py +305 -0
  146. agent_evolve/application/sequential_residual_portfolio_evolution.py +940 -0
  147. agent_evolve/application/single_score_action_allocation.py +299 -0
  148. agent_evolve/application/source_exposure_allocation.py +906 -0
  149. agent_evolve/application/staged_memory.py +210 -0
  150. agent_evolve/application/stratified_cold_start_allocation.py +732 -0
  151. agent_evolve/application/support_guarded_hurdle_score.py +549 -0
  152. agent_evolve/application/target_conditioned_action_forecast.py +595 -0
  153. agent_evolve/application/target_conditioned_campaign.py +566 -0
  154. agent_evolve/application/treatment_assignment.py +201 -0
  155. agent_evolve/application/trusted_objective_evidence.py +217 -0
  156. agent_evolve/application/two_stage_action_evolution.py +1131 -0
  157. agent_evolve/application/v8lite_allocation_policy.py +1083 -0
  158. agent_evolve/application/v9_candidate_policy.py +1303 -0
  159. agent_evolve/bootstrap.py +108 -0
  160. agent_evolve/campaign_presets.py +517 -0
  161. agent_evolve/campaign_profiles.py +452 -0
  162. agent_evolve/campaign_variation_topology.py +288 -0
  163. agent_evolve/campaign_workload.py +950 -0
  164. agent_evolve/cli.py +797 -0
  165. agent_evolve/contract.py +241 -0
  166. agent_evolve/core/__init__.py +91 -0
  167. agent_evolve/core/action_semantics.py +411 -0
  168. agent_evolve/core/authored.py +105 -0
  169. agent_evolve/core/formatting.py +286 -0
  170. agent_evolve/core/optimization_semantics.py +324 -0
  171. agent_evolve/core/problem.py +167 -0
  172. agent_evolve/core/results.py +323 -0
  173. agent_evolve/core/stats.py +70 -0
  174. agent_evolve/core/telemetry.py +100 -0
  175. agent_evolve/domain/__init__.py +89 -0
  176. agent_evolve/domain/artifact.py +162 -0
  177. agent_evolve/domain/durable_text.py +68 -0
  178. agent_evolve/domain/event.py +1454 -0
  179. agent_evolve/domain/finite_action_set.py +426 -0
  180. agent_evolve/domain/finite_variation.py +526 -0
  181. agent_evolve/domain/generative_emission.py +559 -0
  182. agent_evolve/domain/ids.py +163 -0
  183. agent_evolve/domain/inline_text.py +106 -0
  184. agent_evolve/domain/insight.py +27 -0
  185. agent_evolve/domain/lineage.py +737 -0
  186. agent_evolve/domain/llm_task_queue.py +960 -0
  187. agent_evolve/domain/outcome.py +96 -0
  188. agent_evolve/domain/patch.py +854 -0
  189. agent_evolve/domain/typed_json.py +542 -0
  190. agent_evolve/domain/variation_space.py +158 -0
  191. agent_evolve/driver.py +1014 -0
  192. agent_evolve/harness/__init__.py +29 -0
  193. agent_evolve/harness/base.py +242 -0
  194. agent_evolve/harness/directives.py +163 -0
  195. agent_evolve/harness/generative_seal.py +479 -0
  196. agent_evolve/harness/registry.py +41 -0
  197. agent_evolve/infrastructure/__init__.py +39 -0
  198. agent_evolve/infrastructure/artifacts/__init__.py +6 -0
  199. agent_evolve/infrastructure/artifacts/_verification.py +67 -0
  200. agent_evolve/infrastructure/artifacts/filesystem.py +343 -0
  201. agent_evolve/infrastructure/artifacts/in_memory.py +73 -0
  202. agent_evolve/infrastructure/asyncio_runtime.py +109 -0
  203. agent_evolve/infrastructure/authored_runtime.py +188 -0
  204. agent_evolve/infrastructure/authored_worker.py +171 -0
  205. agent_evolve/infrastructure/clock.py +53 -0
  206. agent_evolve/infrastructure/events/__init__.py +6 -0
  207. agent_evolve/infrastructure/events/_validation.py +89 -0
  208. agent_evolve/infrastructure/events/in_memory.py +56 -0
  209. agent_evolve/infrastructure/events/jsonl.py +193 -0
  210. agent_evolve/infrastructure/exception_provenance.py +215 -0
  211. agent_evolve/infrastructure/ids.py +118 -0
  212. agent_evolve/infrastructure/lineage_codec.py +1836 -0
  213. agent_evolve/infrastructure/outcome_adaptive_phase_journal.py +170 -0
  214. agent_evolve/infrastructure/residual_headroom_journal.py +221 -0
  215. agent_evolve/infrastructure/resource_lease.py +370 -0
  216. agent_evolve/infrastructure/sanitization/__init__.py +8 -0
  217. agent_evolve/infrastructure/sanitization/strict_json.py +484 -0
  218. agent_evolve/infrastructure/sequential_phase_journal.py +170 -0
  219. agent_evolve/infrastructure/stream_liveness.py +383 -0
  220. agent_evolve/infrastructure/subprocess_boundary.py +136 -0
  221. agent_evolve/integrations/__init__.py +1 -0
  222. agent_evolve/integrations/botorch/__init__.py +28 -0
  223. agent_evolve/integrations/botorch/finite_qlognehvi.py +190 -0
  224. agent_evolve/integrations/botorch/finite_qlognehvi_batch.py +155 -0
  225. agent_evolve/integrations/botorch/finite_qlognehvi_batch_identity.py +20 -0
  226. agent_evolve/integrations/botorch/finite_qlognehvi_batch_worker.py +55 -0
  227. agent_evolve/integrations/botorch/finite_qlognehvi_identity.py +22 -0
  228. agent_evolve/integrations/botorch/finite_qlognehvi_worker.py +55 -0
  229. agent_evolve/integrations/botorch/subprocess_qlognehvi.py +261 -0
  230. agent_evolve/integrations/botorch/subprocess_qlognehvi_batch.py +273 -0
  231. agent_evolve/integrations/completion.py +242 -0
  232. agent_evolve/integrations/pydantic_ai/__init__.py +441 -0
  233. agent_evolve/integrations/pydantic_ai/action_forecast.py +1068 -0
  234. agent_evolve/integrations/pydantic_ai/agentic_generator.py +2308 -0
  235. agent_evolve/integrations/pydantic_ai/async_generator.py +1604 -0
  236. agent_evolve/integrations/pydantic_ai/boundary_codec.py +1526 -0
  237. agent_evolve/integrations/pydantic_ai/calibrated_portfolio_campaign.py +756 -0
  238. agent_evolve/integrations/pydantic_ai/calibrated_portfolio_selection.py +7537 -0
  239. agent_evolve/integrations/pydantic_ai/campaign_acquisition.py +609 -0
  240. agent_evolve/integrations/pydantic_ai/execution_binding.py +138 -0
  241. agent_evolve/integrations/pydantic_ai/forecast_geometry_action_committee.py +217 -0
  242. agent_evolve/integrations/pydantic_ai/harness.py +159 -0
  243. agent_evolve/integrations/pydantic_ai/heterogeneous_model_execution.py +306 -0
  244. agent_evolve/integrations/pydantic_ai/hierarchical_residual_adaptive_semantic_view.py +179 -0
  245. agent_evolve/integrations/pydantic_ai/json_schema_dialect.py +108 -0
  246. agent_evolve/integrations/pydantic_ai/materialized_hierarchical_residual_expert.py +952 -0
  247. agent_evolve/integrations/pydantic_ai/materialized_portfolio_judge.py +520 -0
  248. agent_evolve/integrations/pydantic_ai/model_execution_profile.py +659 -0
  249. agent_evolve/integrations/pydantic_ai/outbound_request_manifest.py +1170 -0
  250. agent_evolve/integrations/pydantic_ai/portable_residual_consequence_features.py +575 -0
  251. agent_evolve/integrations/pydantic_ai/portfolio_selection.py +422 -0
  252. agent_evolve/integrations/pydantic_ai/progress_aware_openrouter.py +416 -0
  253. agent_evolve/integrations/pydantic_ai/provider_attempt_join.py +1523 -0
  254. agent_evolve/integrations/pydantic_ai/provider_free_calibrated_runner.py +607 -0
  255. agent_evolve/integrations/pydantic_ai/queued_runner.py +2634 -0
  256. agent_evolve/integrations/pydantic_ai/reconciled_residual_reachability.py +1417 -0
  257. agent_evolve/integrations/pydantic_ai/residual_forecast_geometry.py +445 -0
  258. agent_evolve/integrations/pydantic_ai/residual_reachability.py +674 -0
  259. agent_evolve/integrations/pydantic_ai/residual_semantic_cells.py +239 -0
  260. agent_evolve/integrations/pydantic_ai/sealed_output_replay.py +1068 -0
  261. agent_evolve/integrations/pydantic_ai/semantic_coverage_residual_portfolio.py +770 -0
  262. agent_evolve/integrations/pydantic_ai/semantic_decision_replay.py +383 -0
  263. agent_evolve/integrations/pydantic_ai/support_adaptive_residual_portfolio.py +135 -0
  264. agent_evolve/integrations/pydantic_ai/trusted_residual_prompt_context.py +143 -0
  265. agent_evolve/integrations/pydantic_ai/validated_openrouter_model.py +107 -0
  266. agent_evolve/integrations/pymoo_adapter.py +242 -0
  267. agent_evolve/policies/__init__.py +17 -0
  268. agent_evolve/policies/check.py +469 -0
  269. agent_evolve/policies/emit_scaffold.py +451 -0
  270. agent_evolve/policies/feedback/__init__.py +37 -0
  271. agent_evolve/policies/feedback/held_out_asn.py +1325 -0
  272. agent_evolve/policies/genetic.py +607 -0
  273. agent_evolve/policies/llm_backoff.py +183 -0
  274. agent_evolve/policies/llm_chooser.py +226 -0
  275. agent_evolve/policies/llm_generator.py +1760 -0
  276. agent_evolve/policies/llm_init.py +267 -0
  277. agent_evolve/policies/llm_operator.py +109 -0
  278. agent_evolve/policies/llm_prior.py +194 -0
  279. agent_evolve/policies/llm_surrogate.py +334 -0
  280. agent_evolve/policies/measurement_evidence.py +704 -0
  281. agent_evolve/policies/memory/__init__.py +223 -0
  282. agent_evolve/policies/memory/balanced_subset_blocks.py +707 -0
  283. agent_evolve/policies/memory/compatibility_matching.py +593 -0
  284. agent_evolve/policies/memory/global_falsification.py +1841 -0
  285. agent_evolve/policies/memory/prompt_shape.py +503 -0
  286. agent_evolve/policies/memory/randomized_subset.py +714 -0
  287. agent_evolve/policies/memory/staged_causal.py +1270 -0
  288. agent_evolve/policies/memory/treatment_compliance.py +759 -0
  289. agent_evolve/policies/objective_resolution/__init__.py +17 -0
  290. agent_evolve/policies/objective_resolution/fixed_grid.py +364 -0
  291. agent_evolve/policies/operator_portfolio.py +407 -0
  292. agent_evolve/policies/reguidance.py +1133 -0
  293. agent_evolve/policies/reward/__init__.py +83 -0
  294. agent_evolve/policies/reward/affine_candidate_consequence.py +156 -0
  295. agent_evolve/policies/reward/affine_candidate_consequence_3d.py +159 -0
  296. agent_evolve/policies/reward/affine_hypervolume.py +490 -0
  297. agent_evolve/policies/reward/affine_hypervolume_3d.py +567 -0
  298. agent_evolve/policies/reward/contextual_marginal_utility.py +318 -0
  299. agent_evolve/policies/reward/frozen_archive.py +360 -0
  300. agent_evolve/policies/reward/frozen_wave_archive.py +368 -0
  301. agent_evolve/policies/search_state.py +208 -0
  302. agent_evolve/policies/selection/__init__.py +345 -0
  303. agent_evolve/policies/selection/acquisition_certified_slate.py +684 -0
  304. agent_evolve/policies/selection/affine_frontier_context.py +330 -0
  305. agent_evolve/policies/selection/affine_frontier_target.py +473 -0
  306. agent_evolve/policies/selection/archive_elite.py +1346 -0
  307. agent_evolve/policies/selection/calibrated_portfolio_binding.py +640 -0
  308. agent_evolve/policies/selection/calibrated_slate.py +1394 -0
  309. agent_evolve/policies/selection/calibrated_slate_codec.py +579 -0
  310. agent_evolve/policies/selection/common_candidate_pool.py +685 -0
  311. agent_evolve/policies/selection/diagnostic_sampling.py +319 -0
  312. agent_evolve/policies/selection/disjoint_pairs.py +479 -0
  313. agent_evolve/policies/selection/elite_explorer.py +719 -0
  314. agent_evolve/policies/selection/finite_action.py +187 -0
  315. agent_evolve/policies/selection/finite_option_prompt_projection.py +377 -0
  316. agent_evolve/policies/selection/finite_palette_evidence.py +247 -0
  317. agent_evolve/policies/selection/forecast_calibration.py +922 -0
  318. agent_evolve/policies/selection/frontier_probe_slate.py +814 -0
  319. agent_evolve/policies/selection/frozen_archive_pairs.py +762 -0
  320. agent_evolve/policies/selection/full_support_slate.py +91 -0
  321. agent_evolve/policies/selection/meaningful_direction.py +240 -0
  322. agent_evolve/policies/selection/memory_dose_feasibility.py +259 -0
  323. agent_evolve/policies/selection/model_anchored_slate.py +826 -0
  324. agent_evolve/policies/selection/phenotype_recourse.py +979 -0
  325. agent_evolve/policies/selection/proposal_support.py +368 -0
  326. agent_evolve/policies/selection/random_portfolio.py +254 -0
  327. agent_evolve/policies/selection/regret_bounded_slate.py +1084 -0
  328. agent_evolve/policies/selection/residual_frontier.py +463 -0
  329. agent_evolve/policies/selection/residual_frontier_target.py +605 -0
  330. agent_evolve/policies/selection/structural_posterior_slate.py +1571 -0
  331. agent_evolve/policies/selection/target_conditioned_allocator.py +648 -0
  332. agent_evolve/policies/selection/target_conditioned_features.py +812 -0
  333. agent_evolve/policies/selection/target_conditioned_prequential.py +1527 -0
  334. agent_evolve/policies/selection/task_keyed_palette.py +906 -0
  335. agent_evolve/policies/semantics.py +147 -0
  336. agent_evolve/policies/structure.py +362 -0
  337. agent_evolve/policies/structured_output_budget.py +62 -0
  338. agent_evolve/policies/surrogate.py +696 -0
  339. agent_evolve/policies/variation/__init__.py +1 -0
  340. agent_evolve/policies/variation/compositional_finite_catalog.py +426 -0
  341. agent_evolve/policies/variation/crossover_inheritance.py +575 -0
  342. agent_evolve/policies/variation/disjoint_recombination.py +611 -0
  343. agent_evolve/policies/variation/exact_composition_capacity.py +214 -0
  344. agent_evolve/policies/variation/exact_parent_crossover.py +950 -0
  345. agent_evolve/policies/variation/multiscale_restart_catalog.py +372 -0
  346. agent_evolve/policies/variation/source_union_finite_catalog.py +403 -0
  347. agent_evolve/policies/variation/typed_patch.py +1981 -0
  348. agent_evolve/policies/weighted_prior.py +394 -0
  349. agent_evolve/ports/__init__.py +383 -0
  350. agent_evolve/ports/action_allocation.py +733 -0
  351. agent_evolve/ports/action_allocation_frame.py +1153 -0
  352. agent_evolve/ports/action_allocation_frame_commit.py +294 -0
  353. agent_evolve/ports/action_allocation_frame_commit_v3.py +432 -0
  354. agent_evolve/ports/action_allocation_frame_v3.py +995 -0
  355. agent_evolve/ports/action_forecast.py +1568 -0
  356. agent_evolve/ports/action_metric_projection.py +165 -0
  357. agent_evolve/ports/agentic_generator.py +1561 -0
  358. agent_evolve/ports/archive_context.py +136 -0
  359. agent_evolve/ports/artifact_sanitizer.py +44 -0
  360. agent_evolve/ports/artifact_store.py +225 -0
  361. agent_evolve/ports/clock.py +13 -0
  362. agent_evolve/ports/contextual_search_allocation.py +827 -0
  363. agent_evolve/ports/decision_metric_projection.py +258 -0
  364. agent_evolve/ports/event_store.py +55 -0
  365. agent_evolve/ports/executable_hypothesis.py +557 -0
  366. agent_evolve/ports/finite_acquisition.py +377 -0
  367. agent_evolve/ports/finite_acquisition_batch.py +296 -0
  368. agent_evolve/ports/finite_acquisition_batch_json.py +164 -0
  369. agent_evolve/ports/finite_acquisition_json.py +247 -0
  370. agent_evolve/ports/finite_acquisition_space.py +168 -0
  371. agent_evolve/ports/finite_action_selection.py +348 -0
  372. agent_evolve/ports/finite_action_set.py +256 -0
  373. agent_evolve/ports/frontier_target.py +396 -0
  374. agent_evolve/ports/generation_failure.py +43 -0
  375. agent_evolve/ports/hard_feasibility.py +233 -0
  376. agent_evolve/ports/id_factory.py +34 -0
  377. agent_evolve/ports/llm_task_queue.py +93 -0
  378. agent_evolve/ports/objective_resolution.py +419 -0
  379. agent_evolve/ports/paired_allocation_comparison.py +401 -0
  380. agent_evolve/ports/paired_block_schedule.py +475 -0
  381. agent_evolve/ports/parent_measurement.py +336 -0
  382. agent_evolve/ports/portfolio_memory_dose.py +643 -0
  383. agent_evolve/ports/portfolio_selection.py +3169 -0
  384. agent_evolve/ports/postcommit_rank_authority.py +467 -0
  385. agent_evolve/ports/presented_action_evidence.py +794 -0
  386. agent_evolve/ports/resource_lease.py +162 -0
  387. agent_evolve/ports/structured_generator.py +734 -0
  388. agent_evolve/ports/structured_output_budget.py +120 -0
  389. agent_evolve/ports/subprocess_boundary.py +138 -0
  390. agent_evolve/ports/treatment_assignment.py +466 -0
  391. agent_evolve/ports/variation_catalog.py +76 -0
  392. agent_evolve/ports/variation_source.py +226 -0
  393. agent_evolve/proposal_mode.py +157 -0
  394. agent_evolve/proposers/__init__.py +10 -0
  395. agent_evolve/proposers/random_proposer.py +188 -0
  396. agent_evolve/provider_accounting.py +163 -0
  397. agent_evolve/py.typed +0 -0
  398. agent_evolve/reference_method.py +1570 -0
  399. agent_evolve/session/__init__.py +11 -0
  400. agent_evolve/session/authorship.py +864 -0
  401. agent_evolve/session/evaluate.py +236 -0
  402. agent_evolve/session/fidelity.py +237 -0
  403. agent_evolve/session/genetic_loop.py +742 -0
  404. agent_evolve/session/loop.py +803 -0
  405. agent_evolve/session/screening.py +671 -0
  406. agent_evolve/settings.py +376 -0
  407. agent_evolve/workload_kit.py +368 -0
  408. agent_evolve/workload_prompt.py +398 -0
  409. agentevolve_optimizer-0.5.0.dist-info/METADATA +599 -0
  410. agentevolve_optimizer-0.5.0.dist-info/RECORD +414 -0
  411. agentevolve_optimizer-0.5.0.dist-info/WHEEL +5 -0
  412. agentevolve_optimizer-0.5.0.dist-info/entry_points.txt +2 -0
  413. agentevolve_optimizer-0.5.0.dist-info/licenses/LICENSE +21 -0
  414. agentevolve_optimizer-0.5.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,188 @@
1
+ """Bounded execution for authored artifacts: one process per batch, typed outcomes.
2
+
3
+ The optimizer never executes model-written source in its own process. Each
4
+ batch of calls is shipped to ``authored_worker`` through the deny-by-default
5
+ subprocess boundary -- exact argv, pinned cwd, no inherited environment --
6
+ with a wall-clock timeout enforced here and CPU/memory rlimits applied inside
7
+ the worker before the source is compiled. Every failure mode returns a
8
+ :class:`RuntimeOutcome` with a typed status; nothing raises into the loop,
9
+ because a crashing artifact is an ordinary, countable event, not an
10
+ emergency.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import json
16
+ import subprocess
17
+ import sys
18
+ import tempfile
19
+ from dataclasses import dataclass, field
20
+ from pathlib import Path
21
+ from typing import Any, Mapping, Optional, Sequence
22
+
23
+ from agent_evolve.core.authored import AuthoredArtifact
24
+ from agent_evolve.infrastructure.subprocess_boundary import (
25
+ ExplicitEnvironmentSubprocessBoundary,
26
+ )
27
+ from agent_evolve.ports.subprocess_boundary import ChildProcessPolicy
28
+
29
+ __all__ = ["RuntimeLimits", "RuntimeOutcome", "AuthoredRuntime"]
30
+
31
+ _DETAIL_LIMIT = 500
32
+
33
+ #: Statuses the runtime can report. ``ok`` carries results; everything else
34
+ #: carries a bounded detail string and empty results.
35
+ STATUSES = ("ok", "unparseable", "forbidden_import", "crash", "timeout",
36
+ "memory", "bad_shape")
37
+
38
+
39
+ @dataclass(frozen=True, slots=True)
40
+ class RuntimeLimits:
41
+ wall_time_s: float = 10.0
42
+ cpu_seconds: int = 8
43
+ memory_bytes: int = 512 * 1024 * 1024
44
+
45
+
46
+ @dataclass(frozen=True, slots=True)
47
+ class RuntimeOutcome:
48
+ status: str
49
+ results: tuple = ()
50
+ detail: str = ""
51
+ #: Counters a harness-written prelude published inside the sandbox (see
52
+ #: ``policies/emit_scaffold.py``). Empty whenever no prelude ran or the
53
+ #: prelude wrote nothing serializable -- diagnostics, never results.
54
+ notes: Mapping[str, Any] = field(default_factory=dict)
55
+
56
+ @property
57
+ def ok(self) -> bool:
58
+ return self.status == "ok"
59
+
60
+
61
+ class AuthoredRuntime:
62
+ """Runs authored artifacts out of process, one spawned worker per batch."""
63
+
64
+ def __init__(
65
+ self,
66
+ *,
67
+ limits: RuntimeLimits | None = None,
68
+ python_executable: str | None = None,
69
+ ) -> None:
70
+ self._limits = limits or RuntimeLimits()
71
+ self._python = python_executable or sys.executable
72
+ # The worker is launched BY PATH rather than as `-m agent_evolve...`.
73
+ # It imports nothing from this package (ast, json, sys, traceback and
74
+ # the interpreter), so the module form spent the whole of
75
+ # `agent_evolve.__init__` -- pydantic included -- on every batch:
76
+ # measured at 922ms per spawn against 21ms by path, which is 45x on
77
+ # every screen, every operator generation and every generated pool.
78
+ # The module form stays as a fallback for exotic installs (zipimport)
79
+ # where the source file is not on disk.
80
+ worker = Path(__file__).resolve().with_name("authored_worker.py")
81
+ self._worker = str(worker) if worker.is_file() else None
82
+ package_root = str(Path(__file__).resolve().parents[2])
83
+ self._policy = ChildProcessPolicy(
84
+ policy_id="authored_artifact_runtime",
85
+ policy_version=2,
86
+ inherited_environment_allowlist=(),
87
+ fixed_environment=(("PYTHONPATH", package_root),),
88
+ )
89
+
90
+ @property
91
+ def limits(self) -> RuntimeLimits:
92
+ return self._limits
93
+
94
+ def call(
95
+ self,
96
+ artifact: AuthoredArtifact,
97
+ calls: Sequence[Sequence[Any]],
98
+ *,
99
+ prelude: Optional[str] = None,
100
+ notes_global: Optional[str] = None,
101
+ ) -> RuntimeOutcome:
102
+ """Run *artifact* against every argument list in *calls*, bounded.
103
+
104
+ One process for the whole batch: a per-call process would pay the
105
+ interpreter startup once per candidate, and the batch is the unit the
106
+ loop actually needs (screen a pool, vary a generation's offspring).
107
+
108
+ *prelude* is HARNESS-written source executed into the artifact's
109
+ namespace before it -- machinery the caller would otherwise be asking
110
+ the model to re-derive, such as the emit scaffold that makes a
111
+ shape error impossible to construct. It is not import-gated, because
112
+ it is not the model's code; the artifact still is. *notes_global*
113
+ names a variable the prelude may leave counters in, returned as
114
+ :attr:`RuntimeOutcome.notes`.
115
+ """
116
+
117
+ if not calls:
118
+ return RuntimeOutcome(status="ok", results=())
119
+ with tempfile.TemporaryDirectory(prefix="agent_evolve_authored_") as scratch:
120
+ request_path = Path(scratch) / "request.json"
121
+ response_path = Path(scratch) / "response.json"
122
+ request = {
123
+ "schema_version": 1,
124
+ "entry_point": artifact.entry_point,
125
+ "source": artifact.source,
126
+ "limits": {
127
+ "cpu_seconds": self._limits.cpu_seconds,
128
+ "memory_bytes": self._limits.memory_bytes,
129
+ },
130
+ "calls": [list(call) for call in calls],
131
+ }
132
+ if prelude:
133
+ request["prelude"] = prelude
134
+ request["notes_global"] = notes_global or ""
135
+ request_path.write_text(json.dumps(request), encoding="utf-8")
136
+
137
+ boundary = ExplicitEnvironmentSubprocessBoundary(
138
+ policy=self._policy, working_directory=Path(scratch)
139
+ )
140
+ launch = ((self._worker,) if self._worker is not None
141
+ else ("-m", "agent_evolve.infrastructure.authored_worker"))
142
+ argv = (self._python, *launch,
143
+ str(request_path), str(response_path))
144
+ try:
145
+ result = boundary.run(argv, timeout_s=self._limits.wall_time_s)
146
+ except subprocess.TimeoutExpired:
147
+ return RuntimeOutcome(
148
+ status="timeout",
149
+ detail=f"wall limit {self._limits.wall_time_s}s",
150
+ )
151
+
152
+ if not response_path.exists():
153
+ if result.returncode != 0:
154
+ return RuntimeOutcome(
155
+ status="timeout" if result.returncode == -24 else "crash",
156
+ detail=(f"rc {result.returncode}: "
157
+ f"{result.stderr[-_DETAIL_LIMIT:]}"),
158
+ )
159
+ return RuntimeOutcome(
160
+ status="bad_shape", detail="worker wrote no response"
161
+ )
162
+ try:
163
+ payload = json.loads(response_path.read_text(encoding="utf-8"))
164
+ except (ValueError, OSError) as error:
165
+ return RuntimeOutcome(
166
+ status="bad_shape",
167
+ detail=f"unreadable response: {error}"[:_DETAIL_LIMIT],
168
+ )
169
+ status = payload.get("status")
170
+ if status not in STATUSES:
171
+ return RuntimeOutcome(
172
+ status="bad_shape", detail=f"unknown status {status!r}"
173
+ )
174
+ if status != "ok":
175
+ return RuntimeOutcome(
176
+ status=status, detail=str(payload.get("detail", ""))[:_DETAIL_LIMIT]
177
+ )
178
+ results = payload.get("results")
179
+ if not isinstance(results, list) or len(results) != len(calls):
180
+ return RuntimeOutcome(
181
+ status="bad_shape",
182
+ detail=(f"{len(results) if isinstance(results, list) else 'no'}"
183
+ f" results for {len(calls)} calls"),
184
+ )
185
+ notes = payload.get("notes")
186
+ return RuntimeOutcome(
187
+ status="ok", results=tuple(results),
188
+ notes=dict(notes) if isinstance(notes, dict) else {})
@@ -0,0 +1,171 @@
1
+ """Executes one authored artifact against a batch of calls, bounded.
2
+
3
+ Runs OUT of the optimizer's process, one process per batch, launched by
4
+ ``AuthoredRuntime`` through the deny-by-default subprocess boundary. The
5
+ threat model is untrusted-BUGGY code, not adversarial code: resource limits
6
+ contain runaway loops and allocations, the import gate keeps an artifact from
7
+ reaching the filesystem or network by accident, and every failure becomes a
8
+ typed status the parent counts -- never an exception in the optimizer.
9
+
10
+ Order of operations matters and is deliberate: the request is read first
11
+ (this module's own imports must work unconstrained), THEN the resource
12
+ limits are applied, THEN the source is gated and compiled. Authored code
13
+ never runs a single instruction outside the limits.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import ast
19
+ import json
20
+ import sys
21
+ import traceback
22
+
23
+ #: Modules an authored artifact may import. Enough for arithmetic, statistics
24
+ #: and seeded randomness; nothing that reaches the filesystem, the network,
25
+ #: or another process.
26
+ ALLOWED_IMPORTS = frozenset(
27
+ {"math", "statistics", "random", "json", "itertools", "collections",
28
+ "functools", "heapq", "bisect"}
29
+ )
30
+
31
+ _DETAIL_LIMIT = 500
32
+
33
+
34
+ def _forbidden_import(source: str) -> str | None:
35
+ """The first disallowed root module the source imports, or ``None``."""
36
+
37
+ tree = ast.parse(source)
38
+ for node in ast.walk(tree):
39
+ if isinstance(node, ast.Import):
40
+ for alias in node.names:
41
+ root = alias.name.split(".")[0]
42
+ if root not in ALLOWED_IMPORTS:
43
+ return root
44
+ elif isinstance(node, ast.ImportFrom):
45
+ root = (node.module or "").split(".")[0]
46
+ if node.level or root not in ALLOWED_IMPORTS:
47
+ return root or "<relative>"
48
+ elif isinstance(node, ast.Name) and node.id == "__import__":
49
+ return "__import__"
50
+ return None
51
+
52
+
53
+ def _apply_limits(limits: dict) -> None:
54
+ """Best-effort rlimits: containment for buggy code, not a security wall."""
55
+
56
+ try:
57
+ import resource
58
+ except ImportError: # non-POSIX: wall timeout remains
59
+ return
60
+ cpu = int(limits.get("cpu_seconds", 8))
61
+ memory = int(limits.get("memory_bytes", 512 * 1024 * 1024))
62
+ for kind, value in ((resource.RLIMIT_CPU, (cpu, cpu + 1)),
63
+ (resource.RLIMIT_AS, (memory, memory))):
64
+ try:
65
+ resource.setrlimit(kind, value)
66
+ except (ValueError, OSError):
67
+ pass
68
+
69
+
70
+ def _respond(path: str, payload: dict) -> None:
71
+ with open(path, "w", encoding="utf-8") as handle:
72
+ json.dump(payload, handle)
73
+
74
+
75
+ def main(argv: list[str]) -> int:
76
+ request_path, response_path = argv
77
+ with open(request_path, encoding="utf-8") as handle:
78
+ request = json.load(handle)
79
+
80
+ _apply_limits(request.get("limits") or {})
81
+ source = request["source"]
82
+ entry_point = request["entry_point"]
83
+
84
+ try:
85
+ forbidden = _forbidden_import(source)
86
+ except SyntaxError as error:
87
+ _respond(response_path, {"status": "unparseable",
88
+ "detail": str(error)[:_DETAIL_LIMIT]})
89
+ return 0
90
+ if forbidden is not None:
91
+ _respond(response_path, {"status": "forbidden_import", "detail": forbidden})
92
+ return 0
93
+
94
+ namespace: dict = {"__name__": "authored_artifact"}
95
+ # A HARNESS-WRITTEN prelude, executed into the same namespace before the
96
+ # artifact and never gated: it is ours, not the model's, and it is how the
97
+ # caller hands authored code machinery it should not have to re-derive
98
+ # (see policies/emit_scaffold.py). Absent, nothing changes.
99
+ prelude = request.get("prelude")
100
+ if prelude:
101
+ try:
102
+ exec(compile(prelude, "<harness_prelude>", "exec"), namespace) # noqa: S102
103
+ except Exception:
104
+ _respond(response_path, {
105
+ "status": "crash",
106
+ "detail": "harness prelude: "
107
+ + traceback.format_exc()[-_DETAIL_LIMIT:],
108
+ })
109
+ return 0
110
+
111
+ try:
112
+ exec(compile(source, "<authored>", "exec"), namespace) # noqa: S102
113
+ except SyntaxError as error:
114
+ _respond(response_path, {"status": "unparseable",
115
+ "detail": str(error)[:_DETAIL_LIMIT]})
116
+ return 0
117
+ except MemoryError:
118
+ _respond(response_path, {"status": "memory", "detail": "during exec"})
119
+ return 0
120
+ except Exception:
121
+ _respond(response_path, {"status": "crash",
122
+ "detail": traceback.format_exc()[-_DETAIL_LIMIT:]})
123
+ return 0
124
+
125
+ fn = namespace.get(entry_point)
126
+ if not callable(fn):
127
+ _respond(response_path, {
128
+ "status": "unparseable",
129
+ "detail": f"source defines no callable named {entry_point!r}",
130
+ })
131
+ return 0
132
+
133
+ results = []
134
+ try:
135
+ for call in request.get("calls", []):
136
+ results.append(fn(*call))
137
+ except MemoryError:
138
+ _respond(response_path, {"status": "memory",
139
+ "detail": f"after {len(results)} calls"})
140
+ return 0
141
+ except Exception:
142
+ _respond(response_path, {"status": "crash",
143
+ "detail": traceback.format_exc()[-_DETAIL_LIMIT:]})
144
+ return 0
145
+
146
+ payload = {"status": "ok", "results": results}
147
+ notes = request.get("notes_global")
148
+ if notes:
149
+ # The prelude's own counters, returned alongside the results. Only a
150
+ # JSON-serializable mapping crosses back, and a prelude that wrote
151
+ # something else costs the caller its diagnostics, never the batch.
152
+ value = namespace.get(notes)
153
+ if isinstance(value, dict):
154
+ try:
155
+ json.dumps(value)
156
+ except (TypeError, ValueError):
157
+ value = None
158
+ if value is not None:
159
+ payload["notes"] = value
160
+ try:
161
+ _respond(response_path, payload)
162
+ except (TypeError, ValueError):
163
+ _respond(response_path, {
164
+ "status": "bad_shape",
165
+ "detail": "results are not JSON-serializable",
166
+ })
167
+ return 0
168
+
169
+
170
+ if __name__ == "__main__": # pragma: no cover
171
+ raise SystemExit(main(sys.argv[1:]))
@@ -0,0 +1,53 @@
1
+ """System and deterministic clock implementations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import threading
6
+ import time
7
+ from datetime import datetime, timedelta, timezone
8
+
9
+
10
+ class SystemClock:
11
+ def utc_now(self) -> datetime:
12
+ return datetime.now(timezone.utc)
13
+
14
+ def monotonic_ns(self) -> int:
15
+ return time.monotonic_ns()
16
+
17
+
18
+ class FakeClock:
19
+ """Manually advanced clock; reads have no hidden side effects."""
20
+
21
+ def __init__(
22
+ self,
23
+ start_utc: datetime | None = None,
24
+ *,
25
+ start_monotonic_ns: int = 0,
26
+ ) -> None:
27
+ start_utc = start_utc or datetime(2000, 1, 1, tzinfo=timezone.utc)
28
+ if start_utc.utcoffset() != timedelta(0):
29
+ raise ValueError("start_utc must be timezone-aware UTC")
30
+ if (
31
+ isinstance(start_monotonic_ns, bool)
32
+ or not isinstance(start_monotonic_ns, int)
33
+ or start_monotonic_ns < 0
34
+ ):
35
+ raise ValueError("start_monotonic_ns must be a non-negative integer")
36
+ self._utc = start_utc
37
+ self._monotonic_ns = start_monotonic_ns
38
+ self._lock = threading.Lock()
39
+
40
+ def utc_now(self) -> datetime:
41
+ with self._lock:
42
+ return self._utc
43
+
44
+ def monotonic_ns(self) -> int:
45
+ with self._lock:
46
+ return self._monotonic_ns
47
+
48
+ def advance_ns(self, nanoseconds: int) -> None:
49
+ if isinstance(nanoseconds, bool) or not isinstance(nanoseconds, int) or nanoseconds < 0:
50
+ raise ValueError("nanoseconds must be a non-negative integer")
51
+ with self._lock:
52
+ self._monotonic_ns += nanoseconds
53
+ self._utc += timedelta(microseconds=nanoseconds / 1_000)
@@ -0,0 +1,6 @@
1
+ """Append-only event-store implementations."""
2
+
3
+ from agent_evolve.infrastructure.events.in_memory import InMemoryEventStore
4
+ from agent_evolve.infrastructure.events.jsonl import JsonlEventStore
5
+
6
+ __all__ = ["InMemoryEventStore", "JsonlEventStore"]
@@ -0,0 +1,89 @@
1
+ """Shared validation for EventStore implementations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from typing import AbstractSet, Sequence
6
+
7
+ from agent_evolve.domain.event import EventEnvelope, validated_event_snapshot
8
+ from agent_evolve.domain.ids import EventId, RunId
9
+ from agent_evolve.ports.event_store import (
10
+ DuplicateEventIdError,
11
+ EventCausationError,
12
+ EventRunMismatchError,
13
+ EventSequenceError,
14
+ OptimisticConcurrencyError,
15
+ )
16
+
17
+
18
+ def prepare_event_for_append(event: EventEnvelope) -> EventEnvelope:
19
+ """Detach and fully revalidate a caller-owned event before persistence."""
20
+
21
+ return validated_event_snapshot(event)
22
+
23
+
24
+ def validate_append(
25
+ existing: Sequence[EventEnvelope],
26
+ event: EventEnvelope,
27
+ *,
28
+ expected_previous_sequence: int,
29
+ known_event_ids: AbstractSet[EventId],
30
+ ) -> None:
31
+ if (
32
+ type(expected_previous_sequence) is not int
33
+ or expected_previous_sequence < 0
34
+ ):
35
+ raise EventSequenceError("expected_previous_sequence must be non-negative")
36
+
37
+ current_sequence = existing[-1].sequence_number if existing else 0
38
+ if expected_previous_sequence != current_sequence:
39
+ raise OptimisticConcurrencyError(
40
+ f"Expected previous sequence {expected_previous_sequence}, "
41
+ f"but run is at {current_sequence}"
42
+ )
43
+ if event.sequence_number != current_sequence + 1:
44
+ raise EventSequenceError(
45
+ f"Event sequence must be {current_sequence + 1}, got {event.sequence_number}"
46
+ )
47
+ if existing and event.run_id != existing[0].run_id:
48
+ raise EventRunMismatchError(
49
+ f"Event run {event.run_id} does not match stream {existing[0].run_id}"
50
+ )
51
+ if event.event_id in known_event_ids:
52
+ raise DuplicateEventIdError(f"Duplicate event ID {event.event_id}")
53
+ if event.causation_event_id is not None and all(
54
+ prior.event_id != event.causation_event_id for prior in existing
55
+ ):
56
+ raise EventCausationError(
57
+ f"Causation event {event.causation_event_id} must precede the event "
58
+ f"in the same run {event.run_id}"
59
+ )
60
+ if existing and event.monotonic_offset_ns < existing[-1].monotonic_offset_ns:
61
+ raise EventSequenceError("monotonic_offset_ns cannot decrease within a run")
62
+
63
+
64
+ def validate_loaded_stream(run_id: RunId, events: Sequence[EventEnvelope]) -> None:
65
+ previous_sequence = 0
66
+ previous_offset = 0
67
+ ids = set()
68
+ for event in events:
69
+ if event.run_id != run_id:
70
+ raise EventRunMismatchError(
71
+ f"Event run {event.run_id} does not match stream {run_id}"
72
+ )
73
+ if event.sequence_number != previous_sequence + 1:
74
+ raise EventSequenceError(
75
+ f"Non-contiguous event sequence for {run_id}: "
76
+ f"expected {previous_sequence + 1}, got {event.sequence_number}"
77
+ )
78
+ if event.event_id in ids:
79
+ raise DuplicateEventIdError(f"Duplicate event ID {event.event_id}")
80
+ if event.causation_event_id is not None and event.causation_event_id not in ids:
81
+ raise EventCausationError(
82
+ f"Causation event {event.causation_event_id} must precede the event "
83
+ f"in the same run {run_id}"
84
+ )
85
+ if previous_sequence and event.monotonic_offset_ns < previous_offset:
86
+ raise EventSequenceError("monotonic_offset_ns cannot decrease within a run")
87
+ ids.add(event.event_id)
88
+ previous_sequence = event.sequence_number
89
+ previous_offset = event.monotonic_offset_ns
@@ -0,0 +1,56 @@
1
+ """Thread-safe in-memory EventStore, primarily for tests and ephemeral runs."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import threading
6
+ from typing import Dict, List, Tuple
7
+
8
+ from agent_evolve.domain.event import EventEnvelope, validated_event_snapshot
9
+ from agent_evolve.domain.ids import EventId, RunId
10
+ from agent_evolve.infrastructure.events._validation import (
11
+ prepare_event_for_append,
12
+ validate_append,
13
+ )
14
+
15
+
16
+ class InMemoryEventStore:
17
+ def __init__(self) -> None:
18
+ self._events: Dict[RunId, List[EventEnvelope]] = {}
19
+ self._event_ids: set[EventId] = set()
20
+ self._lock = threading.RLock()
21
+
22
+ def append(
23
+ self,
24
+ event: EventEnvelope,
25
+ *,
26
+ expected_previous_sequence: int,
27
+ ) -> None:
28
+ snapshot = prepare_event_for_append(event)
29
+ with self._lock:
30
+ stream = self._events.setdefault(snapshot.run_id, [])
31
+ validate_append(
32
+ stream,
33
+ snapshot,
34
+ expected_previous_sequence=expected_previous_sequence,
35
+ known_event_ids=self._event_ids,
36
+ )
37
+ stream.append(snapshot)
38
+ self._event_ids.add(snapshot.event_id)
39
+
40
+ def read(
41
+ self,
42
+ run_id: RunId,
43
+ *,
44
+ after_sequence: int = 0,
45
+ ) -> Tuple[EventEnvelope, ...]:
46
+ if type(after_sequence) is not int or after_sequence < 0:
47
+ raise ValueError("after_sequence must be a non-negative integer")
48
+ if type(run_id) is not RunId:
49
+ raise TypeError("run_id must be an exact RunId")
50
+ canonical_run_id = RunId(run_id.value)
51
+ with self._lock:
52
+ return tuple(
53
+ validated_event_snapshot(event)
54
+ for event in self._events.get(canonical_run_id, ())
55
+ if event.sequence_number > after_sequence
56
+ )