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,542 @@
1
+ """Immutable, type-sensitive JSON values and canonical hashing.
2
+
3
+ The standard-library JSON codec intentionally treats several Python values as
4
+ interchangeable (notably ``True`` and ``1``) and offers no immutable container
5
+ representation. Evolutionary lineage needs stricter evidence: hashes and
6
+ patch preconditions must identify the exact typed tree that was observed.
7
+
8
+ This module is an inward domain codec. It accepts only exact built-in JSON
9
+ types, freezes objects/arrays, rejects cycles and non-finite numbers, and emits
10
+ a small length-framed binary canonical form. It is not an interchange JSON
11
+ serializer and it has no filesystem or framework dependency.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ import hashlib
17
+ import math
18
+ import struct
19
+ from dataclasses import dataclass, field
20
+ from typing import Union
21
+
22
+
23
+ _TYPED_JSON_HASH_DOMAIN = b"agent-evolve:typed-json:v1\x00"
24
+ _UINT64_MAX = (1 << 64) - 1
25
+
26
+
27
+ @dataclass(frozen=True, slots=True, eq=False)
28
+ class TypedJsonLimits:
29
+ """Hard resource bounds for validation and canonicalization.
30
+
31
+ User-provided limits may only tighten these process-independent ceilings.
32
+ This keeps a serialized patch from claiming effectively unbounded limits.
33
+ """
34
+
35
+ max_depth: int = 64
36
+ max_nodes: int = 50_000
37
+ max_container_items: int = 10_000
38
+ max_string_bytes: int = 1_048_576
39
+ max_integer_bits: int = 4096
40
+ max_canonical_bytes: int = 8_388_608
41
+
42
+ def __post_init__(self) -> None:
43
+ ceilings = {
44
+ "max_depth": 64,
45
+ "max_nodes": 50_000,
46
+ "max_container_items": 10_000,
47
+ "max_string_bytes": 1_048_576,
48
+ "max_integer_bits": 4096,
49
+ "max_canonical_bytes": 8_388_608,
50
+ }
51
+ for name, ceiling in ceilings.items():
52
+ value = getattr(self, name)
53
+ if type(value) is not int:
54
+ raise TypeError(f"{name} must be an exact integer")
55
+ if value <= 0 or value > ceiling:
56
+ raise ValueError(f"{name} must lie in [1, {ceiling}]")
57
+
58
+ def _validated_values(self) -> tuple[int, int, int, int, int, int]:
59
+ if type(self) is not TypedJsonLimits:
60
+ raise TypeError("limits must be an exact TypedJsonLimits value")
61
+ TypedJsonLimits.__post_init__(self)
62
+ return (
63
+ self.max_depth,
64
+ self.max_nodes,
65
+ self.max_container_items,
66
+ self.max_string_bytes,
67
+ self.max_integer_bits,
68
+ self.max_canonical_bytes,
69
+ )
70
+
71
+ def __eq__(self, other: object) -> bool:
72
+ if type(self) is not TypedJsonLimits or type(other) is not TypedJsonLimits:
73
+ return False
74
+ return self._validated_values() == other._validated_values()
75
+
76
+ def __hash__(self) -> int:
77
+ return hash((TypedJsonLimits, self._validated_values()))
78
+
79
+
80
+ DEFAULT_TYPED_JSON_LIMITS = TypedJsonLimits()
81
+
82
+
83
+ def validate_typed_json_limits(limits: TypedJsonLimits) -> None:
84
+ """Revalidate an exact limits value at every public trust boundary.
85
+
86
+ Frozen dataclasses prevent ordinary assignment, but callers may still hand
87
+ a domain service an instance that did not pass its generated constructor
88
+ (for example, a value reconstructed by a custom codec). Consumers must
89
+ therefore validate the complete value rather than relying on construction
90
+ having happened elsewhere.
91
+ """
92
+
93
+ if type(limits) is not TypedJsonLimits:
94
+ raise TypeError("limits must be an exact TypedJsonLimits value")
95
+ TypedJsonLimits.__post_init__(limits)
96
+
97
+
98
+ def _utf8_bytes(value: str, *, limits: TypedJsonLimits, name: str) -> bytes:
99
+ if type(value) is not str:
100
+ raise TypeError(f"{name} must be an exact string")
101
+ if len(value) > limits.max_string_bytes:
102
+ raise ValueError(f"{name} exceeds max_string_bytes")
103
+ try:
104
+ encoded = value.encode("utf-8", errors="strict")
105
+ except UnicodeEncodeError as exc:
106
+ raise ValueError(f"{name} is not valid UTF-8 text") from exc
107
+ if len(encoded) > limits.max_string_bytes:
108
+ raise ValueError(f"{name} exceeds max_string_bytes")
109
+ return encoded
110
+
111
+
112
+ def _is_scalar(value: object) -> bool:
113
+ return value is None or type(value) in (bool, int, float, str)
114
+
115
+
116
+ def _validate_scalar(value: object, *, limits: TypedJsonLimits) -> None:
117
+ value_type = type(value)
118
+ if value is None or value_type is bool:
119
+ return
120
+ if value_type is int:
121
+ if value.bit_length() > limits.max_integer_bits:
122
+ raise ValueError("integer exceeds max_integer_bits")
123
+ return
124
+ if value_type is float:
125
+ if not math.isfinite(value):
126
+ raise ValueError("floating-point values must be finite")
127
+ return
128
+ if value_type is str:
129
+ _utf8_bytes(value, limits=limits, name="string value")
130
+ return
131
+ raise TypeError("value is not an exact typed-JSON scalar")
132
+
133
+
134
+ @dataclass(frozen=True, slots=True, eq=False)
135
+ class FrozenJsonArray:
136
+ """An immutable typed-JSON array."""
137
+
138
+ items: tuple["FrozenJsonValue", ...]
139
+
140
+ def __post_init__(self) -> None:
141
+ if type(self.items) is not tuple:
142
+ raise TypeError("FrozenJsonArray.items must be an exact tuple")
143
+ _validate_frozen(self, limits=DEFAULT_TYPED_JSON_LIMITS)
144
+
145
+ def __eq__(self, other: object) -> bool:
146
+ if type(self) is not FrozenJsonArray or type(other) is not FrozenJsonArray:
147
+ return False
148
+ return typed_json_equal(self, other)
149
+
150
+ __hash__ = None
151
+
152
+
153
+ @dataclass(frozen=True, slots=True, eq=False)
154
+ class FrozenJsonObject:
155
+ """An immutable typed-JSON object in canonical UTF-8 key order."""
156
+
157
+ items: tuple[tuple[str, "FrozenJsonValue"], ...]
158
+
159
+ def __post_init__(self) -> None:
160
+ if type(self.items) is not tuple:
161
+ raise TypeError("FrozenJsonObject.items must be an exact tuple")
162
+ _validate_frozen(self, limits=DEFAULT_TYPED_JSON_LIMITS)
163
+
164
+ def __eq__(self, other: object) -> bool:
165
+ if type(self) is not FrozenJsonObject or type(other) is not FrozenJsonObject:
166
+ return False
167
+ return typed_json_equal(self, other)
168
+
169
+ __hash__ = None
170
+
171
+
172
+ FrozenJsonValue = Union[
173
+ None,
174
+ bool,
175
+ int,
176
+ float,
177
+ str,
178
+ FrozenJsonArray,
179
+ FrozenJsonObject,
180
+ ]
181
+
182
+
183
+ def is_frozen_json_value(value: object) -> bool:
184
+ """Return whether *value* has an exact frozen typed-JSON runtime type."""
185
+
186
+ return _is_scalar(value) or type(value) in (FrozenJsonArray, FrozenJsonObject)
187
+
188
+
189
+ @dataclass(slots=True)
190
+ class _ValidationState:
191
+ nodes: int = 0
192
+ text_bytes: int = 0
193
+ active_container_ids: set[int] = field(default_factory=set)
194
+
195
+
196
+ def _count_text_bytes(
197
+ state: _ValidationState | "_FreezeState",
198
+ encoded: bytes,
199
+ *,
200
+ limits: TypedJsonLimits,
201
+ ) -> None:
202
+ state.text_bytes += len(encoded)
203
+ if state.text_bytes > limits.max_canonical_bytes:
204
+ raise ValueError("typed-JSON text exceeds max_canonical_bytes")
205
+
206
+
207
+ def _validate_frozen(
208
+ value: FrozenJsonValue,
209
+ *,
210
+ limits: TypedJsonLimits,
211
+ depth: int = 0,
212
+ state: _ValidationState | None = None,
213
+ ) -> None:
214
+ if state is None:
215
+ # Limits are immutable and shared through the complete traversal. A
216
+ # public boundary validates them once; recursive nodes must not repeat
217
+ # the same six-field validation thousands of times.
218
+ validate_typed_json_limits(limits)
219
+ state = _ValidationState()
220
+ if depth > limits.max_depth:
221
+ raise ValueError("typed-JSON value exceeds max_depth")
222
+ state.nodes += 1
223
+ if state.nodes > limits.max_nodes:
224
+ raise ValueError("typed-JSON value exceeds max_nodes")
225
+
226
+ if _is_scalar(value):
227
+ _validate_scalar(value, limits=limits)
228
+ if type(value) is str:
229
+ _count_text_bytes(
230
+ state,
231
+ _utf8_bytes(value, limits=limits, name="string value"),
232
+ limits=limits,
233
+ )
234
+ return
235
+
236
+ if type(value) is FrozenJsonArray:
237
+ if type(value.items) is not tuple:
238
+ raise TypeError("FrozenJsonArray.items must be an exact tuple")
239
+ if len(value.items) > limits.max_container_items:
240
+ raise ValueError("array exceeds max_container_items")
241
+ identity = id(value)
242
+ if identity in state.active_container_ids:
243
+ raise ValueError("frozen typed-JSON values cannot contain cycles")
244
+ state.active_container_ids.add(identity)
245
+ try:
246
+ for item in value.items:
247
+ if not is_frozen_json_value(item):
248
+ raise TypeError(
249
+ "frozen arrays may contain only frozen typed-JSON values"
250
+ )
251
+ _validate_frozen(
252
+ item,
253
+ limits=limits,
254
+ depth=depth + 1,
255
+ state=state,
256
+ )
257
+ finally:
258
+ state.active_container_ids.remove(identity)
259
+ return
260
+
261
+ if type(value) is FrozenJsonObject:
262
+ if type(value.items) is not tuple:
263
+ raise TypeError("FrozenJsonObject.items must be an exact tuple")
264
+ if len(value.items) > limits.max_container_items:
265
+ raise ValueError("object exceeds max_container_items")
266
+ identity = id(value)
267
+ if identity in state.active_container_ids:
268
+ raise ValueError("frozen typed-JSON values cannot contain cycles")
269
+ state.active_container_ids.add(identity)
270
+ try:
271
+ previous_key_bytes: bytes | None = None
272
+ for entry in value.items:
273
+ if type(entry) is not tuple or len(entry) != 2:
274
+ raise TypeError(
275
+ "frozen object entries must be exact (key, value) tuples"
276
+ )
277
+ key, item = entry
278
+ key_bytes = _utf8_bytes(key, limits=limits, name="object key")
279
+ _count_text_bytes(state, key_bytes, limits=limits)
280
+ if previous_key_bytes is not None and key_bytes <= previous_key_bytes:
281
+ raise ValueError(
282
+ "frozen object keys must be unique and in canonical UTF-8 order"
283
+ )
284
+ previous_key_bytes = key_bytes
285
+ if not is_frozen_json_value(item):
286
+ raise TypeError(
287
+ "frozen objects may contain only frozen typed-JSON values"
288
+ )
289
+ _validate_frozen(
290
+ item,
291
+ limits=limits,
292
+ depth=depth + 1,
293
+ state=state,
294
+ )
295
+ finally:
296
+ state.active_container_ids.remove(identity)
297
+ return
298
+
299
+ raise TypeError("value is not a frozen typed-JSON value")
300
+
301
+
302
+ @dataclass(slots=True)
303
+ class _FreezeState:
304
+ active_container_ids: set[int]
305
+ nodes: int = 0
306
+ text_bytes: int = 0
307
+
308
+
309
+ def freeze_json(
310
+ value: object,
311
+ *,
312
+ limits: TypedJsonLimits = DEFAULT_TYPED_JSON_LIMITS,
313
+ ) -> FrozenJsonValue:
314
+ """Validate and deeply freeze an exact built-in JSON-like value.
315
+
316
+ ``dict`` and ``list`` subclasses are rejected even if they appear benign:
317
+ their iteration and indexing methods are executable behavior. Tuples are
318
+ not accepted as raw arrays; tuples are reserved for the immutable internal
319
+ representation.
320
+ """
321
+
322
+ validate_typed_json_limits(limits)
323
+ if is_frozen_json_value(value):
324
+ _validate_frozen(value, limits=limits)
325
+ return value
326
+
327
+ state = _FreezeState(active_container_ids=set())
328
+
329
+ def visit(current: object, depth: int) -> FrozenJsonValue:
330
+ if depth > limits.max_depth:
331
+ raise ValueError("typed-JSON value exceeds max_depth")
332
+ state.nodes += 1
333
+ if state.nodes > limits.max_nodes:
334
+ raise ValueError("typed-JSON value exceeds max_nodes")
335
+
336
+ if _is_scalar(current):
337
+ _validate_scalar(current, limits=limits)
338
+ if type(current) is str:
339
+ _count_text_bytes(
340
+ state,
341
+ _utf8_bytes(current, limits=limits, name="string value"),
342
+ limits=limits,
343
+ )
344
+ return current # type: ignore[return-value]
345
+
346
+ current_type = type(current)
347
+ if current_type not in (dict, list):
348
+ raise TypeError(
349
+ "typed-JSON values require exact dict/list containers and exact scalars"
350
+ )
351
+ if len(current) > limits.max_container_items:
352
+ raise ValueError("container exceeds max_container_items")
353
+ identity = id(current)
354
+ if identity in state.active_container_ids:
355
+ raise ValueError("typed-JSON values cannot contain cycles")
356
+ state.active_container_ids.add(identity)
357
+ try:
358
+ if current_type is list:
359
+ frozen_items = tuple(visit(item, depth + 1) for item in current)
360
+ # The raw tree has been checked incrementally and the complete
361
+ # frozen root is checked once below. Calling the public
362
+ # dataclass constructor here would recursively revalidate each
363
+ # just-built child subtree at every ancestor.
364
+ frozen_array = object.__new__(FrozenJsonArray)
365
+ object.__setattr__(frozen_array, "items", frozen_items)
366
+ return frozen_array
367
+
368
+ encoded_entries: list[tuple[bytes, str, FrozenJsonValue]] = []
369
+ for key, item in current.items():
370
+ key_bytes = _utf8_bytes(key, limits=limits, name="object key")
371
+ _count_text_bytes(state, key_bytes, limits=limits)
372
+ encoded_entries.append((key_bytes, key, visit(item, depth + 1)))
373
+ encoded_entries.sort(key=lambda entry: entry[0])
374
+ frozen_object = object.__new__(FrozenJsonObject)
375
+ object.__setattr__(
376
+ frozen_object,
377
+ "items",
378
+ tuple((key, item) for _, key, item in encoded_entries),
379
+ )
380
+ return frozen_object
381
+ finally:
382
+ state.active_container_ids.remove(identity)
383
+
384
+ frozen = visit(value, 0)
385
+ _validate_frozen(frozen, limits=limits)
386
+ return frozen
387
+
388
+
389
+ def thaw_json(value: FrozenJsonValue) -> object:
390
+ """Return a fresh exact ``dict``/``list`` representation."""
391
+
392
+ _validate_frozen(value, limits=DEFAULT_TYPED_JSON_LIMITS)
393
+
394
+ def visit(current: FrozenJsonValue) -> object:
395
+ # The complete immutable graph was validated above. Recursing through
396
+ # this private visitor preserves that one trust-boundary check without
397
+ # revalidating every descendant subtree before thawing it.
398
+ if _is_scalar(current):
399
+ return current
400
+ if type(current) is FrozenJsonArray:
401
+ return [visit(item) for item in current.items]
402
+ if type(current) is FrozenJsonObject:
403
+ return {key: visit(item) for key, item in current.items}
404
+ raise AssertionError("validated frozen value had an impossible type")
405
+
406
+ return visit(value)
407
+
408
+
409
+ def _uint64(value: int) -> bytes:
410
+ if type(value) is not int or value < 0 or value > _UINT64_MAX:
411
+ raise ValueError("canonical length is outside uint64 range")
412
+ return value.to_bytes(8, "big", signed=False)
413
+
414
+
415
+ @dataclass(slots=True)
416
+ class _CanonicalWriter:
417
+ limit: int
418
+ chunks: list[bytes]
419
+ size: int = 0
420
+
421
+ def add(self, value: bytes) -> None:
422
+ if type(value) is not bytes:
423
+ raise TypeError("canonical chunks must be exact bytes")
424
+ self.size += len(value)
425
+ if self.size > self.limit:
426
+ raise ValueError("typed-JSON canonical form exceeds max_canonical_bytes")
427
+ self.chunks.append(value)
428
+
429
+
430
+ def canonical_typed_json_bytes(
431
+ value: object,
432
+ *,
433
+ limits: TypedJsonLimits = DEFAULT_TYPED_JSON_LIMITS,
434
+ ) -> bytes:
435
+ """Encode *value* in the version-1 type-sensitive canonical form."""
436
+
437
+ frozen = freeze_json(value, limits=limits)
438
+ writer = _CanonicalWriter(limits.max_canonical_bytes, [])
439
+
440
+ def encode(current: FrozenJsonValue) -> None:
441
+ if current is None:
442
+ writer.add(b"n")
443
+ elif type(current) is bool:
444
+ writer.add(b"b1" if current else b"b0")
445
+ elif type(current) is int:
446
+ integer_bytes = str(current).encode("ascii", errors="strict")
447
+ writer.add(b"i")
448
+ writer.add(_uint64(len(integer_bytes)))
449
+ writer.add(integer_bytes)
450
+ elif type(current) is float:
451
+ writer.add(b"f")
452
+ writer.add(struct.pack(">d", current))
453
+ elif type(current) is str:
454
+ string_bytes = _utf8_bytes(current, limits=limits, name="string value")
455
+ writer.add(b"s")
456
+ writer.add(_uint64(len(string_bytes)))
457
+ writer.add(string_bytes)
458
+ elif type(current) is FrozenJsonArray:
459
+ writer.add(b"a")
460
+ writer.add(_uint64(len(current.items)))
461
+ for item in current.items:
462
+ encode(item)
463
+ elif type(current) is FrozenJsonObject:
464
+ writer.add(b"o")
465
+ writer.add(_uint64(len(current.items)))
466
+ for key, item in current.items:
467
+ key_bytes = _utf8_bytes(key, limits=limits, name="object key")
468
+ writer.add(_uint64(len(key_bytes)))
469
+ writer.add(key_bytes)
470
+ encode(item)
471
+ else: # pragma: no cover - freeze_json closes the union.
472
+ raise AssertionError("unsupported frozen typed-JSON value")
473
+
474
+ encode(frozen)
475
+ return b"".join(writer.chunks)
476
+
477
+
478
+ def typed_json_sha256(
479
+ value: object,
480
+ *,
481
+ limits: TypedJsonLimits = DEFAULT_TYPED_JSON_LIMITS,
482
+ ) -> str:
483
+ """Hash the exact typed tree with an explicit versioned domain tag."""
484
+
485
+ payload = canonical_typed_json_bytes(value, limits=limits)
486
+ return _typed_json_sha256_canonical_bytes(payload)
487
+
488
+
489
+ def _typed_json_sha256_canonical_bytes(payload: bytes) -> str:
490
+ """Hash canonical bytes already produced by this module's encoder.
491
+
492
+ This is an internal de-duplication seam for trusted domain validators that
493
+ need both canonical bytes and their typed-JSON digest. Public callers must
494
+ continue through :func:`typed_json_sha256`, which validates and encodes.
495
+ """
496
+
497
+ if type(payload) is not bytes:
498
+ raise TypeError("canonical typed-JSON payload must be exact bytes")
499
+ digest = hashlib.sha256()
500
+ digest.update(_TYPED_JSON_HASH_DOMAIN)
501
+ digest.update(payload)
502
+ return digest.hexdigest()
503
+
504
+
505
+ def typed_json_equal(
506
+ left: object,
507
+ right: object,
508
+ *,
509
+ limits: TypedJsonLimits = DEFAULT_TYPED_JSON_LIMITS,
510
+ ) -> bool:
511
+ """Type-sensitive structural equality for validated values."""
512
+
513
+ return canonical_typed_json_bytes(left, limits=limits) == canonical_typed_json_bytes(
514
+ right,
515
+ limits=limits,
516
+ )
517
+
518
+
519
+ def is_json_scalar(value: object) -> bool:
520
+ """Validate the exact scalar type without accepting numeric subclasses."""
521
+
522
+ if not _is_scalar(value):
523
+ return False
524
+ _validate_scalar(value, limits=DEFAULT_TYPED_JSON_LIMITS)
525
+ return True
526
+
527
+
528
+ __all__ = [
529
+ "DEFAULT_TYPED_JSON_LIMITS",
530
+ "FrozenJsonArray",
531
+ "FrozenJsonObject",
532
+ "FrozenJsonValue",
533
+ "TypedJsonLimits",
534
+ "canonical_typed_json_bytes",
535
+ "freeze_json",
536
+ "is_frozen_json_value",
537
+ "is_json_scalar",
538
+ "thaw_json",
539
+ "typed_json_equal",
540
+ "typed_json_sha256",
541
+ "validate_typed_json_limits",
542
+ ]
@@ -0,0 +1,158 @@
1
+ """Immutable option identities for finite atomic variation spaces.
2
+
3
+ An :class:`AtomicEditOption` is deliberately more than a provider-facing
4
+ replacement value. It binds that value to one typed path, one variation
5
+ family, and exact source metadata so observations cannot silently migrate
6
+ between coordinates or catalog revisions.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import hashlib
12
+ import re
13
+ from dataclasses import dataclass
14
+
15
+ from agent_evolve.domain.patch import (
16
+ JsonPath,
17
+ canonical_path_bytes,
18
+ validate_json_path,
19
+ )
20
+ from agent_evolve.domain.typed_json import (
21
+ FrozenJsonValue,
22
+ canonical_typed_json_bytes,
23
+ freeze_json,
24
+ is_json_scalar,
25
+ )
26
+
27
+
28
+ _OPTION_ID = re.compile(r"^[a-z][a-z0-9_.-]{0,255}$")
29
+ _FAMILY = re.compile(r"^[a-z][a-z0-9_.-]{0,95}$")
30
+ _METADATA_KEY = re.compile(r"^[a-z][a-z0-9_.-]{0,95}$")
31
+ _MAX_METADATA_ITEMS = 64
32
+ _MAX_METADATA_VALUE_BYTES = 16_384
33
+ _ATOMIC_EDIT_OPTION_DOMAIN = b"agent-evolve:atomic-edit-option:v1\x00"
34
+
35
+
36
+ def _utf8(value: str, *, name: str, max_bytes: int) -> bytes:
37
+ if type(value) is not str:
38
+ raise TypeError(f"{name} must be an exact string")
39
+ try:
40
+ encoded = value.encode("utf-8", errors="strict")
41
+ except UnicodeEncodeError as exc:
42
+ raise ValueError(f"{name} must be strict UTF-8") from exc
43
+ if len(encoded) > max_bytes:
44
+ raise ValueError(f"{name} exceeds its byte limit")
45
+ return encoded
46
+
47
+
48
+ def _frame(payload: bytes) -> bytes:
49
+ if type(payload) is not bytes:
50
+ raise TypeError("framed payloads must be exact bytes")
51
+ return len(payload).to_bytes(8, "big", signed=False) + payload
52
+
53
+
54
+ @dataclass(frozen=True, slots=True, eq=False)
55
+ class AtomicEditOption:
56
+ """One closed, source-bound scalar replacement at one exact JSON path.
57
+
58
+ ``metadata`` is canonical: keys are unique and lexicographically ordered.
59
+ This makes the complete option identity independent of mapping iteration
60
+ order while retaining a compact tuple representation at the domain edge.
61
+ """
62
+
63
+ option_id: str
64
+ path: JsonPath
65
+ replacement: FrozenJsonValue
66
+ family: str
67
+ metadata: tuple[tuple[str, str], ...] = ()
68
+
69
+ def __post_init__(self) -> None:
70
+ if type(self.option_id) is not str or _OPTION_ID.fullmatch(
71
+ self.option_id
72
+ ) is None:
73
+ raise ValueError(
74
+ "option_id must use the closed lowercase identifier grammar"
75
+ )
76
+ if type(self.path) is not JsonPath:
77
+ raise TypeError("path must be an exact JsonPath")
78
+ validate_json_path(self.path)
79
+ if not self.path.segments:
80
+ raise ValueError("an atomic edit option cannot replace the root")
81
+ if freeze_json(self.replacement) is not self.replacement:
82
+ raise TypeError("replacement must already be frozen typed JSON")
83
+ if not is_json_scalar(self.replacement):
84
+ raise TypeError("replacement must be a typed-JSON scalar")
85
+ if type(self.family) is not str or _FAMILY.fullmatch(self.family) is None:
86
+ raise ValueError("family must use the closed lowercase token grammar")
87
+ if type(self.metadata) is not tuple:
88
+ raise TypeError("metadata must be an exact tuple")
89
+ if len(self.metadata) > _MAX_METADATA_ITEMS:
90
+ raise ValueError("metadata exceeds the item limit")
91
+
92
+ keys: list[str] = []
93
+ for item in self.metadata:
94
+ if type(item) is not tuple or len(item) != 2:
95
+ raise TypeError("metadata entries must be exact key/value tuples")
96
+ key, value = item
97
+ if type(key) is not str or _METADATA_KEY.fullmatch(key) is None:
98
+ raise ValueError(
99
+ "metadata keys must use the closed lowercase token grammar"
100
+ )
101
+ _utf8(
102
+ value,
103
+ name=f"metadata value for {key!r}",
104
+ max_bytes=_MAX_METADATA_VALUE_BYTES,
105
+ )
106
+ keys.append(key)
107
+ if keys != sorted(keys) or len(set(keys)) != len(keys):
108
+ raise ValueError("metadata keys must be unique and canonically sorted")
109
+
110
+ @property
111
+ def identity_sha256(self) -> str:
112
+ """Return a type-sensitive digest binding every semantic field."""
113
+
114
+ validate_atomic_edit_option(self)
115
+ digest = hashlib.sha256()
116
+ digest.update(_ATOMIC_EDIT_OPTION_DOMAIN)
117
+ digest.update(_frame(self.option_id.encode("ascii")))
118
+ digest.update(_frame(canonical_path_bytes(self.path)))
119
+ digest.update(_frame(canonical_typed_json_bytes(self.replacement)))
120
+ digest.update(_frame(self.family.encode("ascii")))
121
+ digest.update(len(self.metadata).to_bytes(8, "big", signed=False))
122
+ for key, value in self.metadata:
123
+ digest.update(_frame(key.encode("ascii")))
124
+ digest.update(_frame(value.encode("utf-8", errors="strict")))
125
+ return digest.hexdigest()
126
+
127
+ def _validated_values(
128
+ self,
129
+ ) -> tuple[str, bytes, bytes, str, tuple[tuple[str, str], ...]]:
130
+ if type(self) is not AtomicEditOption:
131
+ raise TypeError("option must be an exact AtomicEditOption")
132
+ AtomicEditOption.__post_init__(self)
133
+ return (
134
+ self.option_id,
135
+ canonical_path_bytes(self.path),
136
+ canonical_typed_json_bytes(self.replacement),
137
+ self.family,
138
+ self.metadata,
139
+ )
140
+
141
+ def __eq__(self, other: object) -> bool:
142
+ if type(self) is not AtomicEditOption or type(other) is not AtomicEditOption:
143
+ return False
144
+ return self._validated_values() == other._validated_values()
145
+
146
+ def __hash__(self) -> int:
147
+ return hash((AtomicEditOption, self._validated_values()))
148
+
149
+
150
+ def validate_atomic_edit_option(option: AtomicEditOption) -> None:
151
+ """Revalidate an exact option at a public trust boundary."""
152
+
153
+ if type(option) is not AtomicEditOption:
154
+ raise TypeError("option must be an exact AtomicEditOption")
155
+ AtomicEditOption.__post_init__(option)
156
+
157
+
158
+ __all__ = ["AtomicEditOption", "validate_atomic_edit_option"]