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,484 @@
1
+ """Conservative, deterministic sanitization for canonical JSON artifacts.
2
+
3
+ This adapter deliberately makes a bounded claim: it handles configured literal
4
+ secrets and a documented set of common credential forms, then rejects known
5
+ high-risk residual material. It is not advertised as a general DLP system.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import math
11
+ import re
12
+ import hashlib
13
+ import json
14
+ from types import MappingProxyType
15
+ from typing import Any, Iterable, Mapping
16
+
17
+ from agent_evolve.domain.artifact import ArtifactRole
18
+ from agent_evolve.ports.artifact_sanitizer import (
19
+ ArtifactMinimizationError,
20
+ ArtifactSanitizationError,
21
+ )
22
+
23
+ _REDACTED = "[REDACTED]"
24
+ _FAILED = object()
25
+ _POLICY_COMPONENT = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$")
26
+ _SECRET_KEY_NAMES = frozenset(
27
+ {
28
+ "accesstoken",
29
+ "apikey",
30
+ "authorization",
31
+ "authtoken",
32
+ "clientsecret",
33
+ "cookie",
34
+ "credential",
35
+ "credentials",
36
+ "password",
37
+ "passwd",
38
+ "privatekey",
39
+ "proxyauthorization",
40
+ "refreshtoken",
41
+ "secret",
42
+ "setcookie",
43
+ "token",
44
+ }
45
+ )
46
+ _SECRET_KEY_SUFFIXES = (
47
+ "accesstoken",
48
+ "accesskeyid",
49
+ "apikey",
50
+ "authtoken",
51
+ "clientsecret",
52
+ "credential",
53
+ "credentials",
54
+ "password",
55
+ "passwd",
56
+ "privatekey",
57
+ "refreshtoken",
58
+ "authorization",
59
+ "cookie",
60
+ "secret",
61
+ "secretaccesskey",
62
+ "secretkey",
63
+ "sessiontoken",
64
+ "signingkey",
65
+ "token",
66
+ )
67
+
68
+ # Each expression replaces the complete credential-bearing match. Patterns are
69
+ # ordered from contextual headers/assignments to standalone token signatures.
70
+ _CREDENTIAL_PATTERNS = (
71
+ re.compile(
72
+ r"\b(?:Bearer|Basic)[ \t]+[A-Za-z0-9._~+/=-]+",
73
+ re.IGNORECASE,
74
+ ),
75
+ re.compile(
76
+ r"(?<![A-Za-z0-9])(?:api[ _-]?key|access[ _-]?key(?:[ _-]?id)?|"
77
+ r"secret[ _-]?access[ _-]?key|session[ _-]?token|"
78
+ r"access[ _-]?token|auth[ _-]?token|refresh[ _-]?token|"
79
+ r"client[ _-]?secret|authorization|password|passwd|"
80
+ r"credential|token|secret)"
81
+ r"[ \t]*[:=][ \t]*(?:\"[^\"\r\n]+\"|'[^'\r\n]+'|[^\s,;}{]+)",
82
+ re.IGNORECASE,
83
+ ),
84
+ re.compile(r"\b(?:Cookie|Set-Cookie)[ \t]*:[^\r\n]+", re.IGNORECASE),
85
+ re.compile(
86
+ r"(?<![A-Za-z0-9])(?:sk|pk|rk)-[A-Za-z0-9_-]{12,}(?![A-Za-z0-9])",
87
+ re.IGNORECASE,
88
+ ),
89
+ re.compile(
90
+ r"(?<![A-Za-z0-9])or-v1-[A-Za-z0-9_-]{12,}(?![A-Za-z0-9])",
91
+ re.IGNORECASE,
92
+ ),
93
+ re.compile(
94
+ r"(?<![A-Za-z0-9])(?:ghp_[A-Za-z0-9]{20,}|"
95
+ r"github_pat_[A-Za-z0-9_]{20,})(?![A-Za-z0-9])"
96
+ ),
97
+ re.compile(
98
+ r"(?<![A-Za-z0-9])glpat-[A-Za-z0-9_-]{12,}(?![A-Za-z0-9])"
99
+ ),
100
+ re.compile(r"(?<![A-Za-z0-9])AKIA[0-9A-Z]{16}(?![A-Za-z0-9])"),
101
+ re.compile(
102
+ r"(?<![A-Za-z0-9])eyJ[A-Za-z0-9_-]{8,}\."
103
+ r"[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}(?![A-Za-z0-9])"
104
+ ),
105
+ )
106
+ _PRIVATE_KEY_MATERIAL = re.compile(
107
+ r"-----BEGIN(?: [A-Z0-9]+)? PRIVATE KEY-----", re.IGNORECASE
108
+ )
109
+ _CREDENTIAL_IN_URL = re.compile(
110
+ # Bound the scheme scan so long punctuation-heavy nonmatches remain linear.
111
+ r"\b[a-z][a-z0-9+.-]{0,31}://[^/\s:@]+:[^/\s@]+@", re.IGNORECASE
112
+ )
113
+
114
+
115
+ def _generic_minimization_failure() -> ArtifactMinimizationError:
116
+ return ArtifactMinimizationError(
117
+ "artifact minimization failed under the configured role policy"
118
+ )
119
+
120
+
121
+ def _generic_sanitization_failure() -> ArtifactSanitizationError:
122
+ return ArtifactSanitizationError(
123
+ "artifact sanitization rejected unsafe or unsupported content"
124
+ )
125
+
126
+
127
+ class TopLevelAllowlistMinimizer:
128
+ """Keep only explicitly allowed top-level fields for each artifact role.
129
+
130
+ Every role that may be used must be configured. There is intentionally no
131
+ implicit identity fallback: a missing policy is a fail-closed error.
132
+ """
133
+
134
+ __slots__ = ("_allowed", "_policy_config_sha256")
135
+
136
+ def __init__(
137
+ self,
138
+ allowed_fields_by_role: Mapping[ArtifactRole, Iterable[str]],
139
+ ) -> None:
140
+ prepared = self._prepare(allowed_fields_by_role)
141
+ if prepared is _FAILED:
142
+ raise _generic_minimization_failure()
143
+ assert isinstance(prepared, tuple)
144
+ self._allowed, self._policy_config_sha256 = prepared
145
+
146
+ @staticmethod
147
+ def _prepare(
148
+ allowed_fields_by_role: Mapping[ArtifactRole, Iterable[str]],
149
+ ) -> tuple[MappingProxyType, str] | object:
150
+ try:
151
+ prepared: dict[ArtifactRole, frozenset[str]] = {}
152
+ for role, fields in allowed_fields_by_role.items():
153
+ if not isinstance(role, ArtifactRole) or isinstance(fields, str):
154
+ raise _generic_minimization_failure()
155
+ validated_fields: list[str] = []
156
+ for field in fields:
157
+ if (
158
+ type(field) is not str
159
+ or not field
160
+ or field != field.strip()
161
+ ):
162
+ raise _generic_minimization_failure()
163
+ field.encode("utf-8", errors="strict")
164
+ validated_fields.append(field)
165
+ field_set = frozenset(validated_fields)
166
+ prepared[role] = field_set
167
+ policy_record = {
168
+ role.value: sorted(field_set)
169
+ for role, field_set in prepared.items()
170
+ }
171
+ encoded_policy = json.dumps(
172
+ policy_record,
173
+ ensure_ascii=False,
174
+ separators=(",", ":"),
175
+ sort_keys=True,
176
+ ).encode("utf-8", errors="strict")
177
+ return (
178
+ MappingProxyType(prepared),
179
+ hashlib.sha256(encoded_policy).hexdigest(),
180
+ )
181
+ except Exception:
182
+ return _FAILED
183
+
184
+ @property
185
+ def policy_id(self) -> str:
186
+ return "top-level-allowlist"
187
+
188
+ @property
189
+ def policy_version(self) -> str:
190
+ return "1"
191
+
192
+ @property
193
+ def policy_config_sha256(self) -> str:
194
+ return self._policy_config_sha256
195
+
196
+ def minimize_json(self, value: Any, *, role: ArtifactRole) -> Any:
197
+ minimized = self._attempt_minimize(value, role)
198
+ if minimized is _FAILED:
199
+ raise _generic_minimization_failure()
200
+ return minimized
201
+
202
+ def _attempt_minimize(self, value: Any, role: ArtifactRole) -> Any:
203
+ try:
204
+ if not isinstance(role, ArtifactRole):
205
+ raise _generic_minimization_failure()
206
+ allowed = self._allowed.get(role)
207
+ if allowed is None or type(value) is not dict:
208
+ raise _generic_minimization_failure()
209
+ # Sorting is not required by the canonical encoder, but makes the
210
+ # intermediate result deterministic. Iterating the small allowlist
211
+ # also avoids touching or sorting fields that minimization discards.
212
+ return {
213
+ key: value[key]
214
+ for key in sorted(allowed)
215
+ if key in value
216
+ }
217
+ except Exception:
218
+ return _FAILED
219
+
220
+
221
+ class StrictJsonSanitizer:
222
+ """Recursively redact a strict JSON value under a versioned policy."""
223
+
224
+ __slots__ = (
225
+ "_exact_secrets",
226
+ "_max_depth",
227
+ "_max_nodes",
228
+ "_max_string_bytes",
229
+ "_max_total_string_bytes",
230
+ "_policy_id",
231
+ "_policy_version",
232
+ )
233
+
234
+ def __init__(
235
+ self,
236
+ *,
237
+ exact_secret_values: Iterable[str] = (),
238
+ policy_id: str = "strict-json-redaction",
239
+ policy_version: str = "1",
240
+ max_depth: int = 64,
241
+ max_nodes: int = 100_000,
242
+ max_string_bytes: int = 1_000_000,
243
+ max_total_string_bytes: int = 4_000_000,
244
+ ) -> None:
245
+ prepared = self._prepare_configuration(
246
+ exact_secret_values=exact_secret_values,
247
+ policy_id=policy_id,
248
+ policy_version=policy_version,
249
+ max_depth=max_depth,
250
+ max_nodes=max_nodes,
251
+ max_string_bytes=max_string_bytes,
252
+ max_total_string_bytes=max_total_string_bytes,
253
+ )
254
+ if prepared is _FAILED:
255
+ raise _generic_sanitization_failure()
256
+ (
257
+ self._exact_secrets,
258
+ self._policy_id,
259
+ self._policy_version,
260
+ self._max_depth,
261
+ self._max_nodes,
262
+ self._max_string_bytes,
263
+ self._max_total_string_bytes,
264
+ ) = prepared
265
+
266
+ @staticmethod
267
+ def _prepare_configuration(
268
+ *,
269
+ exact_secret_values: Iterable[str],
270
+ policy_id: str,
271
+ policy_version: str,
272
+ max_depth: int,
273
+ max_nodes: int,
274
+ max_string_bytes: int,
275
+ max_total_string_bytes: int,
276
+ ) -> tuple[Any, ...] | object:
277
+ try:
278
+ if (
279
+ not isinstance(policy_id, str)
280
+ or _POLICY_COMPONENT.fullmatch(policy_id) is None
281
+ or not isinstance(policy_version, str)
282
+ or _POLICY_COMPONENT.fullmatch(policy_version) is None
283
+ ):
284
+ raise _generic_sanitization_failure()
285
+ if (
286
+ isinstance(max_depth, bool)
287
+ or not isinstance(max_depth, int)
288
+ or max_depth < 1
289
+ or isinstance(max_nodes, bool)
290
+ or not isinstance(max_nodes, int)
291
+ or max_nodes < 1
292
+ or isinstance(max_string_bytes, bool)
293
+ or not isinstance(max_string_bytes, int)
294
+ or max_string_bytes < 1
295
+ or isinstance(max_total_string_bytes, bool)
296
+ or not isinstance(max_total_string_bytes, int)
297
+ or max_total_string_bytes < 1
298
+ ):
299
+ raise _generic_sanitization_failure()
300
+ secrets: set[str] = set()
301
+ for secret in exact_secret_values:
302
+ if (
303
+ not isinstance(secret, str)
304
+ or len(secret) < 8
305
+ or secret in _REDACTED
306
+ ):
307
+ raise _generic_sanitization_failure()
308
+ encoded_secret = secret.encode("utf-8", errors="strict")
309
+ if len(encoded_secret) > max_string_bytes:
310
+ raise _generic_sanitization_failure()
311
+ secrets.add(secret)
312
+ if any(
313
+ secret in policy_id or secret in policy_version
314
+ for secret in secrets
315
+ ):
316
+ raise _generic_sanitization_failure()
317
+ if (
318
+ _PRIVATE_KEY_MATERIAL.search(policy_id)
319
+ or _PRIVATE_KEY_MATERIAL.search(policy_version)
320
+ or _CREDENTIAL_IN_URL.search(policy_id)
321
+ or _CREDENTIAL_IN_URL.search(policy_version)
322
+ or any(
323
+ pattern.search(component)
324
+ for pattern in _CREDENTIAL_PATTERNS
325
+ for component in (policy_id, policy_version)
326
+ )
327
+ ):
328
+ raise _generic_sanitization_failure()
329
+ # Longest-first replacement prevents a shorter configured value from
330
+ # exposing the unmatched tail of an overlapping longer value.
331
+ exact_secrets = tuple(
332
+ sorted(secrets, key=lambda item: (-len(item), item))
333
+ )
334
+ return (
335
+ exact_secrets,
336
+ policy_id,
337
+ policy_version,
338
+ max_depth,
339
+ max_nodes,
340
+ max_string_bytes,
341
+ max_total_string_bytes,
342
+ )
343
+ except Exception:
344
+ return _FAILED
345
+
346
+ def __repr__(self) -> str:
347
+ return (
348
+ f"{type(self).__name__}(policy_id={self._policy_id!r}, "
349
+ f"policy_version={self._policy_version!r})"
350
+ )
351
+
352
+ @property
353
+ def policy_id(self) -> str:
354
+ return self._policy_id
355
+
356
+ @property
357
+ def policy_version(self) -> str:
358
+ return self._policy_version
359
+
360
+ @staticmethod
361
+ def _is_secret_key(key: str) -> bool:
362
+ normalized = "".join(character.lower() for character in key if character.isalnum())
363
+ return normalized in _SECRET_KEY_NAMES or normalized.endswith(
364
+ _SECRET_KEY_SUFFIXES
365
+ )
366
+
367
+ def _sanitize_valid_string(self, value: str, *, is_key: bool = False) -> str:
368
+ if is_key:
369
+ # Renaming keys could create collisions or alter a schema silently.
370
+ if any(secret in value for secret in self._exact_secrets):
371
+ raise _generic_sanitization_failure()
372
+ if (
373
+ _PRIVATE_KEY_MATERIAL.search(value)
374
+ or _CREDENTIAL_IN_URL.search(value)
375
+ or any(pattern.search(value) for pattern in _CREDENTIAL_PATTERNS)
376
+ ):
377
+ raise _generic_sanitization_failure()
378
+ return value
379
+
380
+ sanitized = value
381
+ for secret in self._exact_secrets:
382
+ sanitized = sanitized.replace(secret, _REDACTED)
383
+ for pattern in _CREDENTIAL_PATTERNS:
384
+ sanitized = pattern.sub(_REDACTED, sanitized)
385
+
386
+ # These forms are too structurally sensitive to persist after an
387
+ # in-place rewrite; fail closed and let the caller supply a minimizer.
388
+ if _PRIVATE_KEY_MATERIAL.search(sanitized) or _CREDENTIAL_IN_URL.search(
389
+ sanitized
390
+ ):
391
+ raise _generic_sanitization_failure()
392
+ if any(secret in sanitized for secret in self._exact_secrets):
393
+ raise _generic_sanitization_failure()
394
+ return sanitized
395
+
396
+ def sanitize_json(self, value: Any, *, role: ArtifactRole) -> Any:
397
+ sanitized = self._attempt_sanitize(value, role)
398
+ if sanitized is _FAILED:
399
+ raise _generic_sanitization_failure()
400
+ return sanitized
401
+
402
+ def _attempt_sanitize(self, value: Any, role: ArtifactRole) -> Any:
403
+ ancestors: set[int] = set()
404
+ nodes_seen = 0
405
+ string_bytes_seen = 0
406
+
407
+ def reserve_nodes(count: int = 1) -> None:
408
+ nonlocal nodes_seen
409
+ nodes_seen += count
410
+ if nodes_seen > self._max_nodes:
411
+ raise _generic_sanitization_failure()
412
+
413
+ def sanitize_string(item: str, *, is_key: bool = False) -> str:
414
+ nonlocal string_bytes_seen
415
+ encoded = item.encode("utf-8", errors="strict")
416
+ size = len(encoded)
417
+ string_bytes_seen += size
418
+ if (
419
+ size > self._max_string_bytes
420
+ or string_bytes_seen > self._max_total_string_bytes
421
+ ):
422
+ raise _generic_sanitization_failure()
423
+ return self._sanitize_valid_string(item, is_key=is_key)
424
+
425
+ def visit(item: Any, depth: int) -> Any:
426
+ reserve_nodes()
427
+ if depth > self._max_depth:
428
+ raise _generic_sanitization_failure()
429
+
430
+ if item is None or type(item) in (bool, int):
431
+ return item
432
+ if type(item) is float:
433
+ if not math.isfinite(item):
434
+ raise _generic_sanitization_failure()
435
+ return item
436
+ if type(item) is str:
437
+ return sanitize_string(item)
438
+ if type(item) in (list, tuple):
439
+ if len(item) > self._max_nodes - nodes_seen:
440
+ raise _generic_sanitization_failure()
441
+ identity = id(item)
442
+ if identity in ancestors:
443
+ raise _generic_sanitization_failure()
444
+ ancestors.add(identity)
445
+ try:
446
+ return [visit(child, depth + 1) for child in item]
447
+ finally:
448
+ ancestors.remove(identity)
449
+ if type(item) is dict:
450
+ # Every entry accounts for one key and at least one value slot.
451
+ # Gate this before sorting to avoid unbounded pre-check work.
452
+ if len(item) * 2 > self._max_nodes - nodes_seen:
453
+ raise _generic_sanitization_failure()
454
+ identity = id(item)
455
+ if identity in ancestors:
456
+ raise _generic_sanitization_failure()
457
+ ancestors.add(identity)
458
+ try:
459
+ result: dict[str, Any] = {}
460
+ validated_keys: list[str] = []
461
+ for key in item:
462
+ reserve_nodes() # object key
463
+ if type(key) is not str:
464
+ raise _generic_sanitization_failure()
465
+ validated_keys.append(sanitize_string(key, is_key=True))
466
+ for clean_key in sorted(validated_keys):
467
+ if self._is_secret_key(clean_key):
468
+ reserve_nodes() # redacted value slot
469
+ result[clean_key] = _REDACTED
470
+ else:
471
+ result[clean_key] = visit(item[clean_key], depth + 1)
472
+ return result
473
+ finally:
474
+ ancestors.remove(identity)
475
+ raise _generic_sanitization_failure()
476
+
477
+ try:
478
+ if not isinstance(role, ArtifactRole):
479
+ raise _generic_sanitization_failure()
480
+ return visit(value, 0)
481
+ except Exception:
482
+ # Returning clears implicit exception context before the public
483
+ # method constructs its generic error.
484
+ return _FAILED
@@ -0,0 +1,170 @@
1
+ """Durable JSONL interception for sequential residual-evolution phases."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import json
7
+ import os
8
+ from dataclasses import dataclass, field
9
+ from pathlib import Path
10
+
11
+ from agent_evolve.application.sequential_residual_portfolio_evolution import (
12
+ SequentialResidualPhaseCommitAck,
13
+ SequentialResidualPhaseReceipt,
14
+ )
15
+ from agent_evolve.domain.typed_json import freeze_json
16
+
17
+
18
+ DURABLE_SEQUENTIAL_PHASE_JOURNAL_ID = (
19
+ "durable_jsonl_sequential_phase_journal"
20
+ )
21
+ DURABLE_SEQUENTIAL_PHASE_JOURNAL_VERSION = 1
22
+ DURABLE_SEQUENTIAL_PHASE_JOURNAL_DEFINITION_SHA256 = hashlib.sha256(
23
+ b"agent-evolve:durable-jsonl-sequential-phase-journal:v1;"
24
+ b"record=full-receipt-evidence-plus-authenticated-ack;"
25
+ b"write=posix-append-before-fsync;"
26
+ b"first-create=parent-directory-fsync;"
27
+ b"duplicate-phase-receipt=fail-closed"
28
+ ).hexdigest()
29
+
30
+
31
+ def _canonical_json(value: object) -> bytes:
32
+ return json.dumps(
33
+ value,
34
+ allow_nan=False,
35
+ ensure_ascii=True,
36
+ separators=(",", ":"),
37
+ sort_keys=True,
38
+ ).encode("ascii", errors="strict")
39
+
40
+
41
+ def _fsync_directory(path: Path) -> None:
42
+ if os.name != "posix": # pragma: no cover - production target is POSIX.
43
+ return
44
+ descriptor = os.open(path, os.O_RDONLY | os.O_DIRECTORY)
45
+ try:
46
+ os.fsync(descriptor)
47
+ finally:
48
+ os.close(descriptor)
49
+
50
+
51
+ @dataclass(slots=True)
52
+ class DurableJsonlSequentialPhaseCommitter:
53
+ """Append and fsync every phase before the runtime may continue."""
54
+
55
+ path: Path
56
+ committer_id: str = field(
57
+ init=False,
58
+ default=DURABLE_SEQUENTIAL_PHASE_JOURNAL_ID,
59
+ )
60
+ committer_version: int = field(
61
+ init=False,
62
+ default=DURABLE_SEQUENTIAL_PHASE_JOURNAL_VERSION,
63
+ )
64
+ definition_sha256: str = field(
65
+ init=False,
66
+ default=DURABLE_SEQUENTIAL_PHASE_JOURNAL_DEFINITION_SHA256,
67
+ )
68
+ _seen_receipt_sha256s: set[str] = field(
69
+ init=False,
70
+ default_factory=set,
71
+ )
72
+ _commit_count: int = field(init=False, default=0)
73
+
74
+ def __post_init__(self) -> None:
75
+ if not isinstance(self.path, Path):
76
+ raise TypeError("path must be a pathlib.Path")
77
+ if self.path.exists() and not self.path.is_file():
78
+ raise ValueError("phase journal path must be a regular file")
79
+ self.path.parent.mkdir(parents=True, exist_ok=True)
80
+ if not self.path.exists():
81
+ return
82
+ for line_number, raw in enumerate(
83
+ self.path.read_bytes().splitlines(),
84
+ start=1,
85
+ ):
86
+ if not raw.strip():
87
+ continue
88
+ try:
89
+ row = json.loads(raw)
90
+ receipt_sha256 = row["receipt"]["receipt_sha256"]
91
+ sequence = row["sequence"]
92
+ except (KeyError, TypeError, ValueError) as error:
93
+ raise ValueError(
94
+ f"invalid sequential phase journal line {line_number}"
95
+ ) from error
96
+ if (
97
+ type(receipt_sha256) is not str
98
+ or len(receipt_sha256) != 64
99
+ or type(sequence) is not int
100
+ or sequence <= 0
101
+ ):
102
+ raise ValueError(
103
+ f"invalid sequential phase journal line {line_number}"
104
+ )
105
+ if receipt_sha256 in self._seen_receipt_sha256s:
106
+ raise ValueError("phase journal repeats a receipt")
107
+ self._seen_receipt_sha256s.add(receipt_sha256)
108
+ self._commit_count = max(self._commit_count, sequence)
109
+
110
+ async def commit(
111
+ self,
112
+ receipt: SequentialResidualPhaseReceipt,
113
+ ) -> SequentialResidualPhaseCommitAck:
114
+ if type(receipt) is not SequentialResidualPhaseReceipt:
115
+ raise TypeError("receipt must be exact")
116
+ receipt.__post_init__()
117
+ if receipt.receipt_sha256 in self._seen_receipt_sha256s:
118
+ raise ValueError("phase receipt was already durably committed")
119
+ sequence = self._commit_count + 1
120
+ ack = SequentialResidualPhaseCommitAck(
121
+ committer_id=self.committer_id,
122
+ committer_version=self.committer_version,
123
+ committer_definition_sha256=self.definition_sha256,
124
+ phase_receipt_sha256=receipt.receipt_sha256,
125
+ durable=True,
126
+ evidence=freeze_json(
127
+ {
128
+ "backend": "posix_append_fsync",
129
+ "append_sequence": sequence,
130
+ "receipt_evidence_included": True,
131
+ "parent_directory_fsynced_on_create": True,
132
+ }
133
+ ),
134
+ )
135
+ row = {
136
+ "schema_version": 1,
137
+ "sequence": sequence,
138
+ "receipt": receipt.to_record(include_evidence=True),
139
+ "ack": ack.to_record(include_evidence=True),
140
+ }
141
+ payload = _canonical_json(row) + b"\n"
142
+ created = not self.path.exists()
143
+ descriptor = os.open(
144
+ self.path,
145
+ os.O_WRONLY | os.O_CREAT | os.O_APPEND,
146
+ 0o600,
147
+ )
148
+ try:
149
+ offset = 0
150
+ while offset < len(payload):
151
+ written = os.write(descriptor, payload[offset:])
152
+ if written <= 0: # pragma: no cover - defensive OS boundary.
153
+ raise OSError("phase journal append made no progress")
154
+ offset += written
155
+ os.fsync(descriptor)
156
+ finally:
157
+ os.close(descriptor)
158
+ if created:
159
+ _fsync_directory(self.path.parent)
160
+ self._seen_receipt_sha256s.add(receipt.receipt_sha256)
161
+ self._commit_count = sequence
162
+ return ack
163
+
164
+
165
+ __all__ = [
166
+ "DURABLE_SEQUENTIAL_PHASE_JOURNAL_DEFINITION_SHA256",
167
+ "DURABLE_SEQUENTIAL_PHASE_JOURNAL_ID",
168
+ "DURABLE_SEQUENTIAL_PHASE_JOURNAL_VERSION",
169
+ "DurableJsonlSequentialPhaseCommitter",
170
+ ]