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,854 @@
1
+ """Immutable value objects for typed, reversible candidate patches."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ from dataclasses import dataclass
7
+ from typing import Union
8
+
9
+ from agent_evolve.domain.ids import CandidateId
10
+ from agent_evolve.domain.typed_json import (
11
+ DEFAULT_TYPED_JSON_LIMITS,
12
+ FrozenJsonArray,
13
+ FrozenJsonObject,
14
+ FrozenJsonValue,
15
+ TypedJsonLimits,
16
+ canonical_typed_json_bytes,
17
+ freeze_json,
18
+ is_frozen_json_value,
19
+ is_json_scalar,
20
+ typed_json_equal,
21
+ typed_json_sha256,
22
+ validate_typed_json_limits,
23
+ )
24
+
25
+
26
+ _LOWER_HEX = frozenset("0123456789abcdef")
27
+ _PATCH_HASH_DOMAIN = b"agent-evolve:typed-patch:v1\x00"
28
+ _PATH_HASH_DOMAIN = b"agent-evolve:typed-path:v1\x00"
29
+ _OPERATION_EFFECT_HASH_DOMAIN = b"agent-evolve:patch-operation-effect:v1\x00"
30
+ MAX_PATH_SEGMENTS = 64
31
+ MAX_PATH_KEY_BYTES = 4096
32
+ MAX_COMPONENT_TAG_BYTES = 256
33
+ MAX_PATCH_OPERATIONS = 4096
34
+ MAX_PATCH_BYTES = 67_108_864
35
+
36
+
37
+ def require_sha256(value: str, name: str) -> None:
38
+ if (
39
+ type(value) is not str
40
+ or len(value) != 64
41
+ or any(character not in _LOWER_HEX for character in value)
42
+ ):
43
+ raise ValueError(f"{name} must be a lowercase SHA-256 digest")
44
+
45
+
46
+ def _validate_candidate_id(value: CandidateId, name: str) -> None:
47
+ if type(value) is not CandidateId:
48
+ raise TypeError(f"{name} must be an exact CandidateId")
49
+ CandidateId.__post_init__(value)
50
+
51
+
52
+ def _strict_utf8(value: str, *, name: str, max_bytes: int) -> bytes:
53
+ if type(value) is not str:
54
+ raise TypeError(f"{name} must be an exact string")
55
+ # UTF-8 never uses fewer bytes than Python code points. Rejecting this
56
+ # cheap lower bound first avoids encoding an attacker-sized exact string
57
+ # solely to discover that it exceeds a small field cap.
58
+ if len(value) > max_bytes:
59
+ raise ValueError(f"{name} exceeds its byte limit")
60
+ try:
61
+ encoded = value.encode("utf-8", errors="strict")
62
+ except UnicodeEncodeError as exc:
63
+ raise ValueError(f"{name} is not valid UTF-8 text") from exc
64
+ if len(encoded) > max_bytes:
65
+ raise ValueError(f"{name} exceeds its byte limit")
66
+ return encoded
67
+
68
+
69
+ def _frame(value: bytes) -> bytes:
70
+ if type(value) is not bytes:
71
+ raise TypeError("framed values must be exact bytes")
72
+ return len(value).to_bytes(8, "big", signed=False) + value
73
+
74
+
75
+ @dataclass(frozen=True, slots=True, eq=False)
76
+ class ObjectKey:
77
+ """An object-key path segment; never interchangeable with an array index."""
78
+
79
+ value: str
80
+
81
+ def __post_init__(self) -> None:
82
+ _strict_utf8(self.value, name="object-key path segment", max_bytes=MAX_PATH_KEY_BYTES)
83
+
84
+ def __eq__(self, other: object) -> bool:
85
+ if type(self) is not ObjectKey or type(other) is not ObjectKey:
86
+ return False
87
+ ObjectKey.__post_init__(self)
88
+ ObjectKey.__post_init__(other)
89
+ return self.value == other.value
90
+
91
+ def __hash__(self) -> int:
92
+ if type(self) is not ObjectKey:
93
+ raise TypeError("object-key path segments must be exact ObjectKey values")
94
+ ObjectKey.__post_init__(self)
95
+ return hash((ObjectKey, self.value))
96
+
97
+
98
+ @dataclass(frozen=True, slots=True, eq=False)
99
+ class ArrayIndex:
100
+ """An array-index path segment; never interchangeable with a string key."""
101
+
102
+ value: int
103
+
104
+ def __post_init__(self) -> None:
105
+ if type(self.value) is not int:
106
+ raise TypeError("array-index path segments must be exact integers")
107
+ if self.value < 0 or self.value > (1 << 63) - 1:
108
+ raise ValueError("array-index path segment is outside uint63 range")
109
+
110
+ def __eq__(self, other: object) -> bool:
111
+ if type(self) is not ArrayIndex or type(other) is not ArrayIndex:
112
+ return False
113
+ ArrayIndex.__post_init__(self)
114
+ ArrayIndex.__post_init__(other)
115
+ return self.value == other.value
116
+
117
+ def __hash__(self) -> int:
118
+ if type(self) is not ArrayIndex:
119
+ raise TypeError("array-index path segments must be exact ArrayIndex values")
120
+ ArrayIndex.__post_init__(self)
121
+ return hash((ArrayIndex, self.value))
122
+
123
+
124
+ PathSegment = Union[ObjectKey, ArrayIndex]
125
+
126
+
127
+ def canonical_path_bytes(path: "JsonPath") -> bytes:
128
+ validate_json_path(path)
129
+ chunks = [len(path.segments).to_bytes(8, "big", signed=False)]
130
+ for segment in path.segments:
131
+ if type(segment) is ObjectKey:
132
+ key = _strict_utf8(
133
+ segment.value,
134
+ name="object-key path segment",
135
+ max_bytes=MAX_PATH_KEY_BYTES,
136
+ )
137
+ chunks.extend((b"k", _frame(key)))
138
+ elif type(segment) is ArrayIndex:
139
+ chunks.extend((b"i", segment.value.to_bytes(8, "big", signed=False)))
140
+ else: # pragma: no cover - JsonPath validation closes the union.
141
+ raise AssertionError("unsupported path segment")
142
+ return b"".join(chunks)
143
+
144
+
145
+ @dataclass(frozen=True, slots=True, eq=False)
146
+ class JsonPath:
147
+ """A typed path through objects and arrays; the empty path denotes root."""
148
+
149
+ segments: tuple[PathSegment, ...] = ()
150
+
151
+ def __post_init__(self) -> None:
152
+ if type(self.segments) is not tuple:
153
+ raise TypeError("JsonPath.segments must be an exact tuple")
154
+ if len(self.segments) > MAX_PATH_SEGMENTS:
155
+ raise ValueError("path exceeds MAX_PATH_SEGMENTS")
156
+ for segment in self.segments:
157
+ if type(segment) is ObjectKey:
158
+ ObjectKey.__post_init__(segment)
159
+ elif type(segment) is ArrayIndex:
160
+ ArrayIndex.__post_init__(segment)
161
+ else:
162
+ raise TypeError(
163
+ "paths contain only exact ObjectKey or ArrayIndex segments"
164
+ )
165
+
166
+ def __eq__(self, other: object) -> bool:
167
+ if type(self) is not JsonPath or type(other) is not JsonPath:
168
+ return False
169
+ return canonical_path_bytes(self) == canonical_path_bytes(other)
170
+
171
+ def __hash__(self) -> int:
172
+ return hash((JsonPath, canonical_path_bytes(self)))
173
+
174
+ def child_key(self, key: str) -> "JsonPath":
175
+ validate_json_path(self)
176
+ return JsonPath(self.segments + (ObjectKey(key),))
177
+
178
+ def child_index(self, index: int) -> "JsonPath":
179
+ validate_json_path(self)
180
+ return JsonPath(self.segments + (ArrayIndex(index),))
181
+
182
+ def is_prefix_of(self, other: "JsonPath") -> bool:
183
+ validate_json_path(self)
184
+ validate_json_path(other)
185
+ if len(self.segments) > len(other.segments):
186
+ return False
187
+ return self.segments == other.segments[: len(self.segments)]
188
+
189
+ @property
190
+ def schema_identity(self) -> str:
191
+ digest = hashlib.sha256()
192
+ digest.update(_PATH_HASH_DOMAIN)
193
+ digest.update(canonical_path_bytes(self))
194
+ return digest.hexdigest()
195
+
196
+
197
+ def validate_json_path(path: JsonPath) -> None:
198
+ """Recursively validate an exact immutable typed path."""
199
+
200
+ if type(path) is not JsonPath:
201
+ raise TypeError("path must be an exact JsonPath")
202
+ JsonPath.__post_init__(path)
203
+ for segment in path.segments:
204
+ if type(segment) is ObjectKey:
205
+ ObjectKey.__post_init__(segment)
206
+ elif type(segment) is ArrayIndex:
207
+ ArrayIndex.__post_init__(segment)
208
+ else: # pragma: no cover - JsonPath.__post_init__ rejects this first.
209
+ raise TypeError("path contains an unsupported segment")
210
+
211
+
212
+ ROOT_PATH = JsonPath()
213
+
214
+
215
+ @dataclass(frozen=True, slots=True, eq=False)
216
+ class PatchLimits:
217
+ """Replay-visible bounds for one typed patch."""
218
+
219
+ json_limits: TypedJsonLimits = DEFAULT_TYPED_JSON_LIMITS
220
+ max_operations: int = 1024
221
+ max_path_segments: int = 32
222
+ max_patch_bytes: int = 33_554_432
223
+
224
+ def __post_init__(self) -> None:
225
+ validate_typed_json_limits(self.json_limits)
226
+ if type(self.max_operations) is not int:
227
+ raise TypeError("max_operations must be an exact integer")
228
+ if self.max_operations <= 0 or self.max_operations > MAX_PATCH_OPERATIONS:
229
+ raise ValueError(f"max_operations must lie in [1, {MAX_PATCH_OPERATIONS}]")
230
+ if type(self.max_path_segments) is not int:
231
+ raise TypeError("max_path_segments must be an exact integer")
232
+ if self.max_path_segments <= 0 or self.max_path_segments > MAX_PATH_SEGMENTS:
233
+ raise ValueError(
234
+ f"max_path_segments must lie in [1, {MAX_PATH_SEGMENTS}]"
235
+ )
236
+ if type(self.max_patch_bytes) is not int:
237
+ raise TypeError("max_patch_bytes must be an exact integer")
238
+ if self.max_patch_bytes <= 0 or self.max_patch_bytes > MAX_PATCH_BYTES:
239
+ raise ValueError(f"max_patch_bytes must lie in [1, {MAX_PATCH_BYTES}]")
240
+
241
+ def _validated_values(self) -> tuple[TypedJsonLimits, int, int, int]:
242
+ if type(self) is not PatchLimits:
243
+ raise TypeError("limits must be an exact PatchLimits value")
244
+ PatchLimits.__post_init__(self)
245
+ return (
246
+ self.json_limits,
247
+ self.max_operations,
248
+ self.max_path_segments,
249
+ self.max_patch_bytes,
250
+ )
251
+
252
+ def __eq__(self, other: object) -> bool:
253
+ if type(self) is not PatchLimits or type(other) is not PatchLimits:
254
+ return False
255
+ return self._validated_values() == other._validated_values()
256
+
257
+ def __hash__(self) -> int:
258
+ return hash((PatchLimits, self._validated_values()))
259
+
260
+
261
+ DEFAULT_PATCH_LIMITS = PatchLimits()
262
+
263
+
264
+ def validate_patch_limits(limits: PatchLimits) -> None:
265
+ """Revalidate an exact patch-limit graph, including nested JSON limits."""
266
+
267
+ if type(limits) is not PatchLimits:
268
+ raise TypeError("limits must be an exact PatchLimits value")
269
+ PatchLimits.__post_init__(limits)
270
+
271
+
272
+ def validate_semantic_component(value: str | None) -> None:
273
+ """Validate one exact bounded semantic-component tag.
274
+
275
+ This is public because both patch operations and replay-derived relation
276
+ values carry the same identity-bearing tag. Validation must happen before
277
+ equality, hashing, or encoding so string-like objects cannot execute code or
278
+ supply a different digest preimage.
279
+ """
280
+
281
+ if value is None:
282
+ return
283
+ encoded = _strict_utf8(
284
+ value,
285
+ name="semantic_component",
286
+ max_bytes=MAX_COMPONENT_TAG_BYTES,
287
+ )
288
+ if not encoded:
289
+ raise ValueError("semantic_component cannot be empty")
290
+
291
+
292
+ def _validate_operation_common(
293
+ path: JsonPath,
294
+ source_candidate_id: CandidateId,
295
+ semantic_component: str | None,
296
+ ) -> None:
297
+ validate_json_path(path)
298
+ _validate_candidate_id(source_candidate_id, "source_candidate_id")
299
+ validate_semantic_component(semantic_component)
300
+
301
+
302
+ def _canonical_permutation(
303
+ before: FrozenJsonArray,
304
+ after: FrozenJsonArray,
305
+ *,
306
+ limits: TypedJsonLimits = DEFAULT_TYPED_JSON_LIMITS,
307
+ ) -> tuple[int, ...] | None:
308
+ """Return the stable lowest-source-index matching permutation, if any."""
309
+
310
+ if type(before) is not FrozenJsonArray or type(after) is not FrozenJsonArray:
311
+ raise TypeError("permutation operands must be exact FrozenJsonArray values")
312
+ freeze_json(before, limits=limits)
313
+ freeze_json(after, limits=limits)
314
+ if len(before.items) != len(after.items):
315
+ return None
316
+ positions: dict[bytes, list[int]] = {}
317
+ for index, item in enumerate(before.items):
318
+ key = canonical_typed_json_bytes(item, limits=limits)
319
+ positions.setdefault(key, []).append(index)
320
+ consumed: dict[bytes, int] = {}
321
+ result: list[int] = []
322
+ for item in after.items:
323
+ key = canonical_typed_json_bytes(item, limits=limits)
324
+ cursor = consumed.get(key, 0)
325
+ candidates = positions.get(key)
326
+ if candidates is None or cursor >= len(candidates):
327
+ return None
328
+ result.append(candidates[cursor])
329
+ consumed[key] = cursor + 1
330
+ if any(consumed.get(key, 0) != len(indices) for key, indices in positions.items()):
331
+ return None
332
+ return tuple(result)
333
+
334
+
335
+ class _CanonicalPatchOperation:
336
+ """Type-sensitive comparison for immutable operation occurrences."""
337
+
338
+ __hash__ = None
339
+
340
+ def __eq__(self, other: object) -> bool:
341
+ if type(self) not in _OPERATION_KINDS or type(other) is not type(self):
342
+ return False
343
+ return operation_occurrence_bytes(self) == operation_occurrence_bytes(other)
344
+
345
+
346
+ @dataclass(frozen=True, slots=True, eq=False)
347
+ class ReplaceScalar(_CanonicalPatchOperation):
348
+ path: JsonPath
349
+ old_value: FrozenJsonValue
350
+ new_value: FrozenJsonValue
351
+ source_candidate_id: CandidateId
352
+ semantic_component: str | None = None
353
+
354
+ def __post_init__(self) -> None:
355
+ _validate_operation_common(
356
+ self.path, self.source_candidate_id, self.semantic_component
357
+ )
358
+ if not is_json_scalar(self.old_value) or not is_json_scalar(self.new_value):
359
+ raise TypeError("replace_scalar requires exact typed-JSON scalar values")
360
+ if typed_json_equal(self.old_value, self.new_value):
361
+ raise ValueError("replace_scalar cannot encode a no-op")
362
+
363
+ @property
364
+ def old_value_hash(self) -> str:
365
+ return typed_json_sha256(self.old_value)
366
+
367
+ @property
368
+ def new_value_hash(self) -> str:
369
+ return typed_json_sha256(self.new_value)
370
+
371
+ @property
372
+ def schema_field_identity(self) -> str:
373
+ return self.path.schema_identity
374
+
375
+
376
+ @dataclass(frozen=True, slots=True, eq=False)
377
+ class ReplaceSubtree(_CanonicalPatchOperation):
378
+ path: JsonPath
379
+ old_value: FrozenJsonValue
380
+ new_value: FrozenJsonValue
381
+ source_candidate_id: CandidateId
382
+ semantic_component: str | None = None
383
+
384
+ def __post_init__(self) -> None:
385
+ _validate_operation_common(
386
+ self.path, self.source_candidate_id, self.semantic_component
387
+ )
388
+ if not is_frozen_json_value(self.old_value) or not is_frozen_json_value(
389
+ self.new_value
390
+ ):
391
+ raise TypeError("replace_subtree requires frozen typed-JSON values")
392
+ if is_json_scalar(self.old_value) and is_json_scalar(self.new_value):
393
+ raise TypeError("scalar-to-scalar edits must use replace_scalar")
394
+ if typed_json_equal(self.old_value, self.new_value):
395
+ raise ValueError("replace_subtree cannot encode a no-op")
396
+
397
+ @property
398
+ def old_value_hash(self) -> str:
399
+ return typed_json_sha256(self.old_value)
400
+
401
+ @property
402
+ def new_value_hash(self) -> str:
403
+ return typed_json_sha256(self.new_value)
404
+
405
+ @property
406
+ def schema_field_identity(self) -> str:
407
+ return self.path.schema_identity
408
+
409
+
410
+ @dataclass(frozen=True, slots=True, eq=False)
411
+ class InsertSequenceItem(_CanonicalPatchOperation):
412
+ path: JsonPath
413
+ index: int
414
+ item: FrozenJsonValue
415
+ before_sequence: FrozenJsonArray
416
+ after_sequence: FrozenJsonArray
417
+ source_candidate_id: CandidateId
418
+ semantic_component: str | None = None
419
+
420
+ def __post_init__(self) -> None:
421
+ _validate_operation_common(
422
+ self.path, self.source_candidate_id, self.semantic_component
423
+ )
424
+ if type(self.index) is not int:
425
+ raise TypeError("insert index must be an exact integer")
426
+ if type(self.before_sequence) is not FrozenJsonArray or type(
427
+ self.after_sequence
428
+ ) is not FrozenJsonArray:
429
+ raise TypeError("insert_sequence_item requires frozen arrays")
430
+ if not is_frozen_json_value(self.item):
431
+ raise TypeError("inserted item must be a frozen typed-JSON value")
432
+ freeze_json(self.before_sequence)
433
+ freeze_json(self.after_sequence)
434
+ freeze_json(self.item)
435
+ if self.index < 0 or self.index > len(self.before_sequence.items):
436
+ raise ValueError("insert index is outside the source array")
437
+ expected = FrozenJsonArray(
438
+ self.before_sequence.items[: self.index]
439
+ + (self.item,)
440
+ + self.before_sequence.items[self.index :]
441
+ )
442
+ if not typed_json_equal(expected, self.after_sequence):
443
+ raise ValueError("after_sequence is not the declared insertion result")
444
+
445
+ @property
446
+ def old_value_hash(self) -> str:
447
+ return typed_json_sha256(self.before_sequence)
448
+
449
+ @property
450
+ def new_value_hash(self) -> str:
451
+ return typed_json_sha256(self.after_sequence)
452
+
453
+ @property
454
+ def schema_field_identity(self) -> str:
455
+ return self.path.schema_identity
456
+
457
+
458
+ @dataclass(frozen=True, slots=True, eq=False)
459
+ class DeleteSequenceItem(_CanonicalPatchOperation):
460
+ path: JsonPath
461
+ index: int
462
+ item: FrozenJsonValue
463
+ before_sequence: FrozenJsonArray
464
+ after_sequence: FrozenJsonArray
465
+ source_candidate_id: CandidateId
466
+ semantic_component: str | None = None
467
+
468
+ def __post_init__(self) -> None:
469
+ _validate_operation_common(
470
+ self.path, self.source_candidate_id, self.semantic_component
471
+ )
472
+ if type(self.index) is not int:
473
+ raise TypeError("delete index must be an exact integer")
474
+ if type(self.before_sequence) is not FrozenJsonArray or type(
475
+ self.after_sequence
476
+ ) is not FrozenJsonArray:
477
+ raise TypeError("delete_sequence_item requires frozen arrays")
478
+ if not is_frozen_json_value(self.item):
479
+ raise TypeError("deleted item must be a frozen typed-JSON value")
480
+ freeze_json(self.before_sequence)
481
+ freeze_json(self.after_sequence)
482
+ freeze_json(self.item)
483
+ if self.index < 0 or self.index >= len(self.before_sequence.items):
484
+ raise ValueError("delete index is outside the source array")
485
+ if not typed_json_equal(self.before_sequence.items[self.index], self.item):
486
+ raise ValueError("deleted item does not match the source array")
487
+ expected = FrozenJsonArray(
488
+ self.before_sequence.items[: self.index]
489
+ + self.before_sequence.items[self.index + 1 :]
490
+ )
491
+ if not typed_json_equal(expected, self.after_sequence):
492
+ raise ValueError("after_sequence is not the declared deletion result")
493
+
494
+ @property
495
+ def old_value_hash(self) -> str:
496
+ return typed_json_sha256(self.before_sequence)
497
+
498
+ @property
499
+ def new_value_hash(self) -> str:
500
+ return typed_json_sha256(self.after_sequence)
501
+
502
+ @property
503
+ def schema_field_identity(self) -> str:
504
+ return self.path.schema_identity
505
+
506
+
507
+ @dataclass(frozen=True, slots=True, eq=False)
508
+ class PermuteSequence(_CanonicalPatchOperation):
509
+ path: JsonPath
510
+ permutation: tuple[int, ...]
511
+ before_sequence: FrozenJsonArray
512
+ after_sequence: FrozenJsonArray
513
+ source_candidate_id: CandidateId
514
+ semantic_component: str | None = None
515
+
516
+ def __post_init__(self) -> None:
517
+ _validate_operation_common(
518
+ self.path, self.source_candidate_id, self.semantic_component
519
+ )
520
+ if type(self.before_sequence) is not FrozenJsonArray or type(
521
+ self.after_sequence
522
+ ) is not FrozenJsonArray:
523
+ raise TypeError("permute_sequence requires frozen arrays")
524
+ freeze_json(self.before_sequence)
525
+ freeze_json(self.after_sequence)
526
+ size = len(self.before_sequence.items)
527
+ if type(self.permutation) is not tuple:
528
+ raise TypeError("permutation must be an exact tuple of exact integers")
529
+ if len(self.permutation) != size:
530
+ raise ValueError("permutation must contain every source index exactly once")
531
+ if any(type(index) is not int for index in self.permutation):
532
+ raise TypeError("permutation must be an exact tuple of exact integers")
533
+ if set(self.permutation) != set(range(size)):
534
+ raise ValueError("permutation must contain every source index exactly once")
535
+ canonical = _canonical_permutation(
536
+ self.before_sequence,
537
+ self.after_sequence,
538
+ )
539
+ if canonical is None:
540
+ raise ValueError("before_sequence and after_sequence are not permutations")
541
+ if self.permutation != canonical:
542
+ raise ValueError(
543
+ "permutation is not the deterministic lowest-source-index mapping"
544
+ )
545
+ if self.permutation == tuple(range(size)):
546
+ raise ValueError("permute_sequence cannot encode an identity permutation")
547
+
548
+ @property
549
+ def old_value_hash(self) -> str:
550
+ return typed_json_sha256(self.before_sequence)
551
+
552
+ @property
553
+ def new_value_hash(self) -> str:
554
+ return typed_json_sha256(self.after_sequence)
555
+
556
+ @property
557
+ def schema_field_identity(self) -> str:
558
+ return self.path.schema_identity
559
+
560
+
561
+ PatchOperation = Union[
562
+ ReplaceScalar,
563
+ ReplaceSubtree,
564
+ InsertSequenceItem,
565
+ DeleteSequenceItem,
566
+ PermuteSequence,
567
+ ]
568
+
569
+ _OPERATION_KINDS = {
570
+ ReplaceScalar: b"replace_scalar",
571
+ ReplaceSubtree: b"replace_subtree",
572
+ InsertSequenceItem: b"insert_sequence_item",
573
+ DeleteSequenceItem: b"delete_sequence_item",
574
+ PermuteSequence: b"permute_sequence",
575
+ }
576
+
577
+
578
+ def validate_patch_operation(operation: PatchOperation) -> None:
579
+ """Re-run the complete invariant set for one exact operation value."""
580
+
581
+ operation_type = type(operation)
582
+ if operation_type not in _OPERATION_KINDS:
583
+ raise TypeError("unsupported patch operation type")
584
+ operation_type.__post_init__(operation)
585
+
586
+
587
+ def operation_kind(operation: PatchOperation) -> str:
588
+ validate_patch_operation(operation)
589
+ kind = _OPERATION_KINDS.get(type(operation))
590
+ if kind is None: # pragma: no cover - validator closes the mapping.
591
+ raise AssertionError("validated operation lacked a kind")
592
+ return kind.decode("ascii")
593
+
594
+
595
+ def _operation_sort_key_unchecked(operation: PatchOperation) -> tuple[bytes, bytes]:
596
+ return canonical_path_bytes(operation.path), _OPERATION_KINDS[type(operation)]
597
+
598
+
599
+ def operation_sort_key(operation: PatchOperation) -> tuple[bytes, bytes]:
600
+ validate_patch_operation(operation)
601
+ return _operation_sort_key_unchecked(operation)
602
+
603
+
604
+ def _operation_values(operation: PatchOperation) -> tuple[FrozenJsonValue, FrozenJsonValue]:
605
+ if type(operation) in (ReplaceScalar, ReplaceSubtree):
606
+ return operation.old_value, operation.new_value
607
+ if type(operation) in (InsertSequenceItem, DeleteSequenceItem, PermuteSequence):
608
+ return operation.before_sequence, operation.after_sequence
609
+ raise TypeError("unsupported patch operation type")
610
+
611
+
612
+ def operation_effect_bytes(
613
+ operation: PatchOperation,
614
+ *,
615
+ include_component: bool = True,
616
+ limits: TypedJsonLimits = DEFAULT_TYPED_JSON_LIMITS,
617
+ ) -> bytes:
618
+ """Canonical operation effect, excluding branch-specific occurrence IDs."""
619
+
620
+ if type(include_component) is not bool:
621
+ raise TypeError("include_component must be an exact Boolean")
622
+ validate_patch_operation(operation)
623
+ validate_typed_json_limits(limits)
624
+ old_value, new_value = _operation_values(operation)
625
+ component = (
626
+ b""
627
+ if not include_component or operation.semantic_component is None
628
+ else _strict_utf8(
629
+ operation.semantic_component,
630
+ name="semantic_component",
631
+ max_bytes=MAX_COMPONENT_TAG_BYTES,
632
+ )
633
+ )
634
+ parts = [
635
+ _frame(_OPERATION_KINDS[type(operation)]),
636
+ _frame(canonical_path_bytes(operation.path)),
637
+ _frame(component),
638
+ _frame(canonical_typed_json_bytes(old_value, limits=limits)),
639
+ _frame(canonical_typed_json_bytes(new_value, limits=limits)),
640
+ ]
641
+ if type(operation) in (InsertSequenceItem, DeleteSequenceItem):
642
+ parts.append(operation.index.to_bytes(8, "big", signed=False))
643
+ parts.append(
644
+ _frame(canonical_typed_json_bytes(operation.item, limits=limits))
645
+ )
646
+ elif type(operation) is PermuteSequence:
647
+ parts.append(len(operation.permutation).to_bytes(8, "big", signed=False))
648
+ parts.extend(
649
+ index.to_bytes(8, "big", signed=False)
650
+ for index in operation.permutation
651
+ )
652
+ return b"".join(parts)
653
+
654
+
655
+ def operation_occurrence_bytes(
656
+ operation: PatchOperation,
657
+ *,
658
+ limits: TypedJsonLimits = DEFAULT_TYPED_JSON_LIMITS,
659
+ ) -> bytes:
660
+ """Canonical operation occurrence, including its exact source identity."""
661
+
662
+ validate_patch_operation(operation)
663
+ source = operation.source_candidate_id.value.encode("ascii", errors="strict")
664
+ return _frame(source) + _frame(operation_effect_bytes(operation, limits=limits))
665
+
666
+
667
+ def operation_effect_sha256(
668
+ operation: PatchOperation,
669
+ *,
670
+ limits: TypedJsonLimits = DEFAULT_TYPED_JSON_LIMITS,
671
+ ) -> str:
672
+ """Hash one exact operation effect independently of its branch occurrence."""
673
+
674
+ digest = hashlib.sha256()
675
+ digest.update(_OPERATION_EFFECT_HASH_DOMAIN)
676
+ digest.update(operation_effect_bytes(operation, limits=limits))
677
+ return digest.hexdigest()
678
+
679
+
680
+ def _validate_non_overlapping_operation_paths(
681
+ operations: tuple[PatchOperation, ...],
682
+ *,
683
+ work_counter: list[int] | None = None,
684
+ ) -> None:
685
+ """Reject equal/prefix paths in linear total path length using a trie."""
686
+
687
+ terminal = object()
688
+ root: dict[object, object] = {}
689
+ for operation in operations:
690
+ node = root
691
+ for segment in operation.path.segments:
692
+ if work_counter is not None:
693
+ work_counter[0] += 1
694
+ if terminal in node:
695
+ raise ValueError("patch operation paths cannot overlap")
696
+ child = node.setdefault(segment, {})
697
+ if type(child) is not dict: # pragma: no cover - local trie invariant.
698
+ raise AssertionError("invalid patch-path trie")
699
+ node = child
700
+ if terminal in node or node:
701
+ raise ValueError("patch operation paths cannot overlap")
702
+ node[terminal] = True
703
+
704
+
705
+ def _validate_operation_limits(operation: PatchOperation, limits: PatchLimits) -> None:
706
+ validate_patch_operation(operation)
707
+ validate_patch_limits(limits)
708
+ if len(operation.path.segments) > limits.max_path_segments:
709
+ raise ValueError("operation path exceeds patch max_path_segments")
710
+ old_value, new_value = _operation_values(operation)
711
+ canonical_typed_json_bytes(old_value, limits=limits.json_limits)
712
+ canonical_typed_json_bytes(new_value, limits=limits.json_limits)
713
+
714
+
715
+ @dataclass(frozen=True, slots=True, eq=False)
716
+ class TypedPatch:
717
+ """One canonical, bounded transformation between exact candidate contents."""
718
+
719
+ base_candidate_id: CandidateId
720
+ target_candidate_id: CandidateId
721
+ base_hash: str
722
+ target_hash: str
723
+ operations: tuple[PatchOperation, ...]
724
+ limits: PatchLimits = DEFAULT_PATCH_LIMITS
725
+ schema_version: str = "typed_json_patch_v1"
726
+
727
+ def __post_init__(self) -> None:
728
+ _validate_candidate_id(self.base_candidate_id, "base_candidate_id")
729
+ _validate_candidate_id(self.target_candidate_id, "target_candidate_id")
730
+ if self.base_candidate_id == self.target_candidate_id:
731
+ raise ValueError("patch endpoints must be distinct candidate occurrences")
732
+ require_sha256(self.base_hash, "base_hash")
733
+ require_sha256(self.target_hash, "target_hash")
734
+ if type(self.operations) is not tuple:
735
+ raise TypeError("patch operations must be an exact tuple")
736
+ validate_patch_limits(self.limits)
737
+ if type(self.schema_version) is not str or self.schema_version != "typed_json_patch_v1":
738
+ raise ValueError("unsupported typed-patch schema_version")
739
+ if len(self.operations) > self.limits.max_operations:
740
+ raise ValueError("patch exceeds max_operations")
741
+ for operation in self.operations:
742
+ validate_patch_operation(operation)
743
+ canonical = tuple(sorted(self.operations, key=_operation_sort_key_unchecked))
744
+ if self.operations != canonical:
745
+ raise ValueError("patch operations must use canonical path/kind order")
746
+ for operation in self.operations:
747
+ if operation.source_candidate_id != self.base_candidate_id:
748
+ raise ValueError("every operation must bind the patch base occurrence")
749
+ _validate_operation_limits(operation, self.limits)
750
+ # Bound the exact framed preimage consumed by ``patch_hash`` (apart
751
+ # from the fixed domain tag, which is process-constant).
752
+ total_patch_bytes = (
753
+ 8
754
+ + len(self.schema_version.encode("ascii", errors="strict"))
755
+ + 8
756
+ + len(self.base_candidate_id.value.encode("ascii", errors="strict"))
757
+ + 8
758
+ + len(self.target_candidate_id.value.encode("ascii", errors="strict"))
759
+ + 64 # two raw SHA-256 endpoint digests
760
+ + 9 * 8 # replay limits
761
+ + 8 # operation count
762
+ )
763
+ if total_patch_bytes > self.limits.max_patch_bytes:
764
+ raise ValueError("patch exceeds max_patch_bytes")
765
+ for operation in self.operations:
766
+ total_patch_bytes += 8 + len(
767
+ operation_effect_bytes(operation, limits=self.limits.json_limits)
768
+ )
769
+ if total_patch_bytes > self.limits.max_patch_bytes:
770
+ raise ValueError("patch exceeds max_patch_bytes")
771
+ _validate_non_overlapping_operation_paths(self.operations)
772
+ if not self.operations and self.base_hash != self.target_hash:
773
+ raise ValueError("an empty patch requires identical base and target hashes")
774
+ if self.operations and self.base_hash == self.target_hash:
775
+ raise ValueError("a non-empty patch cannot claim identical endpoint hashes")
776
+
777
+ def __eq__(self, other: object) -> bool:
778
+ if type(self) is not TypedPatch or type(other) is not TypedPatch:
779
+ return False
780
+ return self.patch_hash == other.patch_hash
781
+
782
+ __hash__ = None
783
+
784
+ @property
785
+ def patch_hash(self) -> str:
786
+ validate_typed_patch(self)
787
+ digest = hashlib.sha256()
788
+ digest.update(_PATCH_HASH_DOMAIN)
789
+ digest.update(_frame(self.schema_version.encode("ascii", errors="strict")))
790
+ digest.update(_frame(self.base_candidate_id.value.encode("ascii", errors="strict")))
791
+ digest.update(_frame(self.target_candidate_id.value.encode("ascii", errors="strict")))
792
+ digest.update(bytes.fromhex(self.base_hash))
793
+ digest.update(bytes.fromhex(self.target_hash))
794
+ limit_values = (
795
+ self.limits.max_operations,
796
+ self.limits.max_path_segments,
797
+ self.limits.max_patch_bytes,
798
+ self.limits.json_limits.max_depth,
799
+ self.limits.json_limits.max_nodes,
800
+ self.limits.json_limits.max_container_items,
801
+ self.limits.json_limits.max_string_bytes,
802
+ self.limits.json_limits.max_integer_bits,
803
+ self.limits.json_limits.max_canonical_bytes,
804
+ )
805
+ for value in limit_values:
806
+ digest.update(value.to_bytes(8, "big", signed=False))
807
+ digest.update(len(self.operations).to_bytes(8, "big", signed=False))
808
+ for operation in self.operations:
809
+ digest.update(
810
+ _frame(
811
+ operation_effect_bytes(
812
+ operation,
813
+ limits=self.limits.json_limits,
814
+ )
815
+ )
816
+ )
817
+ return digest.hexdigest()
818
+
819
+
820
+ def validate_typed_patch(patch: TypedPatch) -> None:
821
+ """Revalidate a complete exact patch graph at a consuming boundary."""
822
+
823
+ if type(patch) is not TypedPatch:
824
+ raise TypeError("patch must be an exact TypedPatch")
825
+ TypedPatch.__post_init__(patch)
826
+
827
+
828
+ __all__ = [
829
+ "ArrayIndex",
830
+ "DEFAULT_PATCH_LIMITS",
831
+ "DeleteSequenceItem",
832
+ "InsertSequenceItem",
833
+ "JsonPath",
834
+ "ObjectKey",
835
+ "PatchLimits",
836
+ "PatchOperation",
837
+ "PermuteSequence",
838
+ "ROOT_PATH",
839
+ "ReplaceScalar",
840
+ "ReplaceSubtree",
841
+ "TypedPatch",
842
+ "canonical_path_bytes",
843
+ "operation_effect_bytes",
844
+ "operation_effect_sha256",
845
+ "operation_occurrence_bytes",
846
+ "operation_kind",
847
+ "operation_sort_key",
848
+ "require_sha256",
849
+ "validate_json_path",
850
+ "validate_patch_limits",
851
+ "validate_patch_operation",
852
+ "validate_semantic_component",
853
+ "validate_typed_patch",
854
+ ]