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.
- agent_evolve/__init__.py +722 -0
- agent_evolve/agentic.py +2800 -0
- agent_evolve/api.py +767 -0
- agent_evolve/application/__init__.py +1580 -0
- agent_evolve/application/action_allocation.py +744 -0
- agent_evolve/application/action_allocation_frame.py +347 -0
- agent_evolve/application/action_allocation_frame_commit.py +185 -0
- agent_evolve/application/action_allocation_frame_commit_v3.py +184 -0
- agent_evolve/application/action_allocation_frame_v3.py +338 -0
- agent_evolve/application/action_archive_value.py +497 -0
- agent_evolve/application/action_evidence_consistency.py +455 -0
- agent_evolve/application/action_forecast_partitioning.py +1471 -0
- agent_evolve/application/action_metric_projection.py +211 -0
- agent_evolve/application/action_role_value.py +680 -0
- agent_evolve/application/action_score_authorities.py +363 -0
- agent_evolve/application/action_structural_signature.py +116 -0
- agent_evolve/application/action_target_realization.py +402 -0
- agent_evolve/application/agentic_evolution.py +7734 -0
- agent_evolve/application/agentic_portfolio_residual_expert.py +835 -0
- agent_evolve/application/anchor_residual_identification.py +463 -0
- agent_evolve/application/archive_conditioned_action_target.py +208 -0
- agent_evolve/application/artifact_journal.py +246 -0
- agent_evolve/application/artifact_replay.py +347 -0
- agent_evolve/application/budgeted_optimizer.py +1828 -0
- agent_evolve/application/calibrated_campaign.py +485 -0
- agent_evolve/application/calibrated_current_prefix_forecast_opportunity.py +322 -0
- agent_evolve/application/calibrated_positive_gain_opportunity.py +1581 -0
- agent_evolve/application/campaign_capacity_recourse.py +254 -0
- agent_evolve/application/campaign_contextual_outcomes.py +119 -0
- agent_evolve/application/campaign_diagnostic_blocks.py +930 -0
- agent_evolve/application/campaign_evidence_registry.py +262 -0
- agent_evolve/application/campaign_execution.py +2537 -0
- agent_evolve/application/campaign_generation_audit.py +942 -0
- agent_evolve/application/campaign_learning.py +1812 -0
- agent_evolve/application/campaign_learning_runtime.py +1977 -0
- agent_evolve/application/campaign_search_phase.py +227 -0
- agent_evolve/application/campaign_selector_context_extension.py +220 -0
- agent_evolve/application/campaign_variation_envelope.py +649 -0
- agent_evolve/application/campaign_variation_trace.py +451 -0
- agent_evolve/application/candidate_archive_consequence.py +128 -0
- agent_evolve/application/causal_opportunity_portfolio_gate.py +385 -0
- agent_evolve/application/composite_outcome_updater.py +145 -0
- agent_evolve/application/composition_portfolio_selection.py +363 -0
- agent_evolve/application/concurrent_stage.py +144 -0
- agent_evolve/application/contextual_action_allocation.py +181 -0
- agent_evolve/application/contextual_campaign_outcomes.py +267 -0
- agent_evolve/application/contextual_campaign_planning.py +1366 -0
- agent_evolve/application/contextual_delayed_credit.py +651 -0
- agent_evolve/application/contextual_search_controller.py +2374 -0
- agent_evolve/application/current_prefix_forecast_opportunity.py +714 -0
- agent_evolve/application/decision_metric_projection.py +112 -0
- agent_evolve/application/derived_action_semantics.py +129 -0
- agent_evolve/application/detailed_evaluation.py +449 -0
- agent_evolve/application/earned_lineage.py +1011 -0
- agent_evolve/application/effective_choice_audit.py +484 -0
- agent_evolve/application/empirical_consequence_calibration.py +908 -0
- agent_evolve/application/evaluation_accounting.py +325 -0
- agent_evolve/application/evaluation_cache.py +199 -0
- agent_evolve/application/evaluation_escrow.py +547 -0
- agent_evolve/application/evaluation_recourse.py +253 -0
- agent_evolve/application/event_recorder.py +151 -0
- agent_evolve/application/evolution_campaign.py +1840 -0
- agent_evolve/application/executable_hypothesis.py +323 -0
- agent_evolve/application/factorial_branch_pilot.py +772 -0
- agent_evolve/application/finite_acquisition_capacity_recourse.py +672 -0
- agent_evolve/application/finite_acquisition_residual_expert.py +373 -0
- agent_evolve/application/finite_acquisition_variation_envelope.py +802 -0
- agent_evolve/application/finite_action_hypothesis_semantics.py +446 -0
- agent_evolve/application/finite_action_selection.py +188 -0
- agent_evolve/application/finite_action_set.py +306 -0
- agent_evolve/application/finite_action_transition.py +537 -0
- agent_evolve/application/finite_variation_eligibility.py +296 -0
- agent_evolve/application/forecast_geometry_portfolio.py +799 -0
- agent_evolve/application/forecast_opportunity_shadow_calibration.py +316 -0
- agent_evolve/application/front_proximity_admission.py +311 -0
- agent_evolve/application/front_proximity_parent_basis.py +458 -0
- agent_evolve/application/frozen_hurdle_score.py +659 -0
- agent_evolve/application/g3_causal_screen.py +2257 -0
- agent_evolve/application/g3_causal_validation.py +1046 -0
- agent_evolve/application/g3_postseal_curation.py +818 -0
- agent_evolve/application/gated_agentic_generator.py +205 -0
- agent_evolve/application/generation_feedback.py +293 -0
- agent_evolve/application/generative_proposal_journal.py +185 -0
- agent_evolve/application/geometry_conditional_elasticity.py +453 -0
- agent_evolve/application/global_wave_action_allocation.py +1151 -0
- agent_evolve/application/head_mass_conditional_seat.py +268 -0
- agent_evolve/application/identifiable_reflection_evidence.py +1147 -0
- agent_evolve/application/identifiable_reflection_learning.py +395 -0
- agent_evolve/application/identifiable_reflection_request.py +364 -0
- agent_evolve/application/in_memory_residual_archive.py +341 -0
- agent_evolve/application/insight_memory.py +1804 -0
- agent_evolve/application/live_runtime_manifest.py +758 -0
- agent_evolve/application/llm_task_queue.py +769 -0
- agent_evolve/application/matched_finite_action_block.py +409 -0
- agent_evolve/application/materialized_action_broker.py +2328 -0
- agent_evolve/application/materialized_action_constraints.py +83 -0
- agent_evolve/application/materialized_variation.py +211 -0
- agent_evolve/application/multi_option_evolution.py +1536 -0
- agent_evolve/application/outcome_adaptive_action_racing.py +2827 -0
- agent_evolve/application/outcome_adaptive_residual_campaign_runtime.py +580 -0
- agent_evolve/application/outcome_adaptive_residual_portfolio_evolution.py +3671 -0
- agent_evolve/application/outcome_conditioned_portfolio_selection.py +1374 -0
- agent_evolve/application/outcome_relation.py +193 -0
- agent_evolve/application/paired_allocation_comparison.py +241 -0
- agent_evolve/application/paired_block_schedule.py +127 -0
- agent_evolve/application/parent_measurement.py +226 -0
- agent_evolve/application/pareto_archive.py +811 -0
- agent_evolve/application/portfolio_campaign_runtime.py +4739 -0
- agent_evolve/application/portfolio_evolution.py +2950 -0
- agent_evolve/application/portfolio_hypothesis_observations.py +814 -0
- agent_evolve/application/portfolio_memory_attribution.py +581 -0
- agent_evolve/application/portfolio_memory_dose.py +788 -0
- agent_evolve/application/portfolio_memory_matched_control.py +938 -0
- agent_evolve/application/portfolio_memory_transfer.py +297 -0
- agent_evolve/application/portfolio_optimization_memory.py +363 -0
- agent_evolve/application/portfolio_outcome_feedback.py +1613 -0
- agent_evolve/application/portfolio_projection.py +335 -0
- agent_evolve/application/portfolio_recombination.py +2032 -0
- agent_evolve/application/post_evolution_reflection.py +834 -0
- agent_evolve/application/postcommit_rank_authority.py +245 -0
- agent_evolve/application/precommitted_portfolio_racing.py +2762 -0
- agent_evolve/application/prequential_archive_opportunity_calibration.py +1154 -0
- agent_evolve/application/prequential_residual_exploration.py +343 -0
- agent_evolve/application/prequential_score_portfolio.py +954 -0
- agent_evolve/application/projections.py +292 -0
- agent_evolve/application/protected_action_committee.py +1027 -0
- agent_evolve/application/protected_branch_pilot.py +376 -0
- agent_evolve/application/protected_current_prefix_forecast_opportunity.py +552 -0
- agent_evolve/application/provider_replay.py +910 -0
- agent_evolve/application/rank_balanced_causal_pilot.py +1372 -0
- agent_evolve/application/recombination_residual_expert.py +403 -0
- agent_evolve/application/reflection_workflow.py +571 -0
- agent_evolve/application/region_conditional_credit.py +911 -0
- agent_evolve/application/residual_campaign_runtime.py +531 -0
- agent_evolve/application/residual_headroom_campaign_runtime.py +459 -0
- agent_evolve/application/residual_headroom_ledger.py +1544 -0
- agent_evolve/application/residual_learning_transaction.py +396 -0
- agent_evolve/application/residual_portfolio_evolution.py +1228 -0
- agent_evolve/application/residual_reachability.py +749 -0
- agent_evolve/application/residual_stage_credit.py +499 -0
- agent_evolve/application/same_prefix_paired_audit.py +1580 -0
- agent_evolve/application/semantic_coverage_score_portfolio.py +838 -0
- agent_evolve/application/sequential_lineage_allocation.py +1017 -0
- agent_evolve/application/sequential_market_replay.py +1395 -0
- agent_evolve/application/sequential_residual_campaign_runtime.py +305 -0
- agent_evolve/application/sequential_residual_portfolio_evolution.py +940 -0
- agent_evolve/application/single_score_action_allocation.py +299 -0
- agent_evolve/application/source_exposure_allocation.py +906 -0
- agent_evolve/application/staged_memory.py +210 -0
- agent_evolve/application/stratified_cold_start_allocation.py +732 -0
- agent_evolve/application/support_guarded_hurdle_score.py +549 -0
- agent_evolve/application/target_conditioned_action_forecast.py +595 -0
- agent_evolve/application/target_conditioned_campaign.py +566 -0
- agent_evolve/application/treatment_assignment.py +201 -0
- agent_evolve/application/trusted_objective_evidence.py +217 -0
- agent_evolve/application/two_stage_action_evolution.py +1131 -0
- agent_evolve/application/v8lite_allocation_policy.py +1083 -0
- agent_evolve/application/v9_candidate_policy.py +1303 -0
- agent_evolve/bootstrap.py +108 -0
- agent_evolve/campaign_presets.py +517 -0
- agent_evolve/campaign_profiles.py +452 -0
- agent_evolve/campaign_variation_topology.py +288 -0
- agent_evolve/campaign_workload.py +950 -0
- agent_evolve/cli.py +797 -0
- agent_evolve/contract.py +241 -0
- agent_evolve/core/__init__.py +91 -0
- agent_evolve/core/action_semantics.py +411 -0
- agent_evolve/core/authored.py +105 -0
- agent_evolve/core/formatting.py +286 -0
- agent_evolve/core/optimization_semantics.py +324 -0
- agent_evolve/core/problem.py +167 -0
- agent_evolve/core/results.py +323 -0
- agent_evolve/core/stats.py +70 -0
- agent_evolve/core/telemetry.py +100 -0
- agent_evolve/domain/__init__.py +89 -0
- agent_evolve/domain/artifact.py +162 -0
- agent_evolve/domain/durable_text.py +68 -0
- agent_evolve/domain/event.py +1454 -0
- agent_evolve/domain/finite_action_set.py +426 -0
- agent_evolve/domain/finite_variation.py +526 -0
- agent_evolve/domain/generative_emission.py +559 -0
- agent_evolve/domain/ids.py +163 -0
- agent_evolve/domain/inline_text.py +106 -0
- agent_evolve/domain/insight.py +27 -0
- agent_evolve/domain/lineage.py +737 -0
- agent_evolve/domain/llm_task_queue.py +960 -0
- agent_evolve/domain/outcome.py +96 -0
- agent_evolve/domain/patch.py +854 -0
- agent_evolve/domain/typed_json.py +542 -0
- agent_evolve/domain/variation_space.py +158 -0
- agent_evolve/driver.py +1014 -0
- agent_evolve/harness/__init__.py +29 -0
- agent_evolve/harness/base.py +242 -0
- agent_evolve/harness/directives.py +163 -0
- agent_evolve/harness/generative_seal.py +479 -0
- agent_evolve/harness/registry.py +41 -0
- agent_evolve/infrastructure/__init__.py +39 -0
- agent_evolve/infrastructure/artifacts/__init__.py +6 -0
- agent_evolve/infrastructure/artifacts/_verification.py +67 -0
- agent_evolve/infrastructure/artifacts/filesystem.py +343 -0
- agent_evolve/infrastructure/artifacts/in_memory.py +73 -0
- agent_evolve/infrastructure/asyncio_runtime.py +109 -0
- agent_evolve/infrastructure/authored_runtime.py +188 -0
- agent_evolve/infrastructure/authored_worker.py +171 -0
- agent_evolve/infrastructure/clock.py +53 -0
- agent_evolve/infrastructure/events/__init__.py +6 -0
- agent_evolve/infrastructure/events/_validation.py +89 -0
- agent_evolve/infrastructure/events/in_memory.py +56 -0
- agent_evolve/infrastructure/events/jsonl.py +193 -0
- agent_evolve/infrastructure/exception_provenance.py +215 -0
- agent_evolve/infrastructure/ids.py +118 -0
- agent_evolve/infrastructure/lineage_codec.py +1836 -0
- agent_evolve/infrastructure/outcome_adaptive_phase_journal.py +170 -0
- agent_evolve/infrastructure/residual_headroom_journal.py +221 -0
- agent_evolve/infrastructure/resource_lease.py +370 -0
- agent_evolve/infrastructure/sanitization/__init__.py +8 -0
- agent_evolve/infrastructure/sanitization/strict_json.py +484 -0
- agent_evolve/infrastructure/sequential_phase_journal.py +170 -0
- agent_evolve/infrastructure/stream_liveness.py +383 -0
- agent_evolve/infrastructure/subprocess_boundary.py +136 -0
- agent_evolve/integrations/__init__.py +1 -0
- agent_evolve/integrations/botorch/__init__.py +28 -0
- agent_evolve/integrations/botorch/finite_qlognehvi.py +190 -0
- agent_evolve/integrations/botorch/finite_qlognehvi_batch.py +155 -0
- agent_evolve/integrations/botorch/finite_qlognehvi_batch_identity.py +20 -0
- agent_evolve/integrations/botorch/finite_qlognehvi_batch_worker.py +55 -0
- agent_evolve/integrations/botorch/finite_qlognehvi_identity.py +22 -0
- agent_evolve/integrations/botorch/finite_qlognehvi_worker.py +55 -0
- agent_evolve/integrations/botorch/subprocess_qlognehvi.py +261 -0
- agent_evolve/integrations/botorch/subprocess_qlognehvi_batch.py +273 -0
- agent_evolve/integrations/completion.py +242 -0
- agent_evolve/integrations/pydantic_ai/__init__.py +441 -0
- agent_evolve/integrations/pydantic_ai/action_forecast.py +1068 -0
- agent_evolve/integrations/pydantic_ai/agentic_generator.py +2308 -0
- agent_evolve/integrations/pydantic_ai/async_generator.py +1604 -0
- agent_evolve/integrations/pydantic_ai/boundary_codec.py +1526 -0
- agent_evolve/integrations/pydantic_ai/calibrated_portfolio_campaign.py +756 -0
- agent_evolve/integrations/pydantic_ai/calibrated_portfolio_selection.py +7537 -0
- agent_evolve/integrations/pydantic_ai/campaign_acquisition.py +609 -0
- agent_evolve/integrations/pydantic_ai/execution_binding.py +138 -0
- agent_evolve/integrations/pydantic_ai/forecast_geometry_action_committee.py +217 -0
- agent_evolve/integrations/pydantic_ai/harness.py +159 -0
- agent_evolve/integrations/pydantic_ai/heterogeneous_model_execution.py +306 -0
- agent_evolve/integrations/pydantic_ai/hierarchical_residual_adaptive_semantic_view.py +179 -0
- agent_evolve/integrations/pydantic_ai/json_schema_dialect.py +108 -0
- agent_evolve/integrations/pydantic_ai/materialized_hierarchical_residual_expert.py +952 -0
- agent_evolve/integrations/pydantic_ai/materialized_portfolio_judge.py +520 -0
- agent_evolve/integrations/pydantic_ai/model_execution_profile.py +659 -0
- agent_evolve/integrations/pydantic_ai/outbound_request_manifest.py +1170 -0
- agent_evolve/integrations/pydantic_ai/portable_residual_consequence_features.py +575 -0
- agent_evolve/integrations/pydantic_ai/portfolio_selection.py +422 -0
- agent_evolve/integrations/pydantic_ai/progress_aware_openrouter.py +416 -0
- agent_evolve/integrations/pydantic_ai/provider_attempt_join.py +1523 -0
- agent_evolve/integrations/pydantic_ai/provider_free_calibrated_runner.py +607 -0
- agent_evolve/integrations/pydantic_ai/queued_runner.py +2634 -0
- agent_evolve/integrations/pydantic_ai/reconciled_residual_reachability.py +1417 -0
- agent_evolve/integrations/pydantic_ai/residual_forecast_geometry.py +445 -0
- agent_evolve/integrations/pydantic_ai/residual_reachability.py +674 -0
- agent_evolve/integrations/pydantic_ai/residual_semantic_cells.py +239 -0
- agent_evolve/integrations/pydantic_ai/sealed_output_replay.py +1068 -0
- agent_evolve/integrations/pydantic_ai/semantic_coverage_residual_portfolio.py +770 -0
- agent_evolve/integrations/pydantic_ai/semantic_decision_replay.py +383 -0
- agent_evolve/integrations/pydantic_ai/support_adaptive_residual_portfolio.py +135 -0
- agent_evolve/integrations/pydantic_ai/trusted_residual_prompt_context.py +143 -0
- agent_evolve/integrations/pydantic_ai/validated_openrouter_model.py +107 -0
- agent_evolve/integrations/pymoo_adapter.py +242 -0
- agent_evolve/policies/__init__.py +17 -0
- agent_evolve/policies/check.py +469 -0
- agent_evolve/policies/emit_scaffold.py +451 -0
- agent_evolve/policies/feedback/__init__.py +37 -0
- agent_evolve/policies/feedback/held_out_asn.py +1325 -0
- agent_evolve/policies/genetic.py +607 -0
- agent_evolve/policies/llm_backoff.py +183 -0
- agent_evolve/policies/llm_chooser.py +226 -0
- agent_evolve/policies/llm_generator.py +1760 -0
- agent_evolve/policies/llm_init.py +267 -0
- agent_evolve/policies/llm_operator.py +109 -0
- agent_evolve/policies/llm_prior.py +194 -0
- agent_evolve/policies/llm_surrogate.py +334 -0
- agent_evolve/policies/measurement_evidence.py +704 -0
- agent_evolve/policies/memory/__init__.py +223 -0
- agent_evolve/policies/memory/balanced_subset_blocks.py +707 -0
- agent_evolve/policies/memory/compatibility_matching.py +593 -0
- agent_evolve/policies/memory/global_falsification.py +1841 -0
- agent_evolve/policies/memory/prompt_shape.py +503 -0
- agent_evolve/policies/memory/randomized_subset.py +714 -0
- agent_evolve/policies/memory/staged_causal.py +1270 -0
- agent_evolve/policies/memory/treatment_compliance.py +759 -0
- agent_evolve/policies/objective_resolution/__init__.py +17 -0
- agent_evolve/policies/objective_resolution/fixed_grid.py +364 -0
- agent_evolve/policies/operator_portfolio.py +407 -0
- agent_evolve/policies/reguidance.py +1133 -0
- agent_evolve/policies/reward/__init__.py +83 -0
- agent_evolve/policies/reward/affine_candidate_consequence.py +156 -0
- agent_evolve/policies/reward/affine_candidate_consequence_3d.py +159 -0
- agent_evolve/policies/reward/affine_hypervolume.py +490 -0
- agent_evolve/policies/reward/affine_hypervolume_3d.py +567 -0
- agent_evolve/policies/reward/contextual_marginal_utility.py +318 -0
- agent_evolve/policies/reward/frozen_archive.py +360 -0
- agent_evolve/policies/reward/frozen_wave_archive.py +368 -0
- agent_evolve/policies/search_state.py +208 -0
- agent_evolve/policies/selection/__init__.py +345 -0
- agent_evolve/policies/selection/acquisition_certified_slate.py +684 -0
- agent_evolve/policies/selection/affine_frontier_context.py +330 -0
- agent_evolve/policies/selection/affine_frontier_target.py +473 -0
- agent_evolve/policies/selection/archive_elite.py +1346 -0
- agent_evolve/policies/selection/calibrated_portfolio_binding.py +640 -0
- agent_evolve/policies/selection/calibrated_slate.py +1394 -0
- agent_evolve/policies/selection/calibrated_slate_codec.py +579 -0
- agent_evolve/policies/selection/common_candidate_pool.py +685 -0
- agent_evolve/policies/selection/diagnostic_sampling.py +319 -0
- agent_evolve/policies/selection/disjoint_pairs.py +479 -0
- agent_evolve/policies/selection/elite_explorer.py +719 -0
- agent_evolve/policies/selection/finite_action.py +187 -0
- agent_evolve/policies/selection/finite_option_prompt_projection.py +377 -0
- agent_evolve/policies/selection/finite_palette_evidence.py +247 -0
- agent_evolve/policies/selection/forecast_calibration.py +922 -0
- agent_evolve/policies/selection/frontier_probe_slate.py +814 -0
- agent_evolve/policies/selection/frozen_archive_pairs.py +762 -0
- agent_evolve/policies/selection/full_support_slate.py +91 -0
- agent_evolve/policies/selection/meaningful_direction.py +240 -0
- agent_evolve/policies/selection/memory_dose_feasibility.py +259 -0
- agent_evolve/policies/selection/model_anchored_slate.py +826 -0
- agent_evolve/policies/selection/phenotype_recourse.py +979 -0
- agent_evolve/policies/selection/proposal_support.py +368 -0
- agent_evolve/policies/selection/random_portfolio.py +254 -0
- agent_evolve/policies/selection/regret_bounded_slate.py +1084 -0
- agent_evolve/policies/selection/residual_frontier.py +463 -0
- agent_evolve/policies/selection/residual_frontier_target.py +605 -0
- agent_evolve/policies/selection/structural_posterior_slate.py +1571 -0
- agent_evolve/policies/selection/target_conditioned_allocator.py +648 -0
- agent_evolve/policies/selection/target_conditioned_features.py +812 -0
- agent_evolve/policies/selection/target_conditioned_prequential.py +1527 -0
- agent_evolve/policies/selection/task_keyed_palette.py +906 -0
- agent_evolve/policies/semantics.py +147 -0
- agent_evolve/policies/structure.py +362 -0
- agent_evolve/policies/structured_output_budget.py +62 -0
- agent_evolve/policies/surrogate.py +696 -0
- agent_evolve/policies/variation/__init__.py +1 -0
- agent_evolve/policies/variation/compositional_finite_catalog.py +426 -0
- agent_evolve/policies/variation/crossover_inheritance.py +575 -0
- agent_evolve/policies/variation/disjoint_recombination.py +611 -0
- agent_evolve/policies/variation/exact_composition_capacity.py +214 -0
- agent_evolve/policies/variation/exact_parent_crossover.py +950 -0
- agent_evolve/policies/variation/multiscale_restart_catalog.py +372 -0
- agent_evolve/policies/variation/source_union_finite_catalog.py +403 -0
- agent_evolve/policies/variation/typed_patch.py +1981 -0
- agent_evolve/policies/weighted_prior.py +394 -0
- agent_evolve/ports/__init__.py +383 -0
- agent_evolve/ports/action_allocation.py +733 -0
- agent_evolve/ports/action_allocation_frame.py +1153 -0
- agent_evolve/ports/action_allocation_frame_commit.py +294 -0
- agent_evolve/ports/action_allocation_frame_commit_v3.py +432 -0
- agent_evolve/ports/action_allocation_frame_v3.py +995 -0
- agent_evolve/ports/action_forecast.py +1568 -0
- agent_evolve/ports/action_metric_projection.py +165 -0
- agent_evolve/ports/agentic_generator.py +1561 -0
- agent_evolve/ports/archive_context.py +136 -0
- agent_evolve/ports/artifact_sanitizer.py +44 -0
- agent_evolve/ports/artifact_store.py +225 -0
- agent_evolve/ports/clock.py +13 -0
- agent_evolve/ports/contextual_search_allocation.py +827 -0
- agent_evolve/ports/decision_metric_projection.py +258 -0
- agent_evolve/ports/event_store.py +55 -0
- agent_evolve/ports/executable_hypothesis.py +557 -0
- agent_evolve/ports/finite_acquisition.py +377 -0
- agent_evolve/ports/finite_acquisition_batch.py +296 -0
- agent_evolve/ports/finite_acquisition_batch_json.py +164 -0
- agent_evolve/ports/finite_acquisition_json.py +247 -0
- agent_evolve/ports/finite_acquisition_space.py +168 -0
- agent_evolve/ports/finite_action_selection.py +348 -0
- agent_evolve/ports/finite_action_set.py +256 -0
- agent_evolve/ports/frontier_target.py +396 -0
- agent_evolve/ports/generation_failure.py +43 -0
- agent_evolve/ports/hard_feasibility.py +233 -0
- agent_evolve/ports/id_factory.py +34 -0
- agent_evolve/ports/llm_task_queue.py +93 -0
- agent_evolve/ports/objective_resolution.py +419 -0
- agent_evolve/ports/paired_allocation_comparison.py +401 -0
- agent_evolve/ports/paired_block_schedule.py +475 -0
- agent_evolve/ports/parent_measurement.py +336 -0
- agent_evolve/ports/portfolio_memory_dose.py +643 -0
- agent_evolve/ports/portfolio_selection.py +3169 -0
- agent_evolve/ports/postcommit_rank_authority.py +467 -0
- agent_evolve/ports/presented_action_evidence.py +794 -0
- agent_evolve/ports/resource_lease.py +162 -0
- agent_evolve/ports/structured_generator.py +734 -0
- agent_evolve/ports/structured_output_budget.py +120 -0
- agent_evolve/ports/subprocess_boundary.py +138 -0
- agent_evolve/ports/treatment_assignment.py +466 -0
- agent_evolve/ports/variation_catalog.py +76 -0
- agent_evolve/ports/variation_source.py +226 -0
- agent_evolve/proposal_mode.py +157 -0
- agent_evolve/proposers/__init__.py +10 -0
- agent_evolve/proposers/random_proposer.py +188 -0
- agent_evolve/provider_accounting.py +163 -0
- agent_evolve/py.typed +0 -0
- agent_evolve/reference_method.py +1570 -0
- agent_evolve/session/__init__.py +11 -0
- agent_evolve/session/authorship.py +864 -0
- agent_evolve/session/evaluate.py +236 -0
- agent_evolve/session/fidelity.py +237 -0
- agent_evolve/session/genetic_loop.py +742 -0
- agent_evolve/session/loop.py +803 -0
- agent_evolve/session/screening.py +671 -0
- agent_evolve/settings.py +376 -0
- agent_evolve/workload_kit.py +368 -0
- agent_evolve/workload_prompt.py +398 -0
- agentevolve_optimizer-0.5.0.dist-info/METADATA +599 -0
- agentevolve_optimizer-0.5.0.dist-info/RECORD +414 -0
- agentevolve_optimizer-0.5.0.dist-info/WHEEL +5 -0
- agentevolve_optimizer-0.5.0.dist-info/entry_points.txt +2 -0
- agentevolve_optimizer-0.5.0.dist-info/licenses/LICENSE +21 -0
- agentevolve_optimizer-0.5.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,484 @@
|
|
|
1
|
+
"""Conservative, deterministic sanitization for canonical JSON artifacts.
|
|
2
|
+
|
|
3
|
+
This adapter deliberately makes a bounded claim: it handles configured literal
|
|
4
|
+
secrets and a documented set of common credential forms, then rejects known
|
|
5
|
+
high-risk residual material. It is not advertised as a general DLP system.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import math
|
|
11
|
+
import re
|
|
12
|
+
import hashlib
|
|
13
|
+
import json
|
|
14
|
+
from types import MappingProxyType
|
|
15
|
+
from typing import Any, Iterable, Mapping
|
|
16
|
+
|
|
17
|
+
from agent_evolve.domain.artifact import ArtifactRole
|
|
18
|
+
from agent_evolve.ports.artifact_sanitizer import (
|
|
19
|
+
ArtifactMinimizationError,
|
|
20
|
+
ArtifactSanitizationError,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
_REDACTED = "[REDACTED]"
|
|
24
|
+
_FAILED = object()
|
|
25
|
+
_POLICY_COMPONENT = re.compile(r"^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$")
|
|
26
|
+
_SECRET_KEY_NAMES = frozenset(
|
|
27
|
+
{
|
|
28
|
+
"accesstoken",
|
|
29
|
+
"apikey",
|
|
30
|
+
"authorization",
|
|
31
|
+
"authtoken",
|
|
32
|
+
"clientsecret",
|
|
33
|
+
"cookie",
|
|
34
|
+
"credential",
|
|
35
|
+
"credentials",
|
|
36
|
+
"password",
|
|
37
|
+
"passwd",
|
|
38
|
+
"privatekey",
|
|
39
|
+
"proxyauthorization",
|
|
40
|
+
"refreshtoken",
|
|
41
|
+
"secret",
|
|
42
|
+
"setcookie",
|
|
43
|
+
"token",
|
|
44
|
+
}
|
|
45
|
+
)
|
|
46
|
+
_SECRET_KEY_SUFFIXES = (
|
|
47
|
+
"accesstoken",
|
|
48
|
+
"accesskeyid",
|
|
49
|
+
"apikey",
|
|
50
|
+
"authtoken",
|
|
51
|
+
"clientsecret",
|
|
52
|
+
"credential",
|
|
53
|
+
"credentials",
|
|
54
|
+
"password",
|
|
55
|
+
"passwd",
|
|
56
|
+
"privatekey",
|
|
57
|
+
"refreshtoken",
|
|
58
|
+
"authorization",
|
|
59
|
+
"cookie",
|
|
60
|
+
"secret",
|
|
61
|
+
"secretaccesskey",
|
|
62
|
+
"secretkey",
|
|
63
|
+
"sessiontoken",
|
|
64
|
+
"signingkey",
|
|
65
|
+
"token",
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
# Each expression replaces the complete credential-bearing match. Patterns are
|
|
69
|
+
# ordered from contextual headers/assignments to standalone token signatures.
|
|
70
|
+
_CREDENTIAL_PATTERNS = (
|
|
71
|
+
re.compile(
|
|
72
|
+
r"\b(?:Bearer|Basic)[ \t]+[A-Za-z0-9._~+/=-]+",
|
|
73
|
+
re.IGNORECASE,
|
|
74
|
+
),
|
|
75
|
+
re.compile(
|
|
76
|
+
r"(?<![A-Za-z0-9])(?:api[ _-]?key|access[ _-]?key(?:[ _-]?id)?|"
|
|
77
|
+
r"secret[ _-]?access[ _-]?key|session[ _-]?token|"
|
|
78
|
+
r"access[ _-]?token|auth[ _-]?token|refresh[ _-]?token|"
|
|
79
|
+
r"client[ _-]?secret|authorization|password|passwd|"
|
|
80
|
+
r"credential|token|secret)"
|
|
81
|
+
r"[ \t]*[:=][ \t]*(?:\"[^\"\r\n]+\"|'[^'\r\n]+'|[^\s,;}{]+)",
|
|
82
|
+
re.IGNORECASE,
|
|
83
|
+
),
|
|
84
|
+
re.compile(r"\b(?:Cookie|Set-Cookie)[ \t]*:[^\r\n]+", re.IGNORECASE),
|
|
85
|
+
re.compile(
|
|
86
|
+
r"(?<![A-Za-z0-9])(?:sk|pk|rk)-[A-Za-z0-9_-]{12,}(?![A-Za-z0-9])",
|
|
87
|
+
re.IGNORECASE,
|
|
88
|
+
),
|
|
89
|
+
re.compile(
|
|
90
|
+
r"(?<![A-Za-z0-9])or-v1-[A-Za-z0-9_-]{12,}(?![A-Za-z0-9])",
|
|
91
|
+
re.IGNORECASE,
|
|
92
|
+
),
|
|
93
|
+
re.compile(
|
|
94
|
+
r"(?<![A-Za-z0-9])(?:ghp_[A-Za-z0-9]{20,}|"
|
|
95
|
+
r"github_pat_[A-Za-z0-9_]{20,})(?![A-Za-z0-9])"
|
|
96
|
+
),
|
|
97
|
+
re.compile(
|
|
98
|
+
r"(?<![A-Za-z0-9])glpat-[A-Za-z0-9_-]{12,}(?![A-Za-z0-9])"
|
|
99
|
+
),
|
|
100
|
+
re.compile(r"(?<![A-Za-z0-9])AKIA[0-9A-Z]{16}(?![A-Za-z0-9])"),
|
|
101
|
+
re.compile(
|
|
102
|
+
r"(?<![A-Za-z0-9])eyJ[A-Za-z0-9_-]{8,}\."
|
|
103
|
+
r"[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}(?![A-Za-z0-9])"
|
|
104
|
+
),
|
|
105
|
+
)
|
|
106
|
+
_PRIVATE_KEY_MATERIAL = re.compile(
|
|
107
|
+
r"-----BEGIN(?: [A-Z0-9]+)? PRIVATE KEY-----", re.IGNORECASE
|
|
108
|
+
)
|
|
109
|
+
_CREDENTIAL_IN_URL = re.compile(
|
|
110
|
+
# Bound the scheme scan so long punctuation-heavy nonmatches remain linear.
|
|
111
|
+
r"\b[a-z][a-z0-9+.-]{0,31}://[^/\s:@]+:[^/\s@]+@", re.IGNORECASE
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
def _generic_minimization_failure() -> ArtifactMinimizationError:
|
|
116
|
+
return ArtifactMinimizationError(
|
|
117
|
+
"artifact minimization failed under the configured role policy"
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def _generic_sanitization_failure() -> ArtifactSanitizationError:
|
|
122
|
+
return ArtifactSanitizationError(
|
|
123
|
+
"artifact sanitization rejected unsafe or unsupported content"
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
class TopLevelAllowlistMinimizer:
|
|
128
|
+
"""Keep only explicitly allowed top-level fields for each artifact role.
|
|
129
|
+
|
|
130
|
+
Every role that may be used must be configured. There is intentionally no
|
|
131
|
+
implicit identity fallback: a missing policy is a fail-closed error.
|
|
132
|
+
"""
|
|
133
|
+
|
|
134
|
+
__slots__ = ("_allowed", "_policy_config_sha256")
|
|
135
|
+
|
|
136
|
+
def __init__(
|
|
137
|
+
self,
|
|
138
|
+
allowed_fields_by_role: Mapping[ArtifactRole, Iterable[str]],
|
|
139
|
+
) -> None:
|
|
140
|
+
prepared = self._prepare(allowed_fields_by_role)
|
|
141
|
+
if prepared is _FAILED:
|
|
142
|
+
raise _generic_minimization_failure()
|
|
143
|
+
assert isinstance(prepared, tuple)
|
|
144
|
+
self._allowed, self._policy_config_sha256 = prepared
|
|
145
|
+
|
|
146
|
+
@staticmethod
|
|
147
|
+
def _prepare(
|
|
148
|
+
allowed_fields_by_role: Mapping[ArtifactRole, Iterable[str]],
|
|
149
|
+
) -> tuple[MappingProxyType, str] | object:
|
|
150
|
+
try:
|
|
151
|
+
prepared: dict[ArtifactRole, frozenset[str]] = {}
|
|
152
|
+
for role, fields in allowed_fields_by_role.items():
|
|
153
|
+
if not isinstance(role, ArtifactRole) or isinstance(fields, str):
|
|
154
|
+
raise _generic_minimization_failure()
|
|
155
|
+
validated_fields: list[str] = []
|
|
156
|
+
for field in fields:
|
|
157
|
+
if (
|
|
158
|
+
type(field) is not str
|
|
159
|
+
or not field
|
|
160
|
+
or field != field.strip()
|
|
161
|
+
):
|
|
162
|
+
raise _generic_minimization_failure()
|
|
163
|
+
field.encode("utf-8", errors="strict")
|
|
164
|
+
validated_fields.append(field)
|
|
165
|
+
field_set = frozenset(validated_fields)
|
|
166
|
+
prepared[role] = field_set
|
|
167
|
+
policy_record = {
|
|
168
|
+
role.value: sorted(field_set)
|
|
169
|
+
for role, field_set in prepared.items()
|
|
170
|
+
}
|
|
171
|
+
encoded_policy = json.dumps(
|
|
172
|
+
policy_record,
|
|
173
|
+
ensure_ascii=False,
|
|
174
|
+
separators=(",", ":"),
|
|
175
|
+
sort_keys=True,
|
|
176
|
+
).encode("utf-8", errors="strict")
|
|
177
|
+
return (
|
|
178
|
+
MappingProxyType(prepared),
|
|
179
|
+
hashlib.sha256(encoded_policy).hexdigest(),
|
|
180
|
+
)
|
|
181
|
+
except Exception:
|
|
182
|
+
return _FAILED
|
|
183
|
+
|
|
184
|
+
@property
|
|
185
|
+
def policy_id(self) -> str:
|
|
186
|
+
return "top-level-allowlist"
|
|
187
|
+
|
|
188
|
+
@property
|
|
189
|
+
def policy_version(self) -> str:
|
|
190
|
+
return "1"
|
|
191
|
+
|
|
192
|
+
@property
|
|
193
|
+
def policy_config_sha256(self) -> str:
|
|
194
|
+
return self._policy_config_sha256
|
|
195
|
+
|
|
196
|
+
def minimize_json(self, value: Any, *, role: ArtifactRole) -> Any:
|
|
197
|
+
minimized = self._attempt_minimize(value, role)
|
|
198
|
+
if minimized is _FAILED:
|
|
199
|
+
raise _generic_minimization_failure()
|
|
200
|
+
return minimized
|
|
201
|
+
|
|
202
|
+
def _attempt_minimize(self, value: Any, role: ArtifactRole) -> Any:
|
|
203
|
+
try:
|
|
204
|
+
if not isinstance(role, ArtifactRole):
|
|
205
|
+
raise _generic_minimization_failure()
|
|
206
|
+
allowed = self._allowed.get(role)
|
|
207
|
+
if allowed is None or type(value) is not dict:
|
|
208
|
+
raise _generic_minimization_failure()
|
|
209
|
+
# Sorting is not required by the canonical encoder, but makes the
|
|
210
|
+
# intermediate result deterministic. Iterating the small allowlist
|
|
211
|
+
# also avoids touching or sorting fields that minimization discards.
|
|
212
|
+
return {
|
|
213
|
+
key: value[key]
|
|
214
|
+
for key in sorted(allowed)
|
|
215
|
+
if key in value
|
|
216
|
+
}
|
|
217
|
+
except Exception:
|
|
218
|
+
return _FAILED
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
class StrictJsonSanitizer:
|
|
222
|
+
"""Recursively redact a strict JSON value under a versioned policy."""
|
|
223
|
+
|
|
224
|
+
__slots__ = (
|
|
225
|
+
"_exact_secrets",
|
|
226
|
+
"_max_depth",
|
|
227
|
+
"_max_nodes",
|
|
228
|
+
"_max_string_bytes",
|
|
229
|
+
"_max_total_string_bytes",
|
|
230
|
+
"_policy_id",
|
|
231
|
+
"_policy_version",
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
def __init__(
|
|
235
|
+
self,
|
|
236
|
+
*,
|
|
237
|
+
exact_secret_values: Iterable[str] = (),
|
|
238
|
+
policy_id: str = "strict-json-redaction",
|
|
239
|
+
policy_version: str = "1",
|
|
240
|
+
max_depth: int = 64,
|
|
241
|
+
max_nodes: int = 100_000,
|
|
242
|
+
max_string_bytes: int = 1_000_000,
|
|
243
|
+
max_total_string_bytes: int = 4_000_000,
|
|
244
|
+
) -> None:
|
|
245
|
+
prepared = self._prepare_configuration(
|
|
246
|
+
exact_secret_values=exact_secret_values,
|
|
247
|
+
policy_id=policy_id,
|
|
248
|
+
policy_version=policy_version,
|
|
249
|
+
max_depth=max_depth,
|
|
250
|
+
max_nodes=max_nodes,
|
|
251
|
+
max_string_bytes=max_string_bytes,
|
|
252
|
+
max_total_string_bytes=max_total_string_bytes,
|
|
253
|
+
)
|
|
254
|
+
if prepared is _FAILED:
|
|
255
|
+
raise _generic_sanitization_failure()
|
|
256
|
+
(
|
|
257
|
+
self._exact_secrets,
|
|
258
|
+
self._policy_id,
|
|
259
|
+
self._policy_version,
|
|
260
|
+
self._max_depth,
|
|
261
|
+
self._max_nodes,
|
|
262
|
+
self._max_string_bytes,
|
|
263
|
+
self._max_total_string_bytes,
|
|
264
|
+
) = prepared
|
|
265
|
+
|
|
266
|
+
@staticmethod
|
|
267
|
+
def _prepare_configuration(
|
|
268
|
+
*,
|
|
269
|
+
exact_secret_values: Iterable[str],
|
|
270
|
+
policy_id: str,
|
|
271
|
+
policy_version: str,
|
|
272
|
+
max_depth: int,
|
|
273
|
+
max_nodes: int,
|
|
274
|
+
max_string_bytes: int,
|
|
275
|
+
max_total_string_bytes: int,
|
|
276
|
+
) -> tuple[Any, ...] | object:
|
|
277
|
+
try:
|
|
278
|
+
if (
|
|
279
|
+
not isinstance(policy_id, str)
|
|
280
|
+
or _POLICY_COMPONENT.fullmatch(policy_id) is None
|
|
281
|
+
or not isinstance(policy_version, str)
|
|
282
|
+
or _POLICY_COMPONENT.fullmatch(policy_version) is None
|
|
283
|
+
):
|
|
284
|
+
raise _generic_sanitization_failure()
|
|
285
|
+
if (
|
|
286
|
+
isinstance(max_depth, bool)
|
|
287
|
+
or not isinstance(max_depth, int)
|
|
288
|
+
or max_depth < 1
|
|
289
|
+
or isinstance(max_nodes, bool)
|
|
290
|
+
or not isinstance(max_nodes, int)
|
|
291
|
+
or max_nodes < 1
|
|
292
|
+
or isinstance(max_string_bytes, bool)
|
|
293
|
+
or not isinstance(max_string_bytes, int)
|
|
294
|
+
or max_string_bytes < 1
|
|
295
|
+
or isinstance(max_total_string_bytes, bool)
|
|
296
|
+
or not isinstance(max_total_string_bytes, int)
|
|
297
|
+
or max_total_string_bytes < 1
|
|
298
|
+
):
|
|
299
|
+
raise _generic_sanitization_failure()
|
|
300
|
+
secrets: set[str] = set()
|
|
301
|
+
for secret in exact_secret_values:
|
|
302
|
+
if (
|
|
303
|
+
not isinstance(secret, str)
|
|
304
|
+
or len(secret) < 8
|
|
305
|
+
or secret in _REDACTED
|
|
306
|
+
):
|
|
307
|
+
raise _generic_sanitization_failure()
|
|
308
|
+
encoded_secret = secret.encode("utf-8", errors="strict")
|
|
309
|
+
if len(encoded_secret) > max_string_bytes:
|
|
310
|
+
raise _generic_sanitization_failure()
|
|
311
|
+
secrets.add(secret)
|
|
312
|
+
if any(
|
|
313
|
+
secret in policy_id or secret in policy_version
|
|
314
|
+
for secret in secrets
|
|
315
|
+
):
|
|
316
|
+
raise _generic_sanitization_failure()
|
|
317
|
+
if (
|
|
318
|
+
_PRIVATE_KEY_MATERIAL.search(policy_id)
|
|
319
|
+
or _PRIVATE_KEY_MATERIAL.search(policy_version)
|
|
320
|
+
or _CREDENTIAL_IN_URL.search(policy_id)
|
|
321
|
+
or _CREDENTIAL_IN_URL.search(policy_version)
|
|
322
|
+
or any(
|
|
323
|
+
pattern.search(component)
|
|
324
|
+
for pattern in _CREDENTIAL_PATTERNS
|
|
325
|
+
for component in (policy_id, policy_version)
|
|
326
|
+
)
|
|
327
|
+
):
|
|
328
|
+
raise _generic_sanitization_failure()
|
|
329
|
+
# Longest-first replacement prevents a shorter configured value from
|
|
330
|
+
# exposing the unmatched tail of an overlapping longer value.
|
|
331
|
+
exact_secrets = tuple(
|
|
332
|
+
sorted(secrets, key=lambda item: (-len(item), item))
|
|
333
|
+
)
|
|
334
|
+
return (
|
|
335
|
+
exact_secrets,
|
|
336
|
+
policy_id,
|
|
337
|
+
policy_version,
|
|
338
|
+
max_depth,
|
|
339
|
+
max_nodes,
|
|
340
|
+
max_string_bytes,
|
|
341
|
+
max_total_string_bytes,
|
|
342
|
+
)
|
|
343
|
+
except Exception:
|
|
344
|
+
return _FAILED
|
|
345
|
+
|
|
346
|
+
def __repr__(self) -> str:
|
|
347
|
+
return (
|
|
348
|
+
f"{type(self).__name__}(policy_id={self._policy_id!r}, "
|
|
349
|
+
f"policy_version={self._policy_version!r})"
|
|
350
|
+
)
|
|
351
|
+
|
|
352
|
+
@property
|
|
353
|
+
def policy_id(self) -> str:
|
|
354
|
+
return self._policy_id
|
|
355
|
+
|
|
356
|
+
@property
|
|
357
|
+
def policy_version(self) -> str:
|
|
358
|
+
return self._policy_version
|
|
359
|
+
|
|
360
|
+
@staticmethod
|
|
361
|
+
def _is_secret_key(key: str) -> bool:
|
|
362
|
+
normalized = "".join(character.lower() for character in key if character.isalnum())
|
|
363
|
+
return normalized in _SECRET_KEY_NAMES or normalized.endswith(
|
|
364
|
+
_SECRET_KEY_SUFFIXES
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
def _sanitize_valid_string(self, value: str, *, is_key: bool = False) -> str:
|
|
368
|
+
if is_key:
|
|
369
|
+
# Renaming keys could create collisions or alter a schema silently.
|
|
370
|
+
if any(secret in value for secret in self._exact_secrets):
|
|
371
|
+
raise _generic_sanitization_failure()
|
|
372
|
+
if (
|
|
373
|
+
_PRIVATE_KEY_MATERIAL.search(value)
|
|
374
|
+
or _CREDENTIAL_IN_URL.search(value)
|
|
375
|
+
or any(pattern.search(value) for pattern in _CREDENTIAL_PATTERNS)
|
|
376
|
+
):
|
|
377
|
+
raise _generic_sanitization_failure()
|
|
378
|
+
return value
|
|
379
|
+
|
|
380
|
+
sanitized = value
|
|
381
|
+
for secret in self._exact_secrets:
|
|
382
|
+
sanitized = sanitized.replace(secret, _REDACTED)
|
|
383
|
+
for pattern in _CREDENTIAL_PATTERNS:
|
|
384
|
+
sanitized = pattern.sub(_REDACTED, sanitized)
|
|
385
|
+
|
|
386
|
+
# These forms are too structurally sensitive to persist after an
|
|
387
|
+
# in-place rewrite; fail closed and let the caller supply a minimizer.
|
|
388
|
+
if _PRIVATE_KEY_MATERIAL.search(sanitized) or _CREDENTIAL_IN_URL.search(
|
|
389
|
+
sanitized
|
|
390
|
+
):
|
|
391
|
+
raise _generic_sanitization_failure()
|
|
392
|
+
if any(secret in sanitized for secret in self._exact_secrets):
|
|
393
|
+
raise _generic_sanitization_failure()
|
|
394
|
+
return sanitized
|
|
395
|
+
|
|
396
|
+
def sanitize_json(self, value: Any, *, role: ArtifactRole) -> Any:
|
|
397
|
+
sanitized = self._attempt_sanitize(value, role)
|
|
398
|
+
if sanitized is _FAILED:
|
|
399
|
+
raise _generic_sanitization_failure()
|
|
400
|
+
return sanitized
|
|
401
|
+
|
|
402
|
+
def _attempt_sanitize(self, value: Any, role: ArtifactRole) -> Any:
|
|
403
|
+
ancestors: set[int] = set()
|
|
404
|
+
nodes_seen = 0
|
|
405
|
+
string_bytes_seen = 0
|
|
406
|
+
|
|
407
|
+
def reserve_nodes(count: int = 1) -> None:
|
|
408
|
+
nonlocal nodes_seen
|
|
409
|
+
nodes_seen += count
|
|
410
|
+
if nodes_seen > self._max_nodes:
|
|
411
|
+
raise _generic_sanitization_failure()
|
|
412
|
+
|
|
413
|
+
def sanitize_string(item: str, *, is_key: bool = False) -> str:
|
|
414
|
+
nonlocal string_bytes_seen
|
|
415
|
+
encoded = item.encode("utf-8", errors="strict")
|
|
416
|
+
size = len(encoded)
|
|
417
|
+
string_bytes_seen += size
|
|
418
|
+
if (
|
|
419
|
+
size > self._max_string_bytes
|
|
420
|
+
or string_bytes_seen > self._max_total_string_bytes
|
|
421
|
+
):
|
|
422
|
+
raise _generic_sanitization_failure()
|
|
423
|
+
return self._sanitize_valid_string(item, is_key=is_key)
|
|
424
|
+
|
|
425
|
+
def visit(item: Any, depth: int) -> Any:
|
|
426
|
+
reserve_nodes()
|
|
427
|
+
if depth > self._max_depth:
|
|
428
|
+
raise _generic_sanitization_failure()
|
|
429
|
+
|
|
430
|
+
if item is None or type(item) in (bool, int):
|
|
431
|
+
return item
|
|
432
|
+
if type(item) is float:
|
|
433
|
+
if not math.isfinite(item):
|
|
434
|
+
raise _generic_sanitization_failure()
|
|
435
|
+
return item
|
|
436
|
+
if type(item) is str:
|
|
437
|
+
return sanitize_string(item)
|
|
438
|
+
if type(item) in (list, tuple):
|
|
439
|
+
if len(item) > self._max_nodes - nodes_seen:
|
|
440
|
+
raise _generic_sanitization_failure()
|
|
441
|
+
identity = id(item)
|
|
442
|
+
if identity in ancestors:
|
|
443
|
+
raise _generic_sanitization_failure()
|
|
444
|
+
ancestors.add(identity)
|
|
445
|
+
try:
|
|
446
|
+
return [visit(child, depth + 1) for child in item]
|
|
447
|
+
finally:
|
|
448
|
+
ancestors.remove(identity)
|
|
449
|
+
if type(item) is dict:
|
|
450
|
+
# Every entry accounts for one key and at least one value slot.
|
|
451
|
+
# Gate this before sorting to avoid unbounded pre-check work.
|
|
452
|
+
if len(item) * 2 > self._max_nodes - nodes_seen:
|
|
453
|
+
raise _generic_sanitization_failure()
|
|
454
|
+
identity = id(item)
|
|
455
|
+
if identity in ancestors:
|
|
456
|
+
raise _generic_sanitization_failure()
|
|
457
|
+
ancestors.add(identity)
|
|
458
|
+
try:
|
|
459
|
+
result: dict[str, Any] = {}
|
|
460
|
+
validated_keys: list[str] = []
|
|
461
|
+
for key in item:
|
|
462
|
+
reserve_nodes() # object key
|
|
463
|
+
if type(key) is not str:
|
|
464
|
+
raise _generic_sanitization_failure()
|
|
465
|
+
validated_keys.append(sanitize_string(key, is_key=True))
|
|
466
|
+
for clean_key in sorted(validated_keys):
|
|
467
|
+
if self._is_secret_key(clean_key):
|
|
468
|
+
reserve_nodes() # redacted value slot
|
|
469
|
+
result[clean_key] = _REDACTED
|
|
470
|
+
else:
|
|
471
|
+
result[clean_key] = visit(item[clean_key], depth + 1)
|
|
472
|
+
return result
|
|
473
|
+
finally:
|
|
474
|
+
ancestors.remove(identity)
|
|
475
|
+
raise _generic_sanitization_failure()
|
|
476
|
+
|
|
477
|
+
try:
|
|
478
|
+
if not isinstance(role, ArtifactRole):
|
|
479
|
+
raise _generic_sanitization_failure()
|
|
480
|
+
return visit(value, 0)
|
|
481
|
+
except Exception:
|
|
482
|
+
# Returning clears implicit exception context before the public
|
|
483
|
+
# method constructs its generic error.
|
|
484
|
+
return _FAILED
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"""Durable JSONL interception for sequential residual-evolution phases."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import hashlib
|
|
6
|
+
import json
|
|
7
|
+
import os
|
|
8
|
+
from dataclasses import dataclass, field
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from agent_evolve.application.sequential_residual_portfolio_evolution import (
|
|
12
|
+
SequentialResidualPhaseCommitAck,
|
|
13
|
+
SequentialResidualPhaseReceipt,
|
|
14
|
+
)
|
|
15
|
+
from agent_evolve.domain.typed_json import freeze_json
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
DURABLE_SEQUENTIAL_PHASE_JOURNAL_ID = (
|
|
19
|
+
"durable_jsonl_sequential_phase_journal"
|
|
20
|
+
)
|
|
21
|
+
DURABLE_SEQUENTIAL_PHASE_JOURNAL_VERSION = 1
|
|
22
|
+
DURABLE_SEQUENTIAL_PHASE_JOURNAL_DEFINITION_SHA256 = hashlib.sha256(
|
|
23
|
+
b"agent-evolve:durable-jsonl-sequential-phase-journal:v1;"
|
|
24
|
+
b"record=full-receipt-evidence-plus-authenticated-ack;"
|
|
25
|
+
b"write=posix-append-before-fsync;"
|
|
26
|
+
b"first-create=parent-directory-fsync;"
|
|
27
|
+
b"duplicate-phase-receipt=fail-closed"
|
|
28
|
+
).hexdigest()
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _canonical_json(value: object) -> bytes:
|
|
32
|
+
return json.dumps(
|
|
33
|
+
value,
|
|
34
|
+
allow_nan=False,
|
|
35
|
+
ensure_ascii=True,
|
|
36
|
+
separators=(",", ":"),
|
|
37
|
+
sort_keys=True,
|
|
38
|
+
).encode("ascii", errors="strict")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _fsync_directory(path: Path) -> None:
|
|
42
|
+
if os.name != "posix": # pragma: no cover - production target is POSIX.
|
|
43
|
+
return
|
|
44
|
+
descriptor = os.open(path, os.O_RDONLY | os.O_DIRECTORY)
|
|
45
|
+
try:
|
|
46
|
+
os.fsync(descriptor)
|
|
47
|
+
finally:
|
|
48
|
+
os.close(descriptor)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@dataclass(slots=True)
|
|
52
|
+
class DurableJsonlSequentialPhaseCommitter:
|
|
53
|
+
"""Append and fsync every phase before the runtime may continue."""
|
|
54
|
+
|
|
55
|
+
path: Path
|
|
56
|
+
committer_id: str = field(
|
|
57
|
+
init=False,
|
|
58
|
+
default=DURABLE_SEQUENTIAL_PHASE_JOURNAL_ID,
|
|
59
|
+
)
|
|
60
|
+
committer_version: int = field(
|
|
61
|
+
init=False,
|
|
62
|
+
default=DURABLE_SEQUENTIAL_PHASE_JOURNAL_VERSION,
|
|
63
|
+
)
|
|
64
|
+
definition_sha256: str = field(
|
|
65
|
+
init=False,
|
|
66
|
+
default=DURABLE_SEQUENTIAL_PHASE_JOURNAL_DEFINITION_SHA256,
|
|
67
|
+
)
|
|
68
|
+
_seen_receipt_sha256s: set[str] = field(
|
|
69
|
+
init=False,
|
|
70
|
+
default_factory=set,
|
|
71
|
+
)
|
|
72
|
+
_commit_count: int = field(init=False, default=0)
|
|
73
|
+
|
|
74
|
+
def __post_init__(self) -> None:
|
|
75
|
+
if not isinstance(self.path, Path):
|
|
76
|
+
raise TypeError("path must be a pathlib.Path")
|
|
77
|
+
if self.path.exists() and not self.path.is_file():
|
|
78
|
+
raise ValueError("phase journal path must be a regular file")
|
|
79
|
+
self.path.parent.mkdir(parents=True, exist_ok=True)
|
|
80
|
+
if not self.path.exists():
|
|
81
|
+
return
|
|
82
|
+
for line_number, raw in enumerate(
|
|
83
|
+
self.path.read_bytes().splitlines(),
|
|
84
|
+
start=1,
|
|
85
|
+
):
|
|
86
|
+
if not raw.strip():
|
|
87
|
+
continue
|
|
88
|
+
try:
|
|
89
|
+
row = json.loads(raw)
|
|
90
|
+
receipt_sha256 = row["receipt"]["receipt_sha256"]
|
|
91
|
+
sequence = row["sequence"]
|
|
92
|
+
except (KeyError, TypeError, ValueError) as error:
|
|
93
|
+
raise ValueError(
|
|
94
|
+
f"invalid sequential phase journal line {line_number}"
|
|
95
|
+
) from error
|
|
96
|
+
if (
|
|
97
|
+
type(receipt_sha256) is not str
|
|
98
|
+
or len(receipt_sha256) != 64
|
|
99
|
+
or type(sequence) is not int
|
|
100
|
+
or sequence <= 0
|
|
101
|
+
):
|
|
102
|
+
raise ValueError(
|
|
103
|
+
f"invalid sequential phase journal line {line_number}"
|
|
104
|
+
)
|
|
105
|
+
if receipt_sha256 in self._seen_receipt_sha256s:
|
|
106
|
+
raise ValueError("phase journal repeats a receipt")
|
|
107
|
+
self._seen_receipt_sha256s.add(receipt_sha256)
|
|
108
|
+
self._commit_count = max(self._commit_count, sequence)
|
|
109
|
+
|
|
110
|
+
async def commit(
|
|
111
|
+
self,
|
|
112
|
+
receipt: SequentialResidualPhaseReceipt,
|
|
113
|
+
) -> SequentialResidualPhaseCommitAck:
|
|
114
|
+
if type(receipt) is not SequentialResidualPhaseReceipt:
|
|
115
|
+
raise TypeError("receipt must be exact")
|
|
116
|
+
receipt.__post_init__()
|
|
117
|
+
if receipt.receipt_sha256 in self._seen_receipt_sha256s:
|
|
118
|
+
raise ValueError("phase receipt was already durably committed")
|
|
119
|
+
sequence = self._commit_count + 1
|
|
120
|
+
ack = SequentialResidualPhaseCommitAck(
|
|
121
|
+
committer_id=self.committer_id,
|
|
122
|
+
committer_version=self.committer_version,
|
|
123
|
+
committer_definition_sha256=self.definition_sha256,
|
|
124
|
+
phase_receipt_sha256=receipt.receipt_sha256,
|
|
125
|
+
durable=True,
|
|
126
|
+
evidence=freeze_json(
|
|
127
|
+
{
|
|
128
|
+
"backend": "posix_append_fsync",
|
|
129
|
+
"append_sequence": sequence,
|
|
130
|
+
"receipt_evidence_included": True,
|
|
131
|
+
"parent_directory_fsynced_on_create": True,
|
|
132
|
+
}
|
|
133
|
+
),
|
|
134
|
+
)
|
|
135
|
+
row = {
|
|
136
|
+
"schema_version": 1,
|
|
137
|
+
"sequence": sequence,
|
|
138
|
+
"receipt": receipt.to_record(include_evidence=True),
|
|
139
|
+
"ack": ack.to_record(include_evidence=True),
|
|
140
|
+
}
|
|
141
|
+
payload = _canonical_json(row) + b"\n"
|
|
142
|
+
created = not self.path.exists()
|
|
143
|
+
descriptor = os.open(
|
|
144
|
+
self.path,
|
|
145
|
+
os.O_WRONLY | os.O_CREAT | os.O_APPEND,
|
|
146
|
+
0o600,
|
|
147
|
+
)
|
|
148
|
+
try:
|
|
149
|
+
offset = 0
|
|
150
|
+
while offset < len(payload):
|
|
151
|
+
written = os.write(descriptor, payload[offset:])
|
|
152
|
+
if written <= 0: # pragma: no cover - defensive OS boundary.
|
|
153
|
+
raise OSError("phase journal append made no progress")
|
|
154
|
+
offset += written
|
|
155
|
+
os.fsync(descriptor)
|
|
156
|
+
finally:
|
|
157
|
+
os.close(descriptor)
|
|
158
|
+
if created:
|
|
159
|
+
_fsync_directory(self.path.parent)
|
|
160
|
+
self._seen_receipt_sha256s.add(receipt.receipt_sha256)
|
|
161
|
+
self._commit_count = sequence
|
|
162
|
+
return ack
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
__all__ = [
|
|
166
|
+
"DURABLE_SEQUENTIAL_PHASE_JOURNAL_DEFINITION_SHA256",
|
|
167
|
+
"DURABLE_SEQUENTIAL_PHASE_JOURNAL_ID",
|
|
168
|
+
"DURABLE_SEQUENTIAL_PHASE_JOURNAL_VERSION",
|
|
169
|
+
"DurableJsonlSequentialPhaseCommitter",
|
|
170
|
+
]
|