loopx 0.4.8__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.
- loopx/__init__.py +5 -0
- loopx/agent_onboarding.py +654 -0
- loopx/agent_registry.py +112 -0
- loopx/ark_managed_agent_host.py +59 -0
- loopx/authority.py +805 -0
- loopx/benchmark.py +2875 -0
- loopx/benchmark_adapters/__init__.py +1 -0
- loopx/benchmark_adapters/agentissue.py +2644 -0
- loopx/benchmark_adapters/agents_last_exam.py +3998 -0
- loopx/benchmark_adapters/edgebench.py +322 -0
- loopx/benchmark_adapters/skillsbench.py +5978 -0
- loopx/benchmark_adapters/skillsbench_acp_failure_policy.py +143 -0
- loopx/benchmark_adapters/skillsbench_acp_process.py +31 -0
- loopx/benchmark_adapters/skillsbench_acp_relay.py +4832 -0
- loopx/benchmark_adapters/skillsbench_batch.py +124 -0
- loopx/benchmark_adapters/skillsbench_bridge_guard.py +209 -0
- loopx/benchmark_adapters/skillsbench_bridge_summary.py +203 -0
- loopx/benchmark_adapters/skillsbench_codex_goal_recovery.py +271 -0
- loopx/benchmark_adapters/skillsbench_codex_goal_trace.py +81 -0
- loopx/benchmark_adapters/skillsbench_codex_runtime.py +339 -0
- loopx/benchmark_adapters/skillsbench_dockerfile_runtime.py +467 -0
- loopx/benchmark_adapters/skillsbench_failure_signals.py +652 -0
- loopx/benchmark_adapters/skillsbench_proxy_runtime.py +327 -0
- loopx/benchmark_adapters/skillsbench_remote_bridge.py +402 -0
- loopx/benchmark_adapters/skillsbench_result_discovery.py +143 -0
- loopx/benchmark_adapters/skillsbench_runner_profile.py +436 -0
- loopx/benchmark_adapters/skillsbench_runner_source.py +99 -0
- loopx/benchmark_adapters/skillsbench_setup_preflight.py +771 -0
- loopx/benchmark_adapters/skillsbench_signals.py +15 -0
- loopx/benchmark_adapters/skillsbench_task_source.py +141 -0
- loopx/benchmark_adapters/skillsbench_turn_route.py +723 -0
- loopx/benchmark_adapters/skillsbench_turn_runtime.py +1069 -0
- loopx/benchmark_adapters/skillsbench_typed_repair.py +689 -0
- loopx/benchmark_adapters/skillsbench_uv_cache.py +111 -0
- loopx/benchmark_adapters/skillsbench_verifier_bootstrap.py +227 -0
- loopx/benchmark_adapters/skillsbench_verifier_cache.py +138 -0
- loopx/benchmark_adapters/terminal_bench.py +10078 -0
- loopx/benchmark_case_analysis.py +1276 -0
- loopx/benchmark_case_state.py +1079 -0
- loopx/benchmark_core/__init__.py +239 -0
- loopx/benchmark_core/adapter.py +84 -0
- loopx/benchmark_core/artifacts.py +517 -0
- loopx/benchmark_core/attempts.py +199 -0
- loopx/benchmark_core/container_exec.py +216 -0
- loopx/benchmark_core/io.py +68 -0
- loopx/benchmark_core/lifecycle.py +211 -0
- loopx/benchmark_core/loop_protocol.py +689 -0
- loopx/benchmark_core/observable_handles.py +348 -0
- loopx/benchmark_core/parity.py +256 -0
- loopx/benchmark_core/remote_closeout.py +482 -0
- loopx/benchmark_core/rounds.py +215 -0
- loopx/benchmark_core/route_profile.py +509 -0
- loopx/benchmark_core/run_permissions.py +206 -0
- loopx/benchmark_core/split_control.py +925 -0
- loopx/benchmark_core/turn_fidelity.py +326 -0
- loopx/benchmark_ledger.py +3793 -0
- loopx/benchmark_ledger_countability.py +372 -0
- loopx/benchmark_ledger_current.py +724 -0
- loopx/benchmark_trajectory.py +405 -0
- loopx/benchmarks/__init__.py +1 -0
- loopx/benchmarks/qualification/__init__.py +1 -0
- loopx/benchmarks/qualification/release_outcome_baseline.py +360 -0
- loopx/benchmarks/read_models/__init__.py +1 -0
- loopx/benchmarks/read_models/benchmark_attempt_accounting.py +53 -0
- loopx/benchmarks/read_models/benchmark_comparison.py +414 -0
- loopx/benchmarks/read_models/benchmark_event_timeline.py +113 -0
- loopx/benchmarks/read_models/benchmark_experiment_report.py +475 -0
- loopx/benchmarks/read_models/benchmark_learning_ledger.py +137 -0
- loopx/benchmarks/read_models/benchmark_lifecycle_contracts.py +228 -0
- loopx/benchmarks/read_models/benchmark_projection.py +723 -0
- loopx/benchmarks/read_models/benchmark_result.py +146 -0
- loopx/benchmarks/read_models/benchmark_run_execution_contract.py +116 -0
- loopx/benchmarks/read_models/benchmark_run_failure.py +157 -0
- loopx/benchmarks/read_models/benchmark_run_metrics.py +213 -0
- loopx/benchmarks/read_models/benchmark_run_post_execution.py +635 -0
- loopx/benchmarks/read_models/benchmark_run_pre_execution.py +541 -0
- loopx/benchmarks/read_models/benchmark_status_compaction.py +1255 -0
- loopx/benchmarks/read_models/benchmark_status_runner.py +780 -0
- loopx/benchmarks/read_models/goal_start_control_score.py +857 -0
- loopx/benchmarks/read_models/skillsbench_post_run_debug.py +746 -0
- loopx/benchmarks/read_models/skillsbench_verifier_attribution.py +269 -0
- loopx/bootstrap.py +1116 -0
- loopx/bootstrap_command_pack.py +2167 -0
- loopx/boundary_authority.py +199 -0
- loopx/canary/__init__.py +1 -0
- loopx/canary/maintainability_ratchet.py +800 -0
- loopx/canary/planner.py +1984 -0
- loopx/canary/premerge.py +1130 -0
- loopx/canary/qualification_profiles.py +309 -0
- loopx/canary/quality_surface_catalog.py +838 -0
- loopx/canary/release_profiles.py +51 -0
- loopx/canary/runner.py +1107 -0
- loopx/canary/smoke_health.py +581 -0
- loopx/canary/smoke_profiles.py +212 -0
- loopx/capabilities/__init__.py +0 -0
- loopx/capabilities/agent_turn_recall/__init__.py +17 -0
- loopx/capabilities/agent_turn_recall/cli.py +369 -0
- loopx/capabilities/agent_turn_recall/core.py +296 -0
- loopx/capabilities/auto_research/__init__.py +16 -0
- loopx/capabilities/auto_research/bootstrap_contract.py +157 -0
- loopx/capabilities/auto_research/cli.py +1468 -0
- loopx/capabilities/auto_research/core.py +11 -0
- loopx/capabilities/auto_research/defaults.py +79 -0
- loopx/capabilities/auto_research/demo_e2e.py +1848 -0
- loopx/capabilities/auto_research/demo_supervisor.py +186 -0
- loopx/capabilities/auto_research/evidence_packet.py +767 -0
- loopx/capabilities/auto_research/human_view.py +794 -0
- loopx/capabilities/auto_research/kernel.py +191 -0
- loopx/capabilities/auto_research/knn_demo_workspace.py +322 -0
- loopx/capabilities/auto_research/live_evidence.py +248 -0
- loopx/capabilities/auto_research/preset.py +176 -0
- loopx/capabilities/auto_research/research_state.py +1085 -0
- loopx/capabilities/auto_research/role_profiles.py +394 -0
- loopx/capabilities/auto_research/rollout_append.py +97 -0
- loopx/capabilities/auto_research/terminal_result_contract.py +422 -0
- loopx/capabilities/auto_research/terminal_result_projection.py +171 -0
- loopx/capabilities/auto_research/terminal_result_query.py +233 -0
- loopx/capabilities/auto_research/terminal_results.py +349 -0
- loopx/capabilities/auto_research/user_contract.py +190 -0
- loopx/capabilities/auto_research/worker_loop.py +163 -0
- loopx/capabilities/auto_research/worker_runtime.py +777 -0
- loopx/capabilities/auto_research/worker_skill/SKILL.md +343 -0
- loopx/capabilities/benchmark_toolkit/__init__.py +19 -0
- loopx/capabilities/benchmark_toolkit/integrity.py +387 -0
- loopx/capabilities/catalog.py +1875 -0
- loopx/capabilities/change_quality/__init__.py +19 -0
- loopx/capabilities/change_quality/cli.py +171 -0
- loopx/capabilities/change_quality/context.py +156 -0
- loopx/capabilities/change_quality/oracles.py +269 -0
- loopx/capabilities/change_quality/policy.py +34 -0
- loopx/capabilities/change_quality/receipt.py +482 -0
- loopx/capabilities/change_quality/result.py +493 -0
- loopx/capabilities/change_quality/scope.py +171 -0
- loopx/capabilities/change_quality/shadow.py +680 -0
- loopx/capabilities/content_ops/__init__.py +0 -0
- loopx/capabilities/content_ops/cli.py +649 -0
- loopx/capabilities/content_ops/connector_packets.py +164 -0
- loopx/capabilities/content_ops/item_lifecycle.py +1000 -0
- loopx/capabilities/content_ops/layout.py +451 -0
- loopx/capabilities/content_ops/markdown.py +456 -0
- loopx/capabilities/content_ops/schemas.py +51 -0
- loopx/capabilities/content_ops/social_browser_x.py +107 -0
- loopx/capabilities/content_ops/surface.py +1956 -0
- loopx/capabilities/content_ops/templates/layout-catalog-v0.json +72 -0
- loopx/capabilities/context_providers/__init__.py +36 -0
- loopx/capabilities/context_providers/base.py +189 -0
- loopx/capabilities/context_providers/factory.py +32 -0
- loopx/capabilities/context_providers/openviking.py +702 -0
- loopx/capabilities/context_providers/service_ownership.py +185 -0
- loopx/capabilities/decision_context/__init__.py +129 -0
- loopx/capabilities/decision_context/architecture.py +83 -0
- loopx/capabilities/decision_context/assembler.py +849 -0
- loopx/capabilities/decision_context/catalog_entry.py +195 -0
- loopx/capabilities/decision_context/cli.py +310 -0
- loopx/capabilities/decision_context/cursor_commit.py +535 -0
- loopx/capabilities/decision_context/outcome_feedback.py +352 -0
- loopx/capabilities/decision_context/packets.py +654 -0
- loopx/capabilities/decision_context/private_state.py +189 -0
- loopx/capabilities/decision_context/profile.py +453 -0
- loopx/capabilities/decision_context/providers.py +228 -0
- loopx/capabilities/decision_context/review_settlement.py +136 -0
- loopx/capabilities/decision_context/runtime.py +273 -0
- loopx/capabilities/decision_context/sources.py +415 -0
- loopx/capabilities/explore/__init__.py +1 -0
- loopx/capabilities/explore/activation.py +198 -0
- loopx/capabilities/explore/adaptive_replay_planner.py +221 -0
- loopx/capabilities/explore/child_replay_runtime.py +463 -0
- loopx/capabilities/explore/composition_frontier.py +291 -0
- loopx/capabilities/explore/counterfactual_runtime.py +578 -0
- loopx/capabilities/explore/episode_runtime.py +647 -0
- loopx/capabilities/explore/harness_checkpoint.py +171 -0
- loopx/capabilities/explore/harness_gate.py +115 -0
- loopx/capabilities/explore/harness_runtime.py +1124 -0
- loopx/capabilities/explore/replay_metrics.py +206 -0
- loopx/capabilities/explore/replay_runtime.py +1271 -0
- loopx/capabilities/explore/resource_portfolio.py +173 -0
- loopx/capabilities/explore/result_log.py +974 -0
- loopx/capabilities/explore/router_state.py +432 -0
- loopx/capabilities/explore/source_history_reconcile.py +255 -0
- loopx/capabilities/explore/speculative_scheduler.py +498 -0
- loopx/capabilities/explore/todo_branch_plan.py +650 -0
- loopx/capabilities/explore/todo_evidence.py +141 -0
- loopx/capabilities/explore/trace_runtime.py +284 -0
- loopx/capabilities/explore/worker_branch_plan.py +1257 -0
- loopx/capabilities/integration_branch/__init__.py +13 -0
- loopx/capabilities/integration_branch/cli.py +148 -0
- loopx/capabilities/integration_branch/core.py +916 -0
- loopx/capabilities/issue_fix/__init__.py +19 -0
- loopx/capabilities/issue_fix/acceptance_loop.py +1050 -0
- loopx/capabilities/issue_fix/candidate_evidence.py +503 -0
- loopx/capabilities/issue_fix/candidate_preflight.py +676 -0
- loopx/capabilities/issue_fix/cli.py +1822 -0
- loopx/capabilities/issue_fix/cli_input.py +87 -0
- loopx/capabilities/issue_fix/content_ops_cli.py +148 -0
- loopx/capabilities/issue_fix/discovered_issue_promotion.py +947 -0
- loopx/capabilities/issue_fix/explore_projection.py +710 -0
- loopx/capabilities/issue_fix/feasibility.py +542 -0
- loopx/capabilities/issue_fix/github_public.py +661 -0
- loopx/capabilities/issue_fix/intake_surface.py +832 -0
- loopx/capabilities/issue_fix/metadata_preview.py +218 -0
- loopx/capabilities/issue_fix/metrics_projection.py +1340 -0
- loopx/capabilities/issue_fix/metrics_supplement.py +634 -0
- loopx/capabilities/issue_fix/metrics_supplement_cli.py +127 -0
- loopx/capabilities/issue_fix/outcome_projection.py +1235 -0
- loopx/capabilities/issue_fix/periodic_report.py +189 -0
- loopx/capabilities/issue_fix/pr_description.py +418 -0
- loopx/capabilities/issue_fix/pr_gate_reconcile.py +496 -0
- loopx/capabilities/issue_fix/pr_gate_reconcile_cli.py +464 -0
- loopx/capabilities/issue_fix/pr_lifecycle.py +1327 -0
- loopx/capabilities/issue_fix/pr_lifecycle_rollout.py +85 -0
- loopx/capabilities/issue_fix/pr_monitor_materialization.py +257 -0
- loopx/capabilities/issue_fix/pr_review_ack.py +439 -0
- loopx/capabilities/issue_fix/provider_hooks.py +24 -0
- loopx/capabilities/issue_fix/repository_commit_evidence.py +186 -0
- loopx/capabilities/issue_fix/repository_context.py +457 -0
- loopx/capabilities/issue_fix/repository_memory.py +459 -0
- loopx/capabilities/issue_fix/repository_memory_provider.py +1454 -0
- loopx/capabilities/issue_fix/repository_snapshot.py +454 -0
- loopx/capabilities/issue_fix/reviewer_cli.py +917 -0
- loopx/capabilities/issue_fix/reviewer_notification.py +882 -0
- loopx/capabilities/issue_fix/reviewer_notification_drain.py +942 -0
- loopx/capabilities/issue_fix/reviewer_recommendation.py +1057 -0
- loopx/capabilities/issue_fix/reviewer_request.py +1282 -0
- loopx/capabilities/issue_fix/reward_memory.py +879 -0
- loopx/capabilities/issue_fix/workflow_plan.py +1286 -0
- loopx/capabilities/material_lifecycle/__init__.py +161 -0
- loopx/capabilities/material_lifecycle/_validation.py +183 -0
- loopx/capabilities/material_lifecycle/apply.py +672 -0
- loopx/capabilities/material_lifecycle/architecture.py +122 -0
- loopx/capabilities/material_lifecycle/cli.py +161 -0
- loopx/capabilities/material_lifecycle/decision_planning.py +470 -0
- loopx/capabilities/material_lifecycle/explore_execution.py +306 -0
- loopx/capabilities/material_lifecycle/intake.py +869 -0
- loopx/capabilities/material_lifecycle/inventory.py +147 -0
- loopx/capabilities/material_lifecycle/lifecycle.py +98 -0
- loopx/capabilities/material_lifecycle/preparation.py +147 -0
- loopx/capabilities/material_lifecycle/project_skill.py +83 -0
- loopx/capabilities/material_lifecycle/ranking.py +267 -0
- loopx/capabilities/material_lifecycle/readable_projection.py +500 -0
- loopx/capabilities/material_lifecycle/rebuild.py +480 -0
- loopx/capabilities/material_lifecycle/settlement.py +238 -0
- loopx/capabilities/periodic_report/__init__.py +71 -0
- loopx/capabilities/periodic_report/adapters.py +939 -0
- loopx/capabilities/periodic_report/archive.py +422 -0
- loopx/capabilities/periodic_report/bindings.py +705 -0
- loopx/capabilities/periodic_report/cli.py +277 -0
- loopx/capabilities/periodic_report/core.py +691 -0
- loopx/capabilities/periodic_report/extension_envelope.py +66 -0
- loopx/capabilities/periodic_report/presets.py +103 -0
- loopx/capabilities/periodic_report/profile.py +235 -0
- loopx/capabilities/periodic_report/project_progress.py +179 -0
- loopx/capabilities/periodic_report/triggers.py +452 -0
- loopx/capabilities/pr_review_queue/__init__.py +17 -0
- loopx/capabilities/pr_review_queue/core.py +506 -0
- loopx/capabilities/pr_review_queue/review_contract.py +506 -0
- loopx/capabilities/registry.py +192 -0
- loopx/capabilities/reward_memory/__init__.py +75 -0
- loopx/capabilities/reward_memory/application.py +819 -0
- loopx/capabilities/reward_memory/architecture.py +572 -0
- loopx/capabilities/reward_memory/candidate_review.py +511 -0
- loopx/capabilities/reward_memory/cli.py +469 -0
- loopx/capabilities/reward_memory/dogfood.py +574 -0
- loopx/capabilities/reward_memory/evaluation.py +296 -0
- loopx/capabilities/reward_memory/evaluation_fixtures.py +362 -0
- loopx/capabilities/reward_memory/experiment.py +567 -0
- loopx/capabilities/reward_memory/health.py +222 -0
- loopx/capabilities/reward_memory/ingestion.py +519 -0
- loopx/capabilities/reward_memory/registry.py +600 -0
- loopx/capabilities/reward_memory/runtime_hooks.py +312 -0
- loopx/capabilities/reward_memory/scoped_feedback.py +173 -0
- loopx/capabilities/semantic_preference/__init__.py +12 -0
- loopx/capabilities/semantic_preference/cli.py +189 -0
- loopx/capabilities/semantic_preference/contract.py +592 -0
- loopx/capabilities/semantic_preference/reward_memory.py +62 -0
- loopx/capabilities/value_connectors/__init__.py +1 -0
- loopx/capabilities/value_connectors/cli.py +401 -0
- loopx/capabilities/value_connectors/finance_extension_migration.py +108 -0
- loopx/capabilities/value_connectors/install_check.py +147 -0
- loopx/capabilities/value_connectors/planner.py +733 -0
- loopx/capabilities/value_connectors/source_map.py +446 -0
- loopx/claude_goal_baseline.py +138 -0
- loopx/claude_goal_mode/__init__.py +23 -0
- loopx/claude_goal_mode/hooks/goal_policy.py +212 -0
- loopx/claude_goal_mode/hooks/goal_state.py +139 -0
- loopx/claude_goal_mode/mcp/loopx_mcp.py +167 -0
- loopx/claude_goal_mode/scripts/connect.py +103 -0
- loopx/claude_goal_mode/scripts/goalmode_cmd.py +241 -0
- loopx/claude_goal_mode/scripts/install.py +328 -0
- loopx/claude_goal_mode/statusline/goal_status.py +97 -0
- loopx/cli.py +836 -0
- loopx/cli_commands/__init__.py +334 -0
- loopx/cli_commands/_host_thread.py +13 -0
- loopx/cli_commands/agentissue_runner_flow.py +447 -0
- loopx/cli_commands/agents_last_exam.py +160 -0
- loopx/cli_commands/agents_last_exam_baked_input.py +302 -0
- loopx/cli_commands/agents_last_exam_host_codex.py +374 -0
- loopx/cli_commands/agents_last_exam_launch_dry_run.py +372 -0
- loopx/cli_commands/agents_last_exam_local_plan.py +322 -0
- loopx/cli_commands/agents_last_exam_runner_source.py +352 -0
- loopx/cli_commands/agents_last_exam_task_material.py +335 -0
- loopx/cli_commands/agents_last_exam_validation_gate.py +236 -0
- loopx/cli_commands/benchmark_boundary.py +499 -0
- loopx/cli_commands/benchmark_dispatch.py +161 -0
- loopx/cli_commands/benchmark_release_outcome.py +123 -0
- loopx/cli_commands/benchmark_review_lifecycle.py +1275 -0
- loopx/cli_commands/benchmark_run_ledger.py +763 -0
- loopx/cli_commands/benchmark_run_ledger_case_analysis.py +249 -0
- loopx/cli_commands/benchmark_run_ledger_classification.py +45 -0
- loopx/cli_commands/benchmark_run_ledger_maintenance.py +486 -0
- loopx/cli_commands/benchmark_run_ledger_maintenance_registration.py +342 -0
- loopx/cli_commands/benchmark_run_ledger_maintenance_rendering.py +233 -0
- loopx/cli_commands/benchmark_run_ledger_parity.py +92 -0
- loopx/cli_commands/bootstrap_connect.py +238 -0
- loopx/cli_commands/canary.py +707 -0
- loopx/cli_commands/canary_release_qualification.py +79 -0
- loopx/cli_commands/capability.py +96 -0
- loopx/cli_commands/doctor.py +43 -0
- loopx/cli_commands/dreaming.py +143 -0
- loopx/cli_commands/edgebench.py +205 -0
- loopx/cli_commands/evidence_log.py +275 -0
- loopx/cli_commands/explore.py +989 -0
- loopx/cli_commands/explore_planning_commands.py +157 -0
- loopx/cli_commands/extension.py +271 -0
- loopx/cli_commands/first_run_report.py +73 -0
- loopx/cli_commands/goal_channel.py +656 -0
- loopx/cli_commands/handoff_mode.py +158 -0
- loopx/cli_commands/history.py +622 -0
- loopx/cli_commands/host_mode_plan.py +113 -0
- loopx/cli_commands/lark_inbox.py +431 -0
- loopx/cli_commands/lark_kanban.py +629 -0
- loopx/cli_commands/ml_experiment.py +321 -0
- loopx/cli_commands/multi_agent.py +211 -0
- loopx/cli_commands/opencode2_goal_worker.py +217 -0
- loopx/cli_commands/pr_review.py +167 -0
- loopx/cli_commands/presentation.py +218 -0
- loopx/cli_commands/preset.py +96 -0
- loopx/cli_commands/project.py +150 -0
- loopx/cli_commands/project_lifecycle.py +915 -0
- loopx/cli_commands/quota.py +859 -0
- loopx/cli_commands/quota_registration.py +241 -0
- loopx/cli_commands/quota_request.py +113 -0
- loopx/cli_commands/ready_score.py +110 -0
- loopx/cli_commands/registry_admin.py +975 -0
- loopx/cli_commands/registry_admin_configure.py +344 -0
- loopx/cli_commands/registry_admin_peer.py +84 -0
- loopx/cli_commands/registry_authority.py +218 -0
- loopx/cli_commands/review_batch.py +146 -0
- loopx/cli_commands/slash_commands.py +145 -0
- loopx/cli_commands/start_goal.py +251 -0
- loopx/cli_commands/starter.py +175 -0
- loopx/cli_commands/starter_bootstrap.py +179 -0
- loopx/cli_commands/starter_bootstrap_registration.py +198 -0
- loopx/cli_commands/starter_runtime_idle.py +107 -0
- loopx/cli_commands/starter_scheduler.py +207 -0
- loopx/cli_commands/starter_session_runtime.py +152 -0
- loopx/cli_commands/starter_visible_common.py +54 -0
- loopx/cli_commands/starter_visible_driver.py +161 -0
- loopx/cli_commands/starter_visible_pilot.py +278 -0
- loopx/cli_commands/status.py +867 -0
- loopx/cli_commands/status_registration.py +239 -0
- loopx/cli_commands/summary_all.py +222 -0
- loopx/cli_commands/support_control.py +809 -0
- loopx/cli_commands/support_control_registry.py +68 -0
- loopx/cli_commands/support_control_supervisor.py +289 -0
- loopx/cli_commands/task_lease.py +306 -0
- loopx/cli_commands/terminal_bench_adapter.py +717 -0
- loopx/cli_commands/terminal_bench_environment_result.py +1246 -0
- loopx/cli_commands/todo.py +940 -0
- loopx/cli_commands/todo_argument_validation.py +572 -0
- loopx/cli_commands/todo_event.py +114 -0
- loopx/cli_commands/turn.py +804 -0
- loopx/cli_commands/version.py +46 -0
- loopx/cli_commands/worker_bridge.py +659 -0
- loopx/cli_rollout.py +314 -0
- loopx/codex_cli_goal_tui.py +672 -0
- loopx/codex_cli_probe.py +1530 -0
- loopx/codex_cli_probe_markdown.py +935 -0
- loopx/codex_cli_runtime_probe.py +733 -0
- loopx/codex_cli_scheduler.py +564 -0
- loopx/codex_goal_baseline.py +620 -0
- loopx/configuration_catalog.py +617 -0
- loopx/configure_goal.py +1375 -0
- loopx/contract.py +996 -0
- loopx/control_plane/__init__.py +71 -0
- loopx/control_plane/agents/__init__.py +1 -0
- loopx/control_plane/agents/agent_lane_recommendation.py +516 -0
- loopx/control_plane/agents/agent_scope.py +1578 -0
- loopx/control_plane/agents/agent_scope_frontier.py +60 -0
- loopx/control_plane/agents/capability_gate.py +531 -0
- loopx/control_plane/agents/identity.py +140 -0
- loopx/control_plane/agents/legacy_migration.py +169 -0
- loopx/control_plane/agents/management_projection.py +658 -0
- loopx/control_plane/agents/material_frontier.py +608 -0
- loopx/control_plane/agents/material_handoff.py +156 -0
- loopx/control_plane/agents/multi_agent/__init__.py +1 -0
- loopx/control_plane/agents/multi_agent/codex_executable.py +207 -0
- loopx/control_plane/agents/multi_agent/collective_round_ledger.py +387 -0
- loopx/control_plane/agents/multi_agent/contract.py +474 -0
- loopx/control_plane/agents/multi_agent/recipe.py +110 -0
- loopx/control_plane/agents/multi_agent/role_successor.py +297 -0
- loopx/control_plane/agents/multi_agent/runtime_scripts.py +426 -0
- loopx/control_plane/agents/multi_agent/visible_launch_policy.py +149 -0
- loopx/control_plane/agents/multi_agent/visible_wake_scheduler.py +392 -0
- loopx/control_plane/agents/profile.py +216 -0
- loopx/control_plane/agents/runtime_model.py +73 -0
- loopx/control_plane/agents/subagent_activity.py +164 -0
- loopx/control_plane/agents/supervisor.py +544 -0
- loopx/control_plane/agents/supervisor_events.py +462 -0
- loopx/control_plane/agents/supervisor_inject.py +204 -0
- loopx/control_plane/agents/work_mode.py +56 -0
- loopx/control_plane/agents/workspace_guard.py +364 -0
- loopx/control_plane/effect_program.py +644 -0
- loopx/control_plane/goals/__init__.py +1 -0
- loopx/control_plane/goals/active_state_event_projection.py +103 -0
- loopx/control_plane/goals/active_state_metadata.py +47 -0
- loopx/control_plane/goals/active_state_sections.py +58 -0
- loopx/control_plane/goals/configure_goal_service.py +354 -0
- loopx/control_plane/goals/contract_health.py +132 -0
- loopx/control_plane/goals/dreaming.py +152 -0
- loopx/control_plane/goals/global_registry_health.py +199 -0
- loopx/control_plane/goals/global_registry_shadow.py +33 -0
- loopx/control_plane/goals/goal_channel.py +34 -0
- loopx/control_plane/goals/goal_channel_projection.py +560 -0
- loopx/control_plane/goals/goal_frontier/__init__.py +1917 -0
- loopx/control_plane/goals/goal_frontier/ack_policy.py +149 -0
- loopx/control_plane/goals/goal_frontier/outcome_continuity.py +437 -0
- loopx/control_plane/goals/goal_frontier/replan_rules.py +210 -0
- loopx/control_plane/goals/goal_frontier/semantic_history.py +314 -0
- loopx/control_plane/goals/goal_frontier/terminal.py +180 -0
- loopx/control_plane/goals/goal_vision.py +443 -0
- loopx/control_plane/goals/goal_vision_policy.py +36 -0
- loopx/control_plane/goals/goal_vision_state.py +62 -0
- loopx/control_plane/goals/goal_vision_wait.py +290 -0
- loopx/control_plane/goals/path_resolution.py +20 -0
- loopx/control_plane/goals/start_contract.py +206 -0
- loopx/control_plane/goals/vision_checkpoint.py +92 -0
- loopx/control_plane/handoff/__init__.py +1 -0
- loopx/control_plane/handoff/cross_runtime_impl_review.py +311 -0
- loopx/control_plane/handoff/delivery_contract.py +161 -0
- loopx/control_plane/handoff/handoff_runs.py +71 -0
- loopx/control_plane/handoff/project_handoff.py +155 -0
- loopx/control_plane/handoff/review_batch.py +463 -0
- loopx/control_plane/handoff/review_packet_context.py +216 -0
- loopx/control_plane/heartbeat/agent.py +173 -0
- loopx/control_plane/heartbeat/budget.py +66 -0
- loopx/control_plane/heartbeat/builder.py +501 -0
- loopx/control_plane/heartbeat/host.py +64 -0
- loopx/control_plane/heartbeat/rules.py +68 -0
- loopx/control_plane/heartbeat/task_body.py +759 -0
- loopx/control_plane/heartbeat/visible_goal.py +86 -0
- loopx/control_plane/projects/__init__.py +1 -0
- loopx/control_plane/projects/contract.py +25 -0
- loopx/control_plane/projects/registry.py +663 -0
- loopx/control_plane/quota/__init__.py +1 -0
- loopx/control_plane/quota/cli_projection.py +704 -0
- loopx/control_plane/quota/decision_summary.py +431 -0
- loopx/control_plane/quota/effect_program.py +152 -0
- loopx/control_plane/quota/error_codes.py +19 -0
- loopx/control_plane/quota/goal_boundary.py +464 -0
- loopx/control_plane/quota/heartbeat_receipt.py +277 -0
- loopx/control_plane/quota/heartbeat_recommendation.py +718 -0
- loopx/control_plane/quota/host_poll_receipts.py +162 -0
- loopx/control_plane/quota/live_decision.py +142 -0
- loopx/control_plane/quota/monitor_poll.py +786 -0
- loopx/control_plane/quota/policy_constants.py +40 -0
- loopx/control_plane/quota/projection_repair.py +262 -0
- loopx/control_plane/quota/recent_runs.py +210 -0
- loopx/control_plane/quota/scheduler_ack.py +490 -0
- loopx/control_plane/quota/selected_todo_projection.py +139 -0
- loopx/control_plane/quota/settlement.py +437 -0
- loopx/control_plane/quota/settlement_cli.py +246 -0
- loopx/control_plane/quota/settlement_validation.py +64 -0
- loopx/control_plane/quota/settlement_workspace_causality.py +180 -0
- loopx/control_plane/quota/should_run.py +249 -0
- loopx/control_plane/quota/should_run_packet.py +1165 -0
- loopx/control_plane/quota/should_run_prepare.py +675 -0
- loopx/control_plane/quota/slot_accounting.py +1123 -0
- loopx/control_plane/quota/spend_sources.py +11 -0
- loopx/control_plane/quota/stall_repair.py +397 -0
- loopx/control_plane/quota/states.py +29 -0
- loopx/control_plane/quota/task_orchestration.py +448 -0
- loopx/control_plane/quota/task_orchestration_admission.py +497 -0
- loopx/control_plane/quota/turn_envelope.py +889 -0
- loopx/control_plane/quota/usage_summary.py +140 -0
- loopx/control_plane/reward_memory.py +43 -0
- loopx/control_plane/runtime/__init__.py +2 -0
- loopx/control_plane/runtime/active_user_assisted_pilot.py +275 -0
- loopx/control_plane/runtime/agent_scoped_evidence_log.py +435 -0
- loopx/control_plane/runtime/decision_freshness.py +203 -0
- loopx/control_plane/runtime/event_ledger.py +197 -0
- loopx/control_plane/runtime/event_store_migration_bridge.py +196 -0
- loopx/control_plane/runtime/goal_project_route.py +70 -0
- loopx/control_plane/runtime/local_state_write_correctness.py +242 -0
- loopx/control_plane/runtime/promotion_readiness.py +152 -0
- loopx/control_plane/runtime/public_safety.py +120 -0
- loopx/control_plane/runtime/run_artifacts.py +78 -0
- loopx/control_plane/runtime/run_compaction.py +397 -0
- loopx/control_plane/runtime/run_context_retention.py +241 -0
- loopx/control_plane/runtime/run_history.py +132 -0
- loopx/control_plane/runtime/run_index_duplicates.py +205 -0
- loopx/control_plane/runtime/run_index_rebuild.py +263 -0
- loopx/control_plane/runtime/run_ingest_health.py +336 -0
- loopx/control_plane/runtime/runtime_projection_route.py +624 -0
- loopx/control_plane/runtime/runtime_projection_writer.py +98 -0
- loopx/control_plane/runtime/session_runtime.py +339 -0
- loopx/control_plane/runtime/shared_runtime_material_projection.py +332 -0
- loopx/control_plane/runtime/shared_runtime_refresh_projection.py +183 -0
- loopx/control_plane/runtime/stale_latest_run.py +90 -0
- loopx/control_plane/runtime/status_classifications.py +49 -0
- loopx/control_plane/runtime/status_projection_cache.py +235 -0
- loopx/control_plane/runtime/stride_observation.py +144 -0
- loopx/control_plane/runtime/time.py +39 -0
- loopx/control_plane/runtime/trajectory_hygiene.py +149 -0
- loopx/control_plane/runtime/validation_command.py +69 -0
- loopx/control_plane/scheduler/__init__.py +1 -0
- loopx/control_plane/scheduler/ack.py +329 -0
- loopx/control_plane/scheduler/arbitration.py +188 -0
- loopx/control_plane/scheduler/automation_liveness.py +183 -0
- loopx/control_plane/scheduler/execution_context.py +555 -0
- loopx/control_plane/scheduler/external_evidence_observation.py +428 -0
- loopx/control_plane/scheduler/monitor_display.py +143 -0
- loopx/control_plane/scheduler/monitor_poll_policy.py +161 -0
- loopx/control_plane/scheduler/monitor_poll_writeback.py +351 -0
- loopx/control_plane/scheduler/monitor_target.py +64 -0
- loopx/control_plane/scheduler/monitor_todo.py +146 -0
- loopx/control_plane/scheduler/monitor_wait.py +237 -0
- loopx/control_plane/scheduler/scheduler_hint.py +1284 -0
- loopx/control_plane/scheduler/state.py +354 -0
- loopx/control_plane/scheduler/state_transition_rules.py +179 -0
- loopx/control_plane/scheduler/time.py +10 -0
- loopx/control_plane/settlement_driver.py +293 -0
- loopx/control_plane/status/__init__.py +6 -0
- loopx/control_plane/status/active_state_projection.py +105 -0
- loopx/control_plane/status/agent_lane_projection.py +375 -0
- loopx/control_plane/status/attention_projection.py +74 -0
- loopx/control_plane/status/autonomous_replan_projection.py +103 -0
- loopx/control_plane/status/collection.py +140 -0
- loopx/control_plane/status/contract_projection.py +31 -0
- loopx/control_plane/status/dreaming_projection.py +52 -0
- loopx/control_plane/status/goal_attention_projection.py +157 -0
- loopx/control_plane/status/lifecycle_projection.py +110 -0
- loopx/control_plane/status/monitor_display_projection.py +69 -0
- loopx/control_plane/status/registry_health_projection.py +75 -0
- loopx/control_plane/status/run_projection.py +70 -0
- loopx/control_plane/status/runtime_summaries.py +161 -0
- loopx/control_plane/testing/__init__.py +1 -0
- loopx/control_plane/testing/actual_default_model_behavior_portfolio.py +1371 -0
- loopx/control_plane/testing/canary_harness.py +182 -0
- loopx/control_plane/testing/capability_monitor_repair_tool_behavior.py +674 -0
- loopx/control_plane/testing/cli_output_budget.py +807 -0
- loopx/control_plane/testing/cli_output_differential.py +250 -0
- loopx/control_plane/testing/cli_output_semantics.py +87 -0
- loopx/control_plane/testing/control_plane_composition_scenarios.py +225 -0
- loopx/control_plane/testing/decision_replay.py +268 -0
- loopx/control_plane/testing/doubao_model_behavior_actor.py +559 -0
- loopx/control_plane/testing/model_behavior_corpus.py +344 -0
- loopx/control_plane/testing/model_behavior_qualification.py +769 -0
- loopx/control_plane/testing/model_behavior_retained_cases.py +235 -0
- loopx/control_plane/testing/model_tool_behavior.py +536 -0
- loopx/control_plane/testing/onboarding_model_behavior_qualification.py +642 -0
- loopx/control_plane/testing/quota_fixtures.py +208 -0
- loopx/control_plane/testing/quota_should_run_parity.py +57 -0
- loopx/control_plane/testing/release_commit_qualification.py +671 -0
- loopx/control_plane/testing/replan_semantic_action_behavior.py +1302 -0
- loopx/control_plane/testing/scoped_gate_successor_tool_behavior.py +527 -0
- loopx/control_plane/testing/selected_todo_tool_behavior.py +1002 -0
- loopx/control_plane/testing/terminal_settlement_tool_behavior.py +656 -0
- loopx/control_plane/todos/__init__.py +1 -0
- loopx/control_plane/todos/active_state_editing.py +296 -0
- loopx/control_plane/todos/active_state_todo_parser.py +138 -0
- loopx/control_plane/todos/active_state_todos.py +175 -0
- loopx/control_plane/todos/addition.py +103 -0
- loopx/control_plane/todos/claim_visibility.py +253 -0
- loopx/control_plane/todos/completed_archive.py +139 -0
- loopx/control_plane/todos/completion_fence.py +49 -0
- loopx/control_plane/todos/completion_policy.py +153 -0
- loopx/control_plane/todos/completion_validation.py +248 -0
- loopx/control_plane/todos/completion_validation_accountability.py +27 -0
- loopx/control_plane/todos/completion_validation_projection.py +57 -0
- loopx/control_plane/todos/contract.py +1476 -0
- loopx/control_plane/todos/decision_scope.py +554 -0
- loopx/control_plane/todos/deferred_resume.py +546 -0
- loopx/control_plane/todos/durable_completion.py +201 -0
- loopx/control_plane/todos/event_writeback.py +484 -0
- loopx/control_plane/todos/frontier_deadline.py +132 -0
- loopx/control_plane/todos/handoff_gate.py +283 -0
- loopx/control_plane/todos/handoff_mode.py +444 -0
- loopx/control_plane/todos/handoff_note.py +202 -0
- loopx/control_plane/todos/line_update.py +361 -0
- loopx/control_plane/todos/list_projection.py +205 -0
- loopx/control_plane/todos/markdown.py +199 -0
- loopx/control_plane/todos/monitor_metadata.py +88 -0
- loopx/control_plane/todos/mutation_authority.py +299 -0
- loopx/control_plane/todos/projection.py +655 -0
- loopx/control_plane/todos/quota_summary.py +1138 -0
- loopx/control_plane/todos/route_continuation.py +267 -0
- loopx/control_plane/todos/succession_warning.py +174 -0
- loopx/control_plane/todos/summary_item.py +223 -0
- loopx/control_plane/todos/text.py +30 -0
- loopx/control_plane/todos/todo_index.py +226 -0
- loopx/control_plane/todos/todo_summary.py +1458 -0
- loopx/control_plane/todos/unblock_resume.py +326 -0
- loopx/control_plane/todos/user_gate.py +263 -0
- loopx/control_plane/todos/write_hint.py +63 -0
- loopx/control_plane/todos/write_policy.py +135 -0
- loopx/control_plane/turn_driver/__init__.py +85 -0
- loopx/control_plane/turn_driver/codex_cli.py +502 -0
- loopx/control_plane/turn_driver/driver.py +355 -0
- loopx/control_plane/turn_driver/executor.py +1468 -0
- loopx/control_plane/turn_driver/loop_controller.py +669 -0
- loopx/control_plane/turn_driver/settlement.py +318 -0
- loopx/control_plane/turn_driver/transaction.py +375 -0
- loopx/control_plane/work_items/__init__.py +1 -0
- loopx/control_plane/work_items/attention_fields.py +56 -0
- loopx/control_plane/work_items/attention_item.py +77 -0
- loopx/control_plane/work_items/attention_queue.py +322 -0
- loopx/control_plane/work_items/attention_routing.py +213 -0
- loopx/control_plane/work_items/autonomous_candidates.py +135 -0
- loopx/control_plane/work_items/autonomous_replan_ack.py +276 -0
- loopx/control_plane/work_items/autonomous_replan_obligation.py +786 -0
- loopx/control_plane/work_items/backlog_hygiene.py +59 -0
- loopx/control_plane/work_items/capability_monitor_fallback.py +221 -0
- loopx/control_plane/work_items/delivery_batch_scale.py +66 -0
- loopx/control_plane/work_items/delivery_outcome.py +152 -0
- loopx/control_plane/work_items/delivery_signals.py +113 -0
- loopx/control_plane/work_items/execution_obligation.py +235 -0
- loopx/control_plane/work_items/goal_route_hint.py +320 -0
- loopx/control_plane/work_items/interaction_contract.py +1540 -0
- loopx/control_plane/work_items/issue_meta_surface.py +159 -0
- loopx/control_plane/work_items/lifecycle.py +139 -0
- loopx/control_plane/work_items/operator_inbox.py +266 -0
- loopx/control_plane/work_items/outcome_followthrough.py +69 -0
- loopx/control_plane/work_items/primary_action.py +326 -0
- loopx/control_plane/work_items/progress_observation.py +630 -0
- loopx/control_plane/work_items/project_asset.py +675 -0
- loopx/control_plane/work_items/repair_delta.py +693 -0
- loopx/control_plane/work_items/runtime_capability_reentry.py +168 -0
- loopx/control_plane/work_items/semantic_replan_writeback.py +177 -0
- loopx/control_plane/work_items/status_contract.py +49 -0
- loopx/control_plane/work_items/task_graph.py +1046 -0
- loopx/control_plane/work_items/task_lease.py +1254 -0
- loopx/control_plane/work_items/task_lease_settlement.py +422 -0
- loopx/control_plane/work_items/work_lane.py +510 -0
- loopx/control_plane/work_items/work_lane_context.py +161 -0
- loopx/demo.py +247 -0
- loopx/diagnose.py +633 -0
- loopx/doctor.py +1251 -0
- loopx/domain_packs/__init__.py +1 -0
- loopx/domain_packs/issue_fix.py +571 -0
- loopx/domain_packs/ml_experiment.py +854 -0
- loopx/domain_state.py +137 -0
- loopx/dreaming.py +706 -0
- loopx/entrypoint.py +16 -0
- loopx/event_sourced_state.py +981 -0
- loopx/execution_profile.py +286 -0
- loopx/experiments/__init__.py +1 -0
- loopx/experiments/planner_worker/__init__.py +1 -0
- loopx/experiments/planner_worker/contract.py +523 -0
- loopx/experiments/planner_worker/runtime.py +391 -0
- loopx/experiments/planner_worker/traex.py +461 -0
- loopx/explore_graph.py +11 -0
- loopx/extensions/__init__.py +1 -0
- loopx/extensions/bundled.py +28 -0
- loopx/extensions/execution_envelope.py +126 -0
- loopx/extensions/lark/__init__.py +11 -0
- loopx/extensions/lark/event_collector.py +478 -0
- loopx/extensions/lark/event_collector_runtime.py +506 -0
- loopx/extensions/lark/event_inbox.py +454 -0
- loopx/extensions/lark/extension.toml +88 -0
- loopx/extensions/lark/goal_channel.py +44 -0
- loopx/extensions/lark/goal_channel_contracts.py +388 -0
- loopx/extensions/lark/goal_channel_lifecycle.py +218 -0
- loopx/extensions/lark/goal_channel_runtime.py +792 -0
- loopx/extensions/lark/goal_channel_setup.py +805 -0
- loopx/extensions/lark/goal_channel_targets.py +215 -0
- loopx/extensions/lark/goal_channel_transport.py +281 -0
- loopx/extensions/lark/inbox_reactions.py +650 -0
- loopx/extensions/lark/inbox_reply.py +430 -0
- loopx/extensions/lark/presentation/__init__.py +11 -0
- loopx/extensions/lark/presentation/explore_results.py +2276 -0
- loopx/extensions/lark/presentation/explore_singleflight.py +127 -0
- loopx/extensions/lark/presentation/explore_source_guard.py +121 -0
- loopx/extensions/lark/presentation/explore_stage_document.py +703 -0
- loopx/extensions/lark/presentation/explore_visual_integrity.py +122 -0
- loopx/extensions/lark/presentation/explore_visual_readback.py +452 -0
- loopx/extensions/lark/presentation/explore_visual_styles.py +156 -0
- loopx/extensions/lark/presentation/issue_fix_surface.py +612 -0
- loopx/extensions/lark/presentation/kanban.py +2791 -0
- loopx/extensions/lark/presentation/message_card.py +112 -0
- loopx/extensions/lark/presentation/periodic_report.py +261 -0
- loopx/extensions/lark/presentation/projection_rows.py +600 -0
- loopx/extensions/lark/presentation/record_io.py +95 -0
- loopx/extensions/lark/presentation/sync_receipt.py +145 -0
- loopx/extensions/lark/private_json.py +40 -0
- loopx/extensions/lark/provider.py +86 -0
- loopx/extensions/lark/reviewer_notification.py +604 -0
- loopx/extensions/manifest.py +385 -0
- loopx/extensions/openviking_periodic_report/__init__.py +17 -0
- loopx/extensions/openviking_periodic_report/activation.py +173 -0
- loopx/extensions/openviking_periodic_report/extension.toml +17 -0
- loopx/extensions/openviking_periodic_report/provider.py +355 -0
- loopx/extensions/openviking_periodic_report/sink.py +117 -0
- loopx/extensions/openviking_semantic_preference/__init__.py +5 -0
- loopx/extensions/openviking_semantic_preference/extension.toml +16 -0
- loopx/extensions/openviking_semantic_preference/history_export.py +484 -0
- loopx/extensions/openviking_semantic_preference/project_peer.py +68 -0
- loopx/extensions/openviking_semantic_preference/provider.py +312 -0
- loopx/extensions/presentation.py +979 -0
- loopx/extensions/process_runtime.py +204 -0
- loopx/extensions/readiness.py +168 -0
- loopx/extensions/runtime.py +931 -0
- loopx/extensions/scaffold.py +335 -0
- loopx/feedback.py +581 -0
- loopx/file_lock.py +382 -0
- loopx/global_registry.py +842 -0
- loopx/global_risks.py +970 -0
- loopx/global_todos.py +568 -0
- loopx/handoff_budget.py +28 -0
- loopx/heartbeat_prequota.py +80 -0
- loopx/heartbeat_prompt.py +159 -0
- loopx/help_surface.py +516 -0
- loopx/history.py +1507 -0
- loopx/host_loop_activation.py +1311 -0
- loopx/host_mode_planner.py +991 -0
- loopx/install_contract.py +1 -0
- loopx/interface_budget.py +196 -0
- loopx/long_task_cadence.py +208 -0
- loopx/materials.py +185 -0
- loopx/ml_experiment.py +3 -0
- loopx/onboarding.py +214 -0
- loopx/opencode2_goal_mode/README.md +81 -0
- loopx/opencode2_goal_mode/__init__.py +9 -0
- loopx/opencode2_goal_mode/opencode2-goal-worker.mjs +1018 -0
- loopx/opencode_goal_mode/README.md +99 -0
- loopx/opencode_goal_mode/__init__.py +13 -0
- loopx/opencode_goal_mode/goal-bridge-runtime.mjs +858 -0
- loopx/opencode_goal_mode/loopx-goal.js +8 -0
- loopx/operator_gate.py +420 -0
- loopx/orchestration.py +127 -0
- loopx/paths.py +59 -0
- loopx/pi_goal_mode/README.md +67 -0
- loopx/pi_goal_mode/__init__.py +13 -0
- loopx/pi_goal_mode/loopx-goal.ts +254 -0
- loopx/pi_goal_mode/pi-goal-loop-runtime.mjs +574 -0
- loopx/pr_review.py +1206 -0
- loopx/presentation/__init__.py +1 -0
- loopx/presentation/explore_views.py +1334 -0
- loopx/presentation/markdown.py +61 -0
- loopx/presentation/projection_source_reconcile.py +140 -0
- loopx/presentation/public_safety.py +42 -0
- loopx/presentation/renderers/__init__.py +17 -0
- loopx/presentation/renderers/goal_channel_html.py +269 -0
- loopx/presentation/renderers/periodic_report_html.py +786 -0
- loopx/presentation/renderers/periodic_report_markdown.py +184 -0
- loopx/presentation/renderers/quota_event_markdown.py +116 -0
- loopx/presentation/renderers/quota_markdown.py +1112 -0
- loopx/presentation/renderers/status_markdown.py +1570 -0
- loopx/presentation/renderers/trajectory_hygiene_markdown.py +39 -0
- loopx/presentation/renderers/turn_envelope_markdown.py +33 -0
- loopx/presentation/sinks/__init__.py +5 -0
- loopx/presentation/sinks/openviking_periodic_report.py +7 -0
- loopx/presentation/static_site.py +691 -0
- loopx/presets.py +369 -0
- loopx/project_alias.py +217 -0
- loopx/project_map.py +589 -0
- loopx/project_prompt.py +1153 -0
- loopx/project_skill_cli.py +125 -0
- loopx/project_skill_delivery.py +470 -0
- loopx/project_uninstall.py +462 -0
- loopx/promotion_gate.py +197 -0
- loopx/quota.py +1197 -0
- loopx/ready_score.py +413 -0
- loopx/registry.py +621 -0
- loopx/registry_writability.py +64 -0
- loopx/release_candidate.py +148 -0
- loopx/release_manifest.py +316 -0
- loopx/repository_identity.py +100 -0
- loopx/review_packet.py +1024 -0
- loopx/rollout_event_log.py +505 -0
- loopx/runtime.py +112 -0
- loopx/self_update.py +750 -0
- loopx/session_runtime.py +418 -0
- loopx/skill_install_readback.py +500 -0
- loopx/slash_command_install.py +1393 -0
- loopx/slash_commands.py +264 -0
- loopx/state_backup.py +573 -0
- loopx/state_migration.py +350 -0
- loopx/state_projection.py +809 -0
- loopx/state_refresh.py +1416 -0
- loopx/status.py +1383 -0
- loopx/status_server.py +935 -0
- loopx/summary_all.py +725 -0
- loopx/terminal_bench_agent.py +2056 -0
- loopx/thread_agent_binding.py +408 -0
- loopx/todo_followups.py +168 -0
- loopx/todo_suggestion_prompt.py +204 -0
- loopx/todos.py +2229 -0
- loopx/turn_identity.py +17 -0
- loopx/upgrade.py +1083 -0
- loopx/visible_governance.py +667 -0
- loopx/visible_multi_agent_launcher.py +1253 -0
- loopx/visible_multi_agent_tmux.py +429 -0
- loopx/worker_bridge.py +1574 -0
- loopx-0.4.8.dist-info/METADATA +708 -0
- loopx-0.4.8.dist-info/RECORD +811 -0
- loopx-0.4.8.dist-info/WHEEL +5 -0
- loopx-0.4.8.dist-info/entry_points.txt +5 -0
- loopx-0.4.8.dist-info/licenses/LICENSE +202 -0
- loopx-0.4.8.dist-info/licenses/LICENSE-MIT +21 -0
- loopx-0.4.8.dist-info/licenses/NOTICE +6 -0
- loopx-0.4.8.dist-info/top_level.txt +1 -0
loopx/configure_goal.py
ADDED
|
@@ -0,0 +1,1375 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import shlex
|
|
5
|
+
import shutil
|
|
6
|
+
from collections.abc import Mapping
|
|
7
|
+
from copy import deepcopy
|
|
8
|
+
from datetime import datetime
|
|
9
|
+
from pathlib import Path, PurePosixPath
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from .boundary_authority import (
|
|
13
|
+
build_checkpointed_boundary_authority_entry,
|
|
14
|
+
checkpointed_boundary_authority_summary,
|
|
15
|
+
)
|
|
16
|
+
from .agent_registry import normalize_registered_agents
|
|
17
|
+
from .capabilities.change_quality.policy import (
|
|
18
|
+
CHANGE_QUALITY_POLICY_SCHEMA_VERSION,
|
|
19
|
+
change_quality_goal_policy_summary,
|
|
20
|
+
)
|
|
21
|
+
from .control_plane import compact_control_plane_policy, control_plane_policy_summary
|
|
22
|
+
from .configuration_catalog import build_goal_configuration_catalog
|
|
23
|
+
from .explore_graph import compact_explore_graph_policy
|
|
24
|
+
from .orchestration import (
|
|
25
|
+
DEFAULT_ORCHESTRATION_MODE,
|
|
26
|
+
EXPLORE_HARNESS_PROFILES,
|
|
27
|
+
MULTI_SUBAGENT_ORCHESTRATION_MODE,
|
|
28
|
+
compact_orchestration_policy,
|
|
29
|
+
compact_peer_task_coordination_policy,
|
|
30
|
+
orchestration_policy_summary,
|
|
31
|
+
)
|
|
32
|
+
from .quota import goal_quota_config
|
|
33
|
+
from .registry import atomic_write_json, read_json, registry_goals
|
|
34
|
+
from .control_plane.reward_memory import (
|
|
35
|
+
reward_memory_goal_policy,
|
|
36
|
+
reward_memory_goal_policy_summary,
|
|
37
|
+
)
|
|
38
|
+
from .control_plane.todos.contract import normalize_todo_claimed_by
|
|
39
|
+
from .control_plane.todos.mutation_authority import (
|
|
40
|
+
normalize_todo_lifecycle_authority,
|
|
41
|
+
)
|
|
42
|
+
from .execution_profile import (
|
|
43
|
+
compact_execution_profile,
|
|
44
|
+
execution_profile_with_turn_granularity,
|
|
45
|
+
normalize_turn_granularity,
|
|
46
|
+
)
|
|
47
|
+
from .control_plane.agents.legacy_migration import (
|
|
48
|
+
completed_peer_agent_runtime_migration,
|
|
49
|
+
legacy_agent_hierarchy_present,
|
|
50
|
+
migrate_coordination_to_peer_v1,
|
|
51
|
+
peer_agent_runtime_migration_completed,
|
|
52
|
+
peer_agent_runtime_migration_id,
|
|
53
|
+
)
|
|
54
|
+
from .control_plane.agents.profile import normalize_agent_profile
|
|
55
|
+
from .control_plane.agents.work_mode import normalize_agent_work_modes
|
|
56
|
+
from .control_plane.agents.runtime_model import (
|
|
57
|
+
AgentRuntimeModel,
|
|
58
|
+
agent_runtime_model_for_goal,
|
|
59
|
+
)
|
|
60
|
+
from .control_plane.agents.supervisor import normalize_peer_supervisor
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
WAITING_ON_CHOICES = (
|
|
64
|
+
"codex",
|
|
65
|
+
"user_or_controller",
|
|
66
|
+
"controller",
|
|
67
|
+
"external_evidence",
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
MULTI_SUBAGENT_FEATURE_CHOICES = ("off", "enabled")
|
|
71
|
+
DEFAULT_MULTI_SUBAGENT_MAX_CHILDREN = 2
|
|
72
|
+
AGENT_MODEL_CHOICES = tuple(model.value for model in AgentRuntimeModel)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _control_plane(goal: dict[str, Any]) -> dict[str, Any]:
|
|
76
|
+
value = goal.get("control_plane")
|
|
77
|
+
return value if isinstance(value, dict) else {}
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _mutable_control_plane(goal: dict[str, Any]) -> dict[str, Any]:
|
|
81
|
+
control_plane = _control_plane(goal)
|
|
82
|
+
goal["control_plane"] = control_plane
|
|
83
|
+
return control_plane
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _reviewer_notification_config_summary(goal: dict[str, Any]) -> dict[str, bool]:
|
|
87
|
+
control_plane = _control_plane(goal)
|
|
88
|
+
issue_fix = (
|
|
89
|
+
control_plane.get("issue_fix")
|
|
90
|
+
if isinstance(control_plane.get("issue_fix"), dict)
|
|
91
|
+
else {}
|
|
92
|
+
)
|
|
93
|
+
reviewer_notification = (
|
|
94
|
+
issue_fix.get("reviewer_notification")
|
|
95
|
+
if isinstance(issue_fix.get("reviewer_notification"), dict)
|
|
96
|
+
else {}
|
|
97
|
+
)
|
|
98
|
+
return {
|
|
99
|
+
"enabled": reviewer_notification.get("enabled") is True,
|
|
100
|
+
"config_pointer_registered": bool(reviewer_notification.get("config_path")),
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _lark_event_inbox_config_summary(goal: dict[str, Any]) -> dict[str, bool]:
|
|
105
|
+
control_plane = _control_plane(goal)
|
|
106
|
+
inbox = (
|
|
107
|
+
control_plane.get("lark_event_inbox")
|
|
108
|
+
if isinstance(control_plane.get("lark_event_inbox"), dict)
|
|
109
|
+
else {}
|
|
110
|
+
)
|
|
111
|
+
return {
|
|
112
|
+
"enabled": inbox.get("enabled") is True,
|
|
113
|
+
"config_pointer_registered": bool(inbox.get("config_path")),
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _lark_kanban_heartbeat_config_summary(goal: dict[str, Any]) -> dict[str, bool]:
|
|
118
|
+
control_plane = _control_plane(goal)
|
|
119
|
+
lark_kanban = (
|
|
120
|
+
control_plane.get("lark_kanban")
|
|
121
|
+
if isinstance(control_plane.get("lark_kanban"), dict)
|
|
122
|
+
else {}
|
|
123
|
+
)
|
|
124
|
+
return {"enabled": lark_kanban.get("heartbeat_sync_enabled") is True}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def _local_private_config_path(
|
|
128
|
+
value: str | None, *, label: str = "local-private config"
|
|
129
|
+
) -> str | None:
|
|
130
|
+
if value is None:
|
|
131
|
+
return None
|
|
132
|
+
text = str(value).strip().replace("\\", "/")
|
|
133
|
+
path = PurePosixPath(text)
|
|
134
|
+
if (
|
|
135
|
+
not text
|
|
136
|
+
or path.is_absolute()
|
|
137
|
+
or ".." in path.parts
|
|
138
|
+
or len(path.parts) < 3
|
|
139
|
+
or path.parts[:2] != (".loopx", "config")
|
|
140
|
+
or path.suffix != ".json"
|
|
141
|
+
):
|
|
142
|
+
raise ValueError(
|
|
143
|
+
f"{label} must be a repo-relative JSON path "
|
|
144
|
+
"under .loopx/config/"
|
|
145
|
+
)
|
|
146
|
+
return path.as_posix()
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
def _now_iso() -> str:
|
|
150
|
+
return datetime.now().astimezone().replace(microsecond=0).isoformat()
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def _positive_number(value: float | None, *, field: str) -> float | None:
|
|
154
|
+
if value is None:
|
|
155
|
+
return None
|
|
156
|
+
if value <= 0:
|
|
157
|
+
raise ValueError(f"{field} must be greater than 0")
|
|
158
|
+
return float(value)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
def _non_negative_number(value: float | None, *, field: str) -> float | None:
|
|
162
|
+
if value is None:
|
|
163
|
+
return None
|
|
164
|
+
if value < 0:
|
|
165
|
+
raise ValueError(f"{field} must be greater than or equal to 0")
|
|
166
|
+
return float(value)
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
def _non_negative_int(value: int | None, *, field: str) -> int | None:
|
|
170
|
+
if value is None:
|
|
171
|
+
return None
|
|
172
|
+
if value < 0:
|
|
173
|
+
raise ValueError(f"{field} must be greater than or equal to 0")
|
|
174
|
+
return int(value)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _clean_domains(values: list[str] | None) -> list[str] | None:
|
|
178
|
+
if values is None:
|
|
179
|
+
return None
|
|
180
|
+
domains: list[str] = []
|
|
181
|
+
for value in values:
|
|
182
|
+
for part in str(value).split(","):
|
|
183
|
+
domain = part.strip()
|
|
184
|
+
if domain and domain not in domains:
|
|
185
|
+
domains.append(domain)
|
|
186
|
+
return domains
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def _clean_registered_agents(values: list[str] | None) -> list[str] | None:
|
|
190
|
+
if values is None:
|
|
191
|
+
return None
|
|
192
|
+
agents: list[str] = []
|
|
193
|
+
for value in values:
|
|
194
|
+
for part in str(value).split(","):
|
|
195
|
+
raw_agent = part.strip()
|
|
196
|
+
if not raw_agent:
|
|
197
|
+
continue
|
|
198
|
+
agent = normalize_todo_claimed_by(raw_agent)
|
|
199
|
+
if not agent:
|
|
200
|
+
raise ValueError("registered agents must be public-safe tokens such as codex-main-control")
|
|
201
|
+
if agent not in agents:
|
|
202
|
+
agents.append(agent)
|
|
203
|
+
return agents
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _clean_write_scope(values: list[str] | None) -> list[str] | None:
|
|
207
|
+
if values is None:
|
|
208
|
+
return None
|
|
209
|
+
scopes: list[str] = []
|
|
210
|
+
for value in values:
|
|
211
|
+
for part in str(value).split(","):
|
|
212
|
+
scope = part.strip()
|
|
213
|
+
if scope and scope not in scopes:
|
|
214
|
+
scopes.append(scope)
|
|
215
|
+
return scopes
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def _settings_summary(goal: dict[str, Any]) -> dict[str, Any]:
|
|
219
|
+
quota = goal_quota_config(goal)
|
|
220
|
+
control_plane = compact_control_plane_policy(goal.get("control_plane"))
|
|
221
|
+
orchestration = compact_orchestration_policy(goal.get("spawn_policy"))
|
|
222
|
+
coordination = goal.get("coordination") if isinstance(goal.get("coordination"), dict) else {}
|
|
223
|
+
agent_model = agent_runtime_model_for_goal(goal)
|
|
224
|
+
registered_agents = normalize_registered_agents(
|
|
225
|
+
coordination.get("registered_agents")
|
|
226
|
+
)
|
|
227
|
+
summary = {
|
|
228
|
+
"execution_profile": compact_execution_profile(goal.get("execution_profile")),
|
|
229
|
+
"quota": {
|
|
230
|
+
"compute": quota.get("compute"),
|
|
231
|
+
"window_hours": quota.get("window_hours"),
|
|
232
|
+
},
|
|
233
|
+
"control_plane": control_plane,
|
|
234
|
+
"issue_fix_reviewer_notification": _reviewer_notification_config_summary(
|
|
235
|
+
goal
|
|
236
|
+
),
|
|
237
|
+
"lark_event_inbox": _lark_event_inbox_config_summary(goal),
|
|
238
|
+
"lark_kanban_heartbeat_sync": _lark_kanban_heartbeat_config_summary(goal),
|
|
239
|
+
"reward_memory": reward_memory_goal_policy_summary(goal),
|
|
240
|
+
"change_quality_qualification": change_quality_goal_policy_summary(goal),
|
|
241
|
+
"explore_graph": compact_explore_graph_policy(goal.get("explore_graph")),
|
|
242
|
+
"orchestration": orchestration,
|
|
243
|
+
"waiting_on": goal.get("waiting_on"),
|
|
244
|
+
"write_scope": _clean_write_scope(coordination.get("write_scope") or []) or [],
|
|
245
|
+
"checkpointed_boundary_authority": checkpointed_boundary_authority_summary(coordination),
|
|
246
|
+
"registered_agents": registered_agents,
|
|
247
|
+
"peer_task_coordination": deepcopy(
|
|
248
|
+
compact_peer_task_coordination_policy(coordination)
|
|
249
|
+
),
|
|
250
|
+
"todo_lifecycle_authority": normalize_todo_lifecycle_authority(
|
|
251
|
+
coordination.get("todo_lifecycle_authority"),
|
|
252
|
+
registered_agents=registered_agents,
|
|
253
|
+
),
|
|
254
|
+
"agent_profiles": deepcopy(
|
|
255
|
+
coordination.get("agent_profiles")
|
|
256
|
+
if isinstance(coordination.get("agent_profiles"), dict)
|
|
257
|
+
else {}
|
|
258
|
+
),
|
|
259
|
+
"agent_work_modes": normalize_agent_work_modes(
|
|
260
|
+
coordination.get("agent_work_modes"),
|
|
261
|
+
registered_agents=registered_agents,
|
|
262
|
+
),
|
|
263
|
+
"agent_model": agent_model.value,
|
|
264
|
+
"configured_agent_model": coordination.get("agent_model"),
|
|
265
|
+
"legacy_hierarchy_present": legacy_agent_hierarchy_present(goal),
|
|
266
|
+
"peer_runtime_migration": deepcopy(
|
|
267
|
+
completed_peer_agent_runtime_migration(goal)
|
|
268
|
+
),
|
|
269
|
+
"supervisor": deepcopy(
|
|
270
|
+
normalize_peer_supervisor(
|
|
271
|
+
coordination.get("supervisor"),
|
|
272
|
+
registered_agents=normalize_registered_agents(
|
|
273
|
+
coordination.get("registered_agents")
|
|
274
|
+
),
|
|
275
|
+
)
|
|
276
|
+
),
|
|
277
|
+
}
|
|
278
|
+
return summary
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def _multi_subagent_feature_status(orchestration: dict[str, Any]) -> str:
|
|
282
|
+
if (
|
|
283
|
+
orchestration.get("mode") == MULTI_SUBAGENT_ORCHESTRATION_MODE
|
|
284
|
+
and orchestration.get("spawn_allowed") is True
|
|
285
|
+
and int(orchestration.get("max_children") or 0) > 0
|
|
286
|
+
):
|
|
287
|
+
return "enabled"
|
|
288
|
+
return "off"
|
|
289
|
+
|
|
290
|
+
|
|
291
|
+
def _changed_fields(before: dict[str, Any], after: dict[str, Any]) -> list[str]:
|
|
292
|
+
changed: list[str] = []
|
|
293
|
+
for group, before_value in before.items():
|
|
294
|
+
after_value = after.get(group)
|
|
295
|
+
if before_value != after_value:
|
|
296
|
+
changed.append(group)
|
|
297
|
+
return changed
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def _heartbeat_scope_hint(
|
|
301
|
+
agent_id: str,
|
|
302
|
+
) -> str:
|
|
303
|
+
del agent_id
|
|
304
|
+
return "peer task claims, leases, evidence, and bounded delivery"
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def _build_heartbeat_prompt_migration(
|
|
308
|
+
*,
|
|
309
|
+
goal_id: str,
|
|
310
|
+
changed_fields: list[str],
|
|
311
|
+
after: dict[str, Any],
|
|
312
|
+
migration_id: str | None = None,
|
|
313
|
+
migration_acknowledged: bool = False,
|
|
314
|
+
) -> dict[str, Any] | None:
|
|
315
|
+
if not migration_id and not any(
|
|
316
|
+
field in changed_fields
|
|
317
|
+
for field in (
|
|
318
|
+
"registered_agents",
|
|
319
|
+
"configured_agent_model",
|
|
320
|
+
"legacy_hierarchy_present",
|
|
321
|
+
)
|
|
322
|
+
):
|
|
323
|
+
return None
|
|
324
|
+
registered_agents = [
|
|
325
|
+
str(agent).strip()
|
|
326
|
+
for agent in after.get("registered_agents") or []
|
|
327
|
+
if str(agent).strip()
|
|
328
|
+
]
|
|
329
|
+
if not registered_agents:
|
|
330
|
+
return None
|
|
331
|
+
agent_model = AgentRuntimeModel.PEER_V1.value
|
|
332
|
+
ordered_agents = list(registered_agents)
|
|
333
|
+
commands = []
|
|
334
|
+
for agent in ordered_agents:
|
|
335
|
+
scope = _heartbeat_scope_hint(agent)
|
|
336
|
+
command = {
|
|
337
|
+
"agent_id": agent,
|
|
338
|
+
"command": (
|
|
339
|
+
"loopx heartbeat-prompt --thin "
|
|
340
|
+
f"--goal-id {shlex.quote(goal_id)} "
|
|
341
|
+
f"--agent-id {shlex.quote(agent)} "
|
|
342
|
+
f"--agent-scope {shlex.quote(scope)}"
|
|
343
|
+
),
|
|
344
|
+
}
|
|
345
|
+
commands.append(command)
|
|
346
|
+
payload = {
|
|
347
|
+
"schema_version": "heartbeat_prompt_migration_v1",
|
|
348
|
+
"agent_model": agent_model,
|
|
349
|
+
"migration_id": migration_id,
|
|
350
|
+
"host_update_idempotency_key": migration_id,
|
|
351
|
+
"status": "completed" if migration_acknowledged else "required",
|
|
352
|
+
"reason": (
|
|
353
|
+
"the host automation update was acknowledged and the registry hard cut completed"
|
|
354
|
+
if migration_acknowledged
|
|
355
|
+
else "coordination agent identity changed; installed heartbeats should be "
|
|
356
|
+
"regenerated with identity-aware prompt args"
|
|
357
|
+
),
|
|
358
|
+
"action": (
|
|
359
|
+
"none; this migration id is complete and will not be projected again"
|
|
360
|
+
if migration_acknowledged
|
|
361
|
+
else "update each installed host automation once using migration_id as the "
|
|
362
|
+
"idempotency key, then run completion_command"
|
|
363
|
+
),
|
|
364
|
+
"commands": [] if migration_acknowledged else commands,
|
|
365
|
+
}
|
|
366
|
+
if migration_id and not migration_acknowledged:
|
|
367
|
+
payload["completion_command"] = (
|
|
368
|
+
"loopx configure-goal "
|
|
369
|
+
f"--goal-id {shlex.quote(goal_id)} "
|
|
370
|
+
f"--ack-automation-prompt-migration {shlex.quote(migration_id)} --execute"
|
|
371
|
+
)
|
|
372
|
+
return payload
|
|
373
|
+
|
|
374
|
+
|
|
375
|
+
def _build_supervisor_prompt_setup(
|
|
376
|
+
*,
|
|
377
|
+
goal_id: str,
|
|
378
|
+
changed_fields: list[str],
|
|
379
|
+
after: dict[str, Any],
|
|
380
|
+
) -> dict[str, Any] | None:
|
|
381
|
+
if "supervisor" not in changed_fields:
|
|
382
|
+
return None
|
|
383
|
+
supervisor = after.get("supervisor")
|
|
384
|
+
if not isinstance(supervisor, dict):
|
|
385
|
+
return {
|
|
386
|
+
"schema_version": "supervisor_prompt_setup_v0",
|
|
387
|
+
"status": "disabled",
|
|
388
|
+
"command": None,
|
|
389
|
+
}
|
|
390
|
+
agent_id = str(supervisor.get("agent_id") or "")
|
|
391
|
+
return {
|
|
392
|
+
"schema_version": "supervisor_prompt_setup_v0",
|
|
393
|
+
"status": "ready",
|
|
394
|
+
"agent_id": agent_id,
|
|
395
|
+
"command": (
|
|
396
|
+
"loopx supervisor-prompt "
|
|
397
|
+
f"--goal-id {shlex.quote(goal_id)} "
|
|
398
|
+
f"--agent-id {shlex.quote(agent_id)}"
|
|
399
|
+
),
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
def configure_goal(
|
|
404
|
+
*,
|
|
405
|
+
registry_path: Path,
|
|
406
|
+
goal_id: str,
|
|
407
|
+
quota_compute: float | None = None,
|
|
408
|
+
quota_window_hours: float | None = None,
|
|
409
|
+
execution_turn_granularity: str | None = None,
|
|
410
|
+
self_repair_enabled: bool | None = None,
|
|
411
|
+
self_repair_health: bool | None = None,
|
|
412
|
+
self_repair_waiting_projection: bool | None = None,
|
|
413
|
+
change_quality_enabled: bool | None = None,
|
|
414
|
+
change_quality_safe_fix: bool | None = None,
|
|
415
|
+
change_quality_strict_receipt: bool | None = None,
|
|
416
|
+
multi_subagent_feature: str | None = None,
|
|
417
|
+
orchestration_mode: str | None = None,
|
|
418
|
+
spawn_allowed: bool | None = None,
|
|
419
|
+
max_children: int | None = None,
|
|
420
|
+
allowed_domains: list[str] | None = None,
|
|
421
|
+
clear_allowed_domains: bool = False,
|
|
422
|
+
explore_harness_enabled: bool | None = None,
|
|
423
|
+
explore_harness_profile: str | None = None,
|
|
424
|
+
clear_explore_harness_profile: bool = False,
|
|
425
|
+
explore_graph_enabled: bool | None = None,
|
|
426
|
+
registered_agents: list[str] | None = None,
|
|
427
|
+
clear_registered_agents: bool = False,
|
|
428
|
+
peer_task_coordinator: str | None = None,
|
|
429
|
+
clear_peer_task_coordinator: bool = False,
|
|
430
|
+
agent_profiles: list[dict[str, Any]] | None = None,
|
|
431
|
+
clear_agent_profiles: list[str] | None = None,
|
|
432
|
+
agent_work_modes: dict[str, str] | None = None,
|
|
433
|
+
clear_agent_work_modes: list[str] | None = None,
|
|
434
|
+
todo_lifecycle_authority: list[dict[str, Any]] | None = None,
|
|
435
|
+
clear_todo_lifecycle_authority: list[str] | None = None,
|
|
436
|
+
agent_model: str | None = None,
|
|
437
|
+
automation_prompt_migration_ack: str | None = None,
|
|
438
|
+
supervisor_agent: str | None = None,
|
|
439
|
+
supervised_agents: list[str] | None = None,
|
|
440
|
+
clear_supervisor: bool = False,
|
|
441
|
+
write_scope: list[str] | None = None,
|
|
442
|
+
replace_write_scope: bool = False,
|
|
443
|
+
clear_write_scope: bool = False,
|
|
444
|
+
waiting_on: str | None = None,
|
|
445
|
+
clear_waiting_on: bool = False,
|
|
446
|
+
boundary_authority_scopes: list[str] | None = None,
|
|
447
|
+
boundary_authority_source: str | None = None,
|
|
448
|
+
boundary_authority_decision_id: str | None = None,
|
|
449
|
+
boundary_authority_recorded_at: str | None = None,
|
|
450
|
+
boundary_authority_expires_at: str | None = None,
|
|
451
|
+
clear_boundary_authority: bool = False,
|
|
452
|
+
issue_fix_reviewer_notification_config: str | None = None,
|
|
453
|
+
clear_issue_fix_reviewer_notification_config: bool = False,
|
|
454
|
+
lark_event_inbox_config: str | None = None,
|
|
455
|
+
clear_lark_event_inbox_config: bool = False,
|
|
456
|
+
lark_kanban_heartbeat_sync: bool | None = None,
|
|
457
|
+
reward_memory_config: str | None = None,
|
|
458
|
+
reward_memory_agents: list[str] | None = None,
|
|
459
|
+
clear_reward_memory_config: bool = False,
|
|
460
|
+
execute: bool = False,
|
|
461
|
+
) -> dict[str, Any]:
|
|
462
|
+
if not registry_path.exists():
|
|
463
|
+
raise FileNotFoundError(f"registry file does not exist: {registry_path}")
|
|
464
|
+
if execution_turn_granularity is not None:
|
|
465
|
+
execution_turn_granularity = normalize_turn_granularity(
|
|
466
|
+
execution_turn_granularity
|
|
467
|
+
)
|
|
468
|
+
if clear_allowed_domains and allowed_domains:
|
|
469
|
+
raise ValueError("--clear-allowed-domains cannot be combined with --allowed-domain")
|
|
470
|
+
if clear_explore_harness_profile and explore_harness_profile:
|
|
471
|
+
raise ValueError(
|
|
472
|
+
"--clear-explore-harness-profile cannot be combined with --explore-harness-profile"
|
|
473
|
+
)
|
|
474
|
+
if clear_registered_agents and registered_agents:
|
|
475
|
+
raise ValueError("--clear-registered-agents cannot be combined with --registered-agent")
|
|
476
|
+
if clear_peer_task_coordinator and peer_task_coordinator:
|
|
477
|
+
raise ValueError(
|
|
478
|
+
"--clear-peer-task-coordinator cannot be combined with "
|
|
479
|
+
"--peer-task-coordinator"
|
|
480
|
+
)
|
|
481
|
+
if agent_model is not None:
|
|
482
|
+
agent_model = str(agent_model).strip().lower()
|
|
483
|
+
if agent_model not in AGENT_MODEL_CHOICES:
|
|
484
|
+
raise ValueError("--agent-model must be one of: " + ", ".join(AGENT_MODEL_CHOICES))
|
|
485
|
+
if automation_prompt_migration_ack is not None:
|
|
486
|
+
automation_prompt_migration_ack = str(
|
|
487
|
+
automation_prompt_migration_ack
|
|
488
|
+
).strip()
|
|
489
|
+
if not automation_prompt_migration_ack:
|
|
490
|
+
raise ValueError("--ack-automation-prompt-migration requires a migration id")
|
|
491
|
+
if registered_agents is not None or clear_registered_agents:
|
|
492
|
+
raise ValueError(
|
|
493
|
+
"--ack-automation-prompt-migration cannot change registered agents; "
|
|
494
|
+
"complete the runtime cutover first, then update the peer set separately"
|
|
495
|
+
)
|
|
496
|
+
if clear_supervisor and (supervisor_agent or supervised_agents):
|
|
497
|
+
raise ValueError(
|
|
498
|
+
"--clear-supervisor cannot be combined with --supervisor-agent or "
|
|
499
|
+
"--supervised-agent"
|
|
500
|
+
)
|
|
501
|
+
if supervised_agents and not supervisor_agent:
|
|
502
|
+
raise ValueError("--supervised-agent requires --supervisor-agent")
|
|
503
|
+
if clear_write_scope and write_scope:
|
|
504
|
+
raise ValueError("--clear-write-scope cannot be combined with --write-scope")
|
|
505
|
+
if replace_write_scope and not write_scope:
|
|
506
|
+
raise ValueError("--replace-write-scope requires --write-scope")
|
|
507
|
+
if clear_write_scope and replace_write_scope:
|
|
508
|
+
raise ValueError("--clear-write-scope cannot be combined with --replace-write-scope")
|
|
509
|
+
if clear_waiting_on and waiting_on:
|
|
510
|
+
raise ValueError("--clear-waiting-on cannot be combined with --waiting-on")
|
|
511
|
+
adding_boundary_authority = any(
|
|
512
|
+
value
|
|
513
|
+
for value in (
|
|
514
|
+
boundary_authority_scopes,
|
|
515
|
+
boundary_authority_source,
|
|
516
|
+
boundary_authority_decision_id,
|
|
517
|
+
boundary_authority_recorded_at,
|
|
518
|
+
boundary_authority_expires_at,
|
|
519
|
+
)
|
|
520
|
+
)
|
|
521
|
+
if clear_boundary_authority and adding_boundary_authority:
|
|
522
|
+
raise ValueError("--clear-boundary-authority cannot be combined with boundary authority fields")
|
|
523
|
+
if (
|
|
524
|
+
clear_issue_fix_reviewer_notification_config
|
|
525
|
+
and issue_fix_reviewer_notification_config
|
|
526
|
+
):
|
|
527
|
+
raise ValueError(
|
|
528
|
+
"--clear-issue-fix-reviewer-notification-config cannot be combined "
|
|
529
|
+
"with --issue-fix-reviewer-notification-config"
|
|
530
|
+
)
|
|
531
|
+
if clear_lark_event_inbox_config and lark_event_inbox_config:
|
|
532
|
+
raise ValueError(
|
|
533
|
+
"--clear-lark-event-inbox-config cannot be combined with "
|
|
534
|
+
"--lark-event-inbox-config"
|
|
535
|
+
)
|
|
536
|
+
if clear_reward_memory_config and (
|
|
537
|
+
reward_memory_config or reward_memory_agents
|
|
538
|
+
):
|
|
539
|
+
raise ValueError(
|
|
540
|
+
"--clear-reward-memory-config cannot be combined with "
|
|
541
|
+
"--reward-memory-config or --reward-memory-agent"
|
|
542
|
+
)
|
|
543
|
+
if waiting_on and waiting_on not in WAITING_ON_CHOICES:
|
|
544
|
+
raise ValueError("--waiting-on must be one of: " + ", ".join(WAITING_ON_CHOICES))
|
|
545
|
+
if multi_subagent_feature is not None and multi_subagent_feature not in MULTI_SUBAGENT_FEATURE_CHOICES:
|
|
546
|
+
raise ValueError("--multi-subagent-feature must be one of: " + ", ".join(MULTI_SUBAGENT_FEATURE_CHOICES))
|
|
547
|
+
if multi_subagent_feature is not None and (orchestration_mode is not None or spawn_allowed is not None):
|
|
548
|
+
raise ValueError(
|
|
549
|
+
"--multi-subagent-feature cannot be combined with --orchestration-mode or --spawn-allowed; "
|
|
550
|
+
"use --max-children/--allowed-domain for bounded feature settings"
|
|
551
|
+
)
|
|
552
|
+
if explore_harness_profile is not None:
|
|
553
|
+
explore_harness_profile = str(explore_harness_profile).strip().lower().replace("_", "-")
|
|
554
|
+
if explore_harness_profile not in EXPLORE_HARNESS_PROFILES:
|
|
555
|
+
raise ValueError(
|
|
556
|
+
"--explore-harness-profile must be one of: "
|
|
557
|
+
+ ", ".join(EXPLORE_HARNESS_PROFILES)
|
|
558
|
+
)
|
|
559
|
+
|
|
560
|
+
quota_compute = _non_negative_number(quota_compute, field="quota_compute")
|
|
561
|
+
quota_window_hours = _positive_number(quota_window_hours, field="quota_window_hours")
|
|
562
|
+
max_children = _non_negative_int(max_children, field="max_children")
|
|
563
|
+
allowed_domains = _clean_domains(allowed_domains)
|
|
564
|
+
if multi_subagent_feature == "off":
|
|
565
|
+
if max_children not in (None, 0):
|
|
566
|
+
raise ValueError("--multi-subagent-feature off cannot be combined with --max-children greater than 0")
|
|
567
|
+
if allowed_domains:
|
|
568
|
+
raise ValueError("--multi-subagent-feature off cannot be combined with --allowed-domain")
|
|
569
|
+
registered_agents = _clean_registered_agents(registered_agents)
|
|
570
|
+
if peer_task_coordinator is not None:
|
|
571
|
+
peer_task_coordinator = normalize_todo_claimed_by(peer_task_coordinator)
|
|
572
|
+
if not peer_task_coordinator:
|
|
573
|
+
raise ValueError(
|
|
574
|
+
"--peer-task-coordinator must be a public-safe registered agent id"
|
|
575
|
+
)
|
|
576
|
+
reward_memory_agents = _clean_registered_agents(reward_memory_agents)
|
|
577
|
+
clear_agent_profiles = _clean_registered_agents(clear_agent_profiles)
|
|
578
|
+
clear_agent_work_modes = _clean_registered_agents(clear_agent_work_modes)
|
|
579
|
+
clear_todo_lifecycle_authority = _clean_registered_agents(
|
|
580
|
+
clear_todo_lifecycle_authority
|
|
581
|
+
)
|
|
582
|
+
supervised_agents = _clean_registered_agents(supervised_agents)
|
|
583
|
+
write_scope = _clean_write_scope(write_scope)
|
|
584
|
+
issue_fix_reviewer_notification_config = _local_private_config_path(
|
|
585
|
+
issue_fix_reviewer_notification_config,
|
|
586
|
+
label="reviewer notification config",
|
|
587
|
+
)
|
|
588
|
+
lark_event_inbox_config = _local_private_config_path(
|
|
589
|
+
lark_event_inbox_config,
|
|
590
|
+
label="lark event inbox config",
|
|
591
|
+
)
|
|
592
|
+
reward_memory_config = _local_private_config_path(
|
|
593
|
+
reward_memory_config,
|
|
594
|
+
label="reward memory experiment config",
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
payload = read_json(registry_path)
|
|
598
|
+
goals = registry_goals(payload)
|
|
599
|
+
goal = next((item for item in goals if str(item.get("id")) == goal_id), None)
|
|
600
|
+
if goal is None:
|
|
601
|
+
raise ValueError(f"goal_id not found in registry: {goal_id}")
|
|
602
|
+
|
|
603
|
+
existing_coordination = (
|
|
604
|
+
goal.get("coordination")
|
|
605
|
+
if isinstance(goal.get("coordination"), dict)
|
|
606
|
+
else {}
|
|
607
|
+
)
|
|
608
|
+
effective_registered_agents = (
|
|
609
|
+
[]
|
|
610
|
+
if clear_registered_agents
|
|
611
|
+
else registered_agents
|
|
612
|
+
if registered_agents is not None
|
|
613
|
+
else normalize_registered_agents(existing_coordination.get("registered_agents"))
|
|
614
|
+
)
|
|
615
|
+
if (
|
|
616
|
+
peer_task_coordinator is not None
|
|
617
|
+
and peer_task_coordinator not in effective_registered_agents
|
|
618
|
+
):
|
|
619
|
+
raise ValueError(
|
|
620
|
+
"--peer-task-coordinator must name an agent already registered for "
|
|
621
|
+
f"this goal: {peer_task_coordinator}"
|
|
622
|
+
)
|
|
623
|
+
existing_reward_memory = reward_memory_goal_policy(goal)
|
|
624
|
+
effective_reward_memory_agents = (
|
|
625
|
+
reward_memory_agents
|
|
626
|
+
if reward_memory_agents is not None
|
|
627
|
+
else existing_reward_memory["enabled_agents"]
|
|
628
|
+
)
|
|
629
|
+
if reward_memory_config is not None or reward_memory_agents is not None:
|
|
630
|
+
effective_reward_memory_config = (
|
|
631
|
+
reward_memory_config or existing_reward_memory["config_path"]
|
|
632
|
+
)
|
|
633
|
+
if not effective_reward_memory_config:
|
|
634
|
+
raise ValueError(
|
|
635
|
+
"--reward-memory-agent requires an existing or supplied "
|
|
636
|
+
"--reward-memory-config"
|
|
637
|
+
)
|
|
638
|
+
if not effective_reward_memory_agents:
|
|
639
|
+
raise ValueError(
|
|
640
|
+
"enabling Reward Memory requires at least one "
|
|
641
|
+
"--reward-memory-agent"
|
|
642
|
+
)
|
|
643
|
+
unknown_reward_memory_agents = sorted(
|
|
644
|
+
set(effective_reward_memory_agents) - set(effective_registered_agents)
|
|
645
|
+
)
|
|
646
|
+
reward_memory_remains_enabled = (
|
|
647
|
+
not clear_reward_memory_config
|
|
648
|
+
and (
|
|
649
|
+
existing_reward_memory["enabled"]
|
|
650
|
+
or reward_memory_config is not None
|
|
651
|
+
or reward_memory_agents is not None
|
|
652
|
+
)
|
|
653
|
+
)
|
|
654
|
+
if reward_memory_remains_enabled and unknown_reward_memory_agents:
|
|
655
|
+
raise ValueError(
|
|
656
|
+
"Reward Memory agents must already be registered for this goal: "
|
|
657
|
+
+ ", ".join(unknown_reward_memory_agents)
|
|
658
|
+
)
|
|
659
|
+
normalized_agent_profiles: dict[str, dict[str, Any]] = {}
|
|
660
|
+
for raw_profile in agent_profiles or []:
|
|
661
|
+
if not isinstance(raw_profile, Mapping):
|
|
662
|
+
raise ValueError("--agent-profile-json must contain a JSON object")
|
|
663
|
+
profile = normalize_agent_profile(
|
|
664
|
+
raw_profile,
|
|
665
|
+
registered_agents=effective_registered_agents,
|
|
666
|
+
)
|
|
667
|
+
profile_agent_id = str(profile["agent_id"])
|
|
668
|
+
if profile_agent_id in normalized_agent_profiles:
|
|
669
|
+
raise ValueError(f"duplicate agent profile for {profile_agent_id}")
|
|
670
|
+
normalized_agent_profiles[profile_agent_id] = profile
|
|
671
|
+
profile_conflicts = sorted(
|
|
672
|
+
set(normalized_agent_profiles) & set(clear_agent_profiles or [])
|
|
673
|
+
)
|
|
674
|
+
if profile_conflicts:
|
|
675
|
+
raise ValueError(
|
|
676
|
+
"cannot write and clear the same agent profile: "
|
|
677
|
+
+ ", ".join(profile_conflicts)
|
|
678
|
+
)
|
|
679
|
+
normalized_agent_work_modes = normalize_agent_work_modes(
|
|
680
|
+
agent_work_modes,
|
|
681
|
+
registered_agents=effective_registered_agents,
|
|
682
|
+
)
|
|
683
|
+
work_mode_conflicts = sorted(
|
|
684
|
+
set(normalized_agent_work_modes) & set(clear_agent_work_modes or [])
|
|
685
|
+
)
|
|
686
|
+
if work_mode_conflicts:
|
|
687
|
+
raise ValueError(
|
|
688
|
+
"cannot write and clear the same agent work mode: "
|
|
689
|
+
+ ", ".join(work_mode_conflicts)
|
|
690
|
+
)
|
|
691
|
+
normalized_todo_lifecycle_authority = normalize_todo_lifecycle_authority(
|
|
692
|
+
todo_lifecycle_authority,
|
|
693
|
+
registered_agents=effective_registered_agents,
|
|
694
|
+
)
|
|
695
|
+
authority_conflicts = sorted(
|
|
696
|
+
{entry["agent_id"] for entry in normalized_todo_lifecycle_authority}
|
|
697
|
+
& set(clear_todo_lifecycle_authority or [])
|
|
698
|
+
)
|
|
699
|
+
if authority_conflicts:
|
|
700
|
+
raise ValueError(
|
|
701
|
+
"cannot write and clear the same todo lifecycle authority grant: "
|
|
702
|
+
+ ", ".join(authority_conflicts)
|
|
703
|
+
)
|
|
704
|
+
|
|
705
|
+
before_goal = deepcopy(goal)
|
|
706
|
+
before = _settings_summary(before_goal)
|
|
707
|
+
if execution_turn_granularity is not None:
|
|
708
|
+
goal["execution_profile"] = execution_profile_with_turn_granularity(
|
|
709
|
+
goal.get("execution_profile"),
|
|
710
|
+
execution_turn_granularity,
|
|
711
|
+
)
|
|
712
|
+
legacy_hierarchy_before = legacy_agent_hierarchy_present(before_goal)
|
|
713
|
+
expected_migration_id = peer_agent_runtime_migration_id(goal_id, before_goal)
|
|
714
|
+
completed_migration_before = completed_peer_agent_runtime_migration(before_goal)
|
|
715
|
+
migration_completed_before = peer_agent_runtime_migration_completed(before_goal)
|
|
716
|
+
migration_already_completed = bool(
|
|
717
|
+
completed_migration_before
|
|
718
|
+
and completed_migration_before.get("migration_id")
|
|
719
|
+
== automation_prompt_migration_ack
|
|
720
|
+
and migration_completed_before
|
|
721
|
+
)
|
|
722
|
+
if automation_prompt_migration_ack is not None:
|
|
723
|
+
if migration_already_completed:
|
|
724
|
+
pass
|
|
725
|
+
elif not legacy_hierarchy_before:
|
|
726
|
+
raise ValueError(
|
|
727
|
+
"no pending peer runtime automation migration matches this goal"
|
|
728
|
+
)
|
|
729
|
+
elif automation_prompt_migration_ack != expected_migration_id:
|
|
730
|
+
raise ValueError(
|
|
731
|
+
"automation prompt migration id does not match the current goal state; "
|
|
732
|
+
f"expected {expected_migration_id}"
|
|
733
|
+
)
|
|
734
|
+
elif execute and legacy_hierarchy_before and not migration_completed_before and (
|
|
735
|
+
agent_model is not None
|
|
736
|
+
or registered_agents is not None
|
|
737
|
+
or peer_task_coordinator is not None
|
|
738
|
+
or clear_peer_task_coordinator
|
|
739
|
+
or clear_registered_agents
|
|
740
|
+
):
|
|
741
|
+
raise ValueError(
|
|
742
|
+
"legacy agent hierarchy requires the one-time host automation migration first; "
|
|
743
|
+
"regenerate/update the installed peer heartbeat, then run "
|
|
744
|
+
f"`loopx configure-goal --goal-id {goal_id} "
|
|
745
|
+
f"--ack-automation-prompt-migration {expected_migration_id} --execute`"
|
|
746
|
+
)
|
|
747
|
+
|
|
748
|
+
if quota_compute is not None or quota_window_hours is not None:
|
|
749
|
+
quota = goal.get("quota") if isinstance(goal.get("quota"), dict) else {}
|
|
750
|
+
if quota_compute is not None:
|
|
751
|
+
quota["compute"] = quota_compute
|
|
752
|
+
if quota_window_hours is not None:
|
|
753
|
+
quota["window_hours"] = quota_window_hours
|
|
754
|
+
goal["quota"] = quota
|
|
755
|
+
|
|
756
|
+
if (
|
|
757
|
+
self_repair_enabled is not None
|
|
758
|
+
or self_repair_health is not None
|
|
759
|
+
or self_repair_waiting_projection is not None
|
|
760
|
+
):
|
|
761
|
+
control_plane = _mutable_control_plane(goal)
|
|
762
|
+
self_repair = control_plane.get("self_repair") if isinstance(control_plane.get("self_repair"), dict) else {}
|
|
763
|
+
if self_repair_enabled is not None:
|
|
764
|
+
self_repair["enabled"] = self_repair_enabled
|
|
765
|
+
if self_repair_health is not None:
|
|
766
|
+
self_repair["allow_health_blocker_repair"] = self_repair_health
|
|
767
|
+
if self_repair_waiting_projection is not None:
|
|
768
|
+
self_repair["allow_waiting_projection_repair"] = self_repair_waiting_projection
|
|
769
|
+
control_plane["self_repair"] = self_repair
|
|
770
|
+
|
|
771
|
+
if (
|
|
772
|
+
change_quality_enabled is not None
|
|
773
|
+
or change_quality_safe_fix is not None
|
|
774
|
+
or change_quality_strict_receipt is not None
|
|
775
|
+
):
|
|
776
|
+
control_plane = _mutable_control_plane(goal)
|
|
777
|
+
current = change_quality_goal_policy_summary(goal)
|
|
778
|
+
change_quality = {
|
|
779
|
+
"schema_version": CHANGE_QUALITY_POLICY_SCHEMA_VERSION,
|
|
780
|
+
"enabled": (
|
|
781
|
+
change_quality_enabled
|
|
782
|
+
if change_quality_enabled is not None
|
|
783
|
+
else current["enabled"]
|
|
784
|
+
),
|
|
785
|
+
"safe_fix": (
|
|
786
|
+
change_quality_safe_fix
|
|
787
|
+
if change_quality_safe_fix is not None
|
|
788
|
+
else current["safe_fix"]
|
|
789
|
+
),
|
|
790
|
+
"strict_receipt": (
|
|
791
|
+
change_quality_strict_receipt
|
|
792
|
+
if change_quality_strict_receipt is not None
|
|
793
|
+
else current["strict_receipt"]
|
|
794
|
+
),
|
|
795
|
+
}
|
|
796
|
+
control_plane["change_quality_qualification"] = change_quality
|
|
797
|
+
|
|
798
|
+
if (
|
|
799
|
+
issue_fix_reviewer_notification_config is not None
|
|
800
|
+
or clear_issue_fix_reviewer_notification_config
|
|
801
|
+
):
|
|
802
|
+
control_plane = _mutable_control_plane(goal)
|
|
803
|
+
issue_fix = (
|
|
804
|
+
control_plane.get("issue_fix")
|
|
805
|
+
if isinstance(control_plane.get("issue_fix"), dict)
|
|
806
|
+
else {}
|
|
807
|
+
)
|
|
808
|
+
if clear_issue_fix_reviewer_notification_config:
|
|
809
|
+
issue_fix.pop("reviewer_notification", None)
|
|
810
|
+
else:
|
|
811
|
+
issue_fix["reviewer_notification"] = {
|
|
812
|
+
"enabled": True,
|
|
813
|
+
"config_path": issue_fix_reviewer_notification_config,
|
|
814
|
+
}
|
|
815
|
+
if issue_fix:
|
|
816
|
+
control_plane["issue_fix"] = issue_fix
|
|
817
|
+
else:
|
|
818
|
+
control_plane.pop("issue_fix", None)
|
|
819
|
+
|
|
820
|
+
if lark_event_inbox_config is not None or clear_lark_event_inbox_config:
|
|
821
|
+
control_plane = _mutable_control_plane(goal)
|
|
822
|
+
if clear_lark_event_inbox_config:
|
|
823
|
+
control_plane.pop("lark_event_inbox", None)
|
|
824
|
+
else:
|
|
825
|
+
control_plane["lark_event_inbox"] = {
|
|
826
|
+
"enabled": True,
|
|
827
|
+
"config_path": lark_event_inbox_config,
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
if lark_kanban_heartbeat_sync is not None:
|
|
831
|
+
control_plane = _mutable_control_plane(goal)
|
|
832
|
+
lark_kanban = (
|
|
833
|
+
control_plane.get("lark_kanban")
|
|
834
|
+
if isinstance(control_plane.get("lark_kanban"), dict)
|
|
835
|
+
else {}
|
|
836
|
+
)
|
|
837
|
+
lark_kanban["heartbeat_sync_enabled"] = lark_kanban_heartbeat_sync
|
|
838
|
+
control_plane["lark_kanban"] = lark_kanban
|
|
839
|
+
|
|
840
|
+
if (
|
|
841
|
+
reward_memory_config is not None
|
|
842
|
+
or reward_memory_agents is not None
|
|
843
|
+
or clear_reward_memory_config
|
|
844
|
+
):
|
|
845
|
+
control_plane = _mutable_control_plane(goal)
|
|
846
|
+
if clear_reward_memory_config:
|
|
847
|
+
control_plane.pop("reward_memory", None)
|
|
848
|
+
else:
|
|
849
|
+
current_reward_memory = reward_memory_goal_policy(goal)
|
|
850
|
+
control_plane["reward_memory"] = {
|
|
851
|
+
"enabled": True,
|
|
852
|
+
"experimental": True,
|
|
853
|
+
"config_path": (
|
|
854
|
+
reward_memory_config or current_reward_memory["config_path"]
|
|
855
|
+
),
|
|
856
|
+
"enabled_agents": list(effective_reward_memory_agents),
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
if explore_graph_enabled is not None:
|
|
860
|
+
goal["explore_graph"] = {"enabled": explore_graph_enabled}
|
|
861
|
+
|
|
862
|
+
if (
|
|
863
|
+
multi_subagent_feature is not None
|
|
864
|
+
or
|
|
865
|
+
orchestration_mode is not None
|
|
866
|
+
or spawn_allowed is not None
|
|
867
|
+
or max_children is not None
|
|
868
|
+
or allowed_domains is not None
|
|
869
|
+
or clear_allowed_domains
|
|
870
|
+
or explore_harness_enabled is not None
|
|
871
|
+
or explore_harness_profile is not None
|
|
872
|
+
or clear_explore_harness_profile
|
|
873
|
+
):
|
|
874
|
+
spawn_policy = goal.get("spawn_policy") if isinstance(goal.get("spawn_policy"), dict) else {}
|
|
875
|
+
if multi_subagent_feature == "enabled":
|
|
876
|
+
spawn_policy["mode"] = MULTI_SUBAGENT_ORCHESTRATION_MODE
|
|
877
|
+
spawn_policy["allowed"] = True
|
|
878
|
+
if max_children is None:
|
|
879
|
+
existing_children = int(compact_orchestration_policy(spawn_policy).get("max_children") or 0)
|
|
880
|
+
spawn_policy["max_children"] = (
|
|
881
|
+
existing_children
|
|
882
|
+
if existing_children > 0
|
|
883
|
+
else DEFAULT_MULTI_SUBAGENT_MAX_CHILDREN
|
|
884
|
+
)
|
|
885
|
+
elif multi_subagent_feature == "off":
|
|
886
|
+
spawn_policy["mode"] = DEFAULT_ORCHESTRATION_MODE
|
|
887
|
+
spawn_policy["allowed"] = False
|
|
888
|
+
spawn_policy["max_children"] = 0
|
|
889
|
+
spawn_policy["allowed_domains"] = []
|
|
890
|
+
elif orchestration_mode is not None:
|
|
891
|
+
spawn_policy["mode"] = orchestration_mode
|
|
892
|
+
if spawn_allowed is not None:
|
|
893
|
+
spawn_policy["allowed"] = spawn_allowed
|
|
894
|
+
if max_children is not None:
|
|
895
|
+
spawn_policy["max_children"] = max_children
|
|
896
|
+
if clear_allowed_domains:
|
|
897
|
+
spawn_policy["allowed_domains"] = []
|
|
898
|
+
elif allowed_domains is not None:
|
|
899
|
+
spawn_policy["allowed_domains"] = allowed_domains
|
|
900
|
+
if (
|
|
901
|
+
explore_harness_enabled is not None
|
|
902
|
+
or explore_harness_profile is not None
|
|
903
|
+
or clear_explore_harness_profile
|
|
904
|
+
):
|
|
905
|
+
explore_harness = (
|
|
906
|
+
spawn_policy.get("explore_harness")
|
|
907
|
+
if isinstance(spawn_policy.get("explore_harness"), dict)
|
|
908
|
+
else {}
|
|
909
|
+
)
|
|
910
|
+
if explore_harness_enabled is not None:
|
|
911
|
+
explore_harness["enabled"] = explore_harness_enabled
|
|
912
|
+
if clear_explore_harness_profile:
|
|
913
|
+
explore_harness.pop("profile", None)
|
|
914
|
+
elif explore_harness_profile is not None:
|
|
915
|
+
explore_harness["profile"] = explore_harness_profile
|
|
916
|
+
if explore_harness:
|
|
917
|
+
spawn_policy["explore_harness"] = explore_harness
|
|
918
|
+
else:
|
|
919
|
+
spawn_policy.pop("explore_harness", None)
|
|
920
|
+
goal["spawn_policy"] = spawn_policy
|
|
921
|
+
|
|
922
|
+
if waiting_on is not None:
|
|
923
|
+
goal["waiting_on"] = waiting_on
|
|
924
|
+
elif clear_waiting_on:
|
|
925
|
+
goal.pop("waiting_on", None)
|
|
926
|
+
|
|
927
|
+
if (
|
|
928
|
+
clear_registered_agents
|
|
929
|
+
or registered_agents is not None
|
|
930
|
+
or peer_task_coordinator is not None
|
|
931
|
+
or clear_peer_task_coordinator
|
|
932
|
+
or normalized_agent_profiles
|
|
933
|
+
or clear_agent_profiles
|
|
934
|
+
or normalized_agent_work_modes
|
|
935
|
+
or clear_agent_work_modes
|
|
936
|
+
or normalized_todo_lifecycle_authority
|
|
937
|
+
or clear_todo_lifecycle_authority
|
|
938
|
+
or agent_model is not None
|
|
939
|
+
or automation_prompt_migration_ack is not None
|
|
940
|
+
or supervisor_agent is not None
|
|
941
|
+
or supervised_agents is not None
|
|
942
|
+
or clear_supervisor
|
|
943
|
+
or write_scope is not None
|
|
944
|
+
or clear_write_scope
|
|
945
|
+
or clear_boundary_authority
|
|
946
|
+
or adding_boundary_authority
|
|
947
|
+
):
|
|
948
|
+
coordination = goal.get("coordination") if isinstance(goal.get("coordination"), dict) else {}
|
|
949
|
+
effective_agent_model = AgentRuntimeModel.PEER_V1.value
|
|
950
|
+
if clear_registered_agents:
|
|
951
|
+
coordination.pop("registered_agents", None)
|
|
952
|
+
coordination.pop("agent_model", None)
|
|
953
|
+
coordination.pop("agent_profiles", None)
|
|
954
|
+
coordination.pop("agent_work_modes", None)
|
|
955
|
+
coordination.pop("supervisor", None)
|
|
956
|
+
coordination.pop("todo_lifecycle_authority", None)
|
|
957
|
+
coordination.pop("peer_task_coordination", None)
|
|
958
|
+
elif registered_agents is not None:
|
|
959
|
+
coordination["registered_agents"] = registered_agents
|
|
960
|
+
current_peer_task_coordination = (
|
|
961
|
+
coordination.get("peer_task_coordination")
|
|
962
|
+
if isinstance(coordination.get("peer_task_coordination"), dict)
|
|
963
|
+
else {}
|
|
964
|
+
)
|
|
965
|
+
current_peer_coordinator = normalize_todo_claimed_by(
|
|
966
|
+
current_peer_task_coordination.get("coordinator_agent_id")
|
|
967
|
+
)
|
|
968
|
+
if (
|
|
969
|
+
current_peer_coordinator
|
|
970
|
+
and current_peer_coordinator not in registered_agents
|
|
971
|
+
):
|
|
972
|
+
coordination.pop("peer_task_coordination", None)
|
|
973
|
+
existing_profiles = (
|
|
974
|
+
coordination.get("agent_profiles")
|
|
975
|
+
if isinstance(coordination.get("agent_profiles"), dict)
|
|
976
|
+
else {}
|
|
977
|
+
)
|
|
978
|
+
retained_profiles = {
|
|
979
|
+
agent: profile
|
|
980
|
+
for agent, profile in existing_profiles.items()
|
|
981
|
+
if agent in registered_agents
|
|
982
|
+
}
|
|
983
|
+
if retained_profiles:
|
|
984
|
+
coordination["agent_profiles"] = retained_profiles
|
|
985
|
+
else:
|
|
986
|
+
coordination.pop("agent_profiles", None)
|
|
987
|
+
raw_work_modes = (
|
|
988
|
+
coordination.get("agent_work_modes")
|
|
989
|
+
if isinstance(coordination.get("agent_work_modes"), Mapping)
|
|
990
|
+
else {}
|
|
991
|
+
)
|
|
992
|
+
retained_work_modes = normalize_agent_work_modes(
|
|
993
|
+
{
|
|
994
|
+
agent: mode
|
|
995
|
+
for agent, mode in raw_work_modes.items()
|
|
996
|
+
if agent in registered_agents
|
|
997
|
+
},
|
|
998
|
+
registered_agents=registered_agents,
|
|
999
|
+
)
|
|
1000
|
+
if retained_work_modes:
|
|
1001
|
+
coordination["agent_work_modes"] = retained_work_modes
|
|
1002
|
+
else:
|
|
1003
|
+
coordination.pop("agent_work_modes", None)
|
|
1004
|
+
retained_authority = [
|
|
1005
|
+
entry
|
|
1006
|
+
for entry in coordination.get("todo_lifecycle_authority") or []
|
|
1007
|
+
if isinstance(entry, Mapping)
|
|
1008
|
+
and normalize_todo_claimed_by(entry.get("agent_id"))
|
|
1009
|
+
in registered_agents
|
|
1010
|
+
]
|
|
1011
|
+
if retained_authority:
|
|
1012
|
+
coordination["todo_lifecycle_authority"] = (
|
|
1013
|
+
normalize_todo_lifecycle_authority(
|
|
1014
|
+
retained_authority,
|
|
1015
|
+
registered_agents=registered_agents,
|
|
1016
|
+
)
|
|
1017
|
+
)
|
|
1018
|
+
else:
|
|
1019
|
+
coordination.pop("todo_lifecycle_authority", None)
|
|
1020
|
+
if not clear_registered_agents:
|
|
1021
|
+
coordination["agent_model"] = effective_agent_model
|
|
1022
|
+
if not clear_registered_agents:
|
|
1023
|
+
if clear_peer_task_coordinator:
|
|
1024
|
+
coordination.pop("peer_task_coordination", None)
|
|
1025
|
+
elif peer_task_coordinator is not None:
|
|
1026
|
+
coordination["peer_task_coordination"] = {
|
|
1027
|
+
"coordinator_agent_id": peer_task_coordinator,
|
|
1028
|
+
}
|
|
1029
|
+
if not clear_registered_agents and (
|
|
1030
|
+
normalized_agent_profiles or clear_agent_profiles
|
|
1031
|
+
):
|
|
1032
|
+
profiles = (
|
|
1033
|
+
dict(coordination.get("agent_profiles"))
|
|
1034
|
+
if isinstance(coordination.get("agent_profiles"), dict)
|
|
1035
|
+
else {}
|
|
1036
|
+
)
|
|
1037
|
+
for profile_agent_id in clear_agent_profiles or []:
|
|
1038
|
+
profiles.pop(profile_agent_id, None)
|
|
1039
|
+
profiles.update(normalized_agent_profiles)
|
|
1040
|
+
if profiles:
|
|
1041
|
+
coordination["agent_profiles"] = profiles
|
|
1042
|
+
else:
|
|
1043
|
+
coordination.pop("agent_profiles", None)
|
|
1044
|
+
if not clear_registered_agents and (
|
|
1045
|
+
normalized_agent_work_modes or clear_agent_work_modes
|
|
1046
|
+
):
|
|
1047
|
+
work_modes = normalize_agent_work_modes(
|
|
1048
|
+
coordination.get("agent_work_modes"),
|
|
1049
|
+
registered_agents=normalize_registered_agents(
|
|
1050
|
+
coordination.get("registered_agents")
|
|
1051
|
+
),
|
|
1052
|
+
)
|
|
1053
|
+
for work_mode_agent_id in clear_agent_work_modes or []:
|
|
1054
|
+
work_modes.pop(work_mode_agent_id, None)
|
|
1055
|
+
work_modes.update(normalized_agent_work_modes)
|
|
1056
|
+
if work_modes:
|
|
1057
|
+
coordination["agent_work_modes"] = dict(sorted(work_modes.items()))
|
|
1058
|
+
else:
|
|
1059
|
+
coordination.pop("agent_work_modes", None)
|
|
1060
|
+
if not clear_registered_agents and (
|
|
1061
|
+
normalized_todo_lifecycle_authority
|
|
1062
|
+
or clear_todo_lifecycle_authority
|
|
1063
|
+
):
|
|
1064
|
+
current_authority = normalize_todo_lifecycle_authority(
|
|
1065
|
+
coordination.get("todo_lifecycle_authority"),
|
|
1066
|
+
registered_agents=normalize_registered_agents(
|
|
1067
|
+
coordination.get("registered_agents")
|
|
1068
|
+
),
|
|
1069
|
+
)
|
|
1070
|
+
authority_by_agent = {
|
|
1071
|
+
entry["agent_id"]: entry for entry in current_authority
|
|
1072
|
+
}
|
|
1073
|
+
for authority_agent_id in clear_todo_lifecycle_authority or []:
|
|
1074
|
+
authority_by_agent.pop(authority_agent_id, None)
|
|
1075
|
+
authority_by_agent.update(
|
|
1076
|
+
{
|
|
1077
|
+
entry["agent_id"]: entry
|
|
1078
|
+
for entry in normalized_todo_lifecycle_authority
|
|
1079
|
+
}
|
|
1080
|
+
)
|
|
1081
|
+
if authority_by_agent:
|
|
1082
|
+
coordination["todo_lifecycle_authority"] = list(
|
|
1083
|
+
authority_by_agent.values()
|
|
1084
|
+
)
|
|
1085
|
+
else:
|
|
1086
|
+
coordination.pop("todo_lifecycle_authority", None)
|
|
1087
|
+
if automation_prompt_migration_ack is not None and not migration_already_completed:
|
|
1088
|
+
coordination = migrate_coordination_to_peer_v1(
|
|
1089
|
+
coordination,
|
|
1090
|
+
migration_id=automation_prompt_migration_ack,
|
|
1091
|
+
completed_at=_now_iso() if execute else None,
|
|
1092
|
+
)
|
|
1093
|
+
if clear_supervisor:
|
|
1094
|
+
coordination.pop("supervisor", None)
|
|
1095
|
+
elif supervisor_agent is not None:
|
|
1096
|
+
normalized_supervisor_agent = normalize_todo_claimed_by(supervisor_agent)
|
|
1097
|
+
if not normalized_supervisor_agent:
|
|
1098
|
+
raise ValueError("--supervisor-agent must be a registered agent id")
|
|
1099
|
+
coordination["supervisor"] = normalize_peer_supervisor(
|
|
1100
|
+
{
|
|
1101
|
+
"agent_id": normalized_supervisor_agent,
|
|
1102
|
+
"supervised_agents": supervised_agents,
|
|
1103
|
+
},
|
|
1104
|
+
registered_agents=normalize_registered_agents(
|
|
1105
|
+
coordination.get("registered_agents")
|
|
1106
|
+
),
|
|
1107
|
+
)
|
|
1108
|
+
if clear_write_scope:
|
|
1109
|
+
coordination["write_scope"] = []
|
|
1110
|
+
elif write_scope is not None:
|
|
1111
|
+
if replace_write_scope:
|
|
1112
|
+
coordination["write_scope"] = write_scope
|
|
1113
|
+
else:
|
|
1114
|
+
existing_write_scope = _clean_write_scope(coordination.get("write_scope") or []) or []
|
|
1115
|
+
coordination["write_scope"] = _clean_write_scope([*existing_write_scope, *write_scope]) or []
|
|
1116
|
+
if clear_boundary_authority:
|
|
1117
|
+
coordination.pop("checkpointed_boundary_authority", None)
|
|
1118
|
+
if adding_boundary_authority:
|
|
1119
|
+
entry = build_checkpointed_boundary_authority_entry(
|
|
1120
|
+
write_scopes=boundary_authority_scopes or [],
|
|
1121
|
+
source=boundary_authority_source or "",
|
|
1122
|
+
decision_id=boundary_authority_decision_id,
|
|
1123
|
+
recorded_at=boundary_authority_recorded_at,
|
|
1124
|
+
expires_at=boundary_authority_expires_at,
|
|
1125
|
+
)
|
|
1126
|
+
entries = (
|
|
1127
|
+
coordination.get("checkpointed_boundary_authority")
|
|
1128
|
+
if isinstance(coordination.get("checkpointed_boundary_authority"), list)
|
|
1129
|
+
else []
|
|
1130
|
+
)
|
|
1131
|
+
coordination["checkpointed_boundary_authority"] = [*entries, entry]
|
|
1132
|
+
goal["coordination"] = coordination
|
|
1133
|
+
|
|
1134
|
+
after = _settings_summary(goal)
|
|
1135
|
+
changed_fields = _changed_fields(before, after)
|
|
1136
|
+
dry_run = not execute
|
|
1137
|
+
model_changed = bool(
|
|
1138
|
+
before.get("legacy_hierarchy_present")
|
|
1139
|
+
or before.get("agent_model") != after.get("agent_model")
|
|
1140
|
+
or automation_prompt_migration_ack is not None
|
|
1141
|
+
)
|
|
1142
|
+
backup_path = None
|
|
1143
|
+
|
|
1144
|
+
if execute and changed_fields:
|
|
1145
|
+
payload["updated_at"] = _now_iso()
|
|
1146
|
+
if model_changed:
|
|
1147
|
+
stamp = datetime.now().astimezone().strftime("%Y%m%dT%H%M%S%z")
|
|
1148
|
+
backup_path = registry_path.with_name(
|
|
1149
|
+
f"{registry_path.name}.before-agent-model-{stamp}.bak"
|
|
1150
|
+
)
|
|
1151
|
+
shutil.copy2(registry_path, backup_path)
|
|
1152
|
+
atomic_write_json(registry_path, payload)
|
|
1153
|
+
|
|
1154
|
+
feature_summary = {
|
|
1155
|
+
"multi_subagent": _multi_subagent_feature_status(after.get("orchestration") or {}),
|
|
1156
|
+
"explore_graph": deepcopy(after.get("explore_graph") or {"enabled": False}),
|
|
1157
|
+
"explore_harness": deepcopy(
|
|
1158
|
+
(after.get("orchestration") or {}).get("explore_harness")
|
|
1159
|
+
or {"enabled": False}
|
|
1160
|
+
),
|
|
1161
|
+
"peer_supervisor": deepcopy(after.get("supervisor") or {"enabled": False}),
|
|
1162
|
+
"peer_task_coordination": deepcopy(
|
|
1163
|
+
after.get("peer_task_coordination") or {"enabled": False}
|
|
1164
|
+
),
|
|
1165
|
+
"lark_event_inbox": _lark_event_inbox_config_summary(goal),
|
|
1166
|
+
"lark_kanban_heartbeat_sync": _lark_kanban_heartbeat_config_summary(goal),
|
|
1167
|
+
"reward_memory": reward_memory_goal_policy_summary(goal),
|
|
1168
|
+
"change_quality_qualification": change_quality_goal_policy_summary(goal),
|
|
1169
|
+
"default": "off",
|
|
1170
|
+
"configuration_entry": "multi_subagent_feature",
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
return {
|
|
1174
|
+
"ok": True,
|
|
1175
|
+
"dry_run": dry_run,
|
|
1176
|
+
"execute": execute,
|
|
1177
|
+
"registry": str(registry_path),
|
|
1178
|
+
"goal_id": goal_id,
|
|
1179
|
+
"changed": bool(changed_fields),
|
|
1180
|
+
"changed_fields": changed_fields,
|
|
1181
|
+
"backup_path": str(backup_path) if backup_path else None,
|
|
1182
|
+
"before": before,
|
|
1183
|
+
"after": after,
|
|
1184
|
+
"written": bool(execute and changed_fields),
|
|
1185
|
+
"automation_prompt_migration": {
|
|
1186
|
+
"migration_id": automation_prompt_migration_ack,
|
|
1187
|
+
"status": (
|
|
1188
|
+
"already_completed"
|
|
1189
|
+
if migration_already_completed
|
|
1190
|
+
else "completed"
|
|
1191
|
+
if automation_prompt_migration_ack is not None
|
|
1192
|
+
else "pending"
|
|
1193
|
+
if legacy_hierarchy_before
|
|
1194
|
+
else "not_required"
|
|
1195
|
+
),
|
|
1196
|
+
"exactly_once_effect": True,
|
|
1197
|
+
},
|
|
1198
|
+
"control_plane_summary": control_plane_policy_summary(after.get("control_plane")),
|
|
1199
|
+
"orchestration_summary": orchestration_policy_summary(after.get("orchestration")),
|
|
1200
|
+
"feature_summary": feature_summary,
|
|
1201
|
+
"configuration_catalog": build_goal_configuration_catalog(
|
|
1202
|
+
goal_id=goal_id,
|
|
1203
|
+
settings=after,
|
|
1204
|
+
feature_summary=feature_summary,
|
|
1205
|
+
default_multi_subagent_max_children=DEFAULT_MULTI_SUBAGENT_MAX_CHILDREN,
|
|
1206
|
+
explore_harness_profiles=EXPLORE_HARNESS_PROFILES,
|
|
1207
|
+
),
|
|
1208
|
+
"heartbeat_prompt_migration": _build_heartbeat_prompt_migration(
|
|
1209
|
+
goal_id=goal_id,
|
|
1210
|
+
changed_fields=changed_fields,
|
|
1211
|
+
after=after,
|
|
1212
|
+
migration_id=(
|
|
1213
|
+
automation_prompt_migration_ack
|
|
1214
|
+
if automation_prompt_migration_ack is not None
|
|
1215
|
+
else expected_migration_id
|
|
1216
|
+
if legacy_hierarchy_before and not migration_completed_before
|
|
1217
|
+
else None
|
|
1218
|
+
),
|
|
1219
|
+
migration_acknowledged=automation_prompt_migration_ack is not None,
|
|
1220
|
+
),
|
|
1221
|
+
"supervisor_prompt": _build_supervisor_prompt_setup(
|
|
1222
|
+
goal_id=goal_id,
|
|
1223
|
+
changed_fields=changed_fields,
|
|
1224
|
+
after=after,
|
|
1225
|
+
),
|
|
1226
|
+
}
|
|
1227
|
+
|
|
1228
|
+
|
|
1229
|
+
def render_configure_goal_markdown(payload: dict[str, Any]) -> str:
|
|
1230
|
+
lines = [
|
|
1231
|
+
"# LoopX Goal Configuration",
|
|
1232
|
+
"",
|
|
1233
|
+
f"- ok: `{payload.get('ok')}`",
|
|
1234
|
+
f"- dry_run: `{payload.get('dry_run')}`",
|
|
1235
|
+
f"- registry: `{payload.get('registry')}`",
|
|
1236
|
+
f"- goal_id: `{payload.get('goal_id')}`",
|
|
1237
|
+
f"- changed: `{payload.get('changed')}`",
|
|
1238
|
+
f"- written: `{payload.get('written')}`",
|
|
1239
|
+
]
|
|
1240
|
+
if payload.get("error"):
|
|
1241
|
+
lines.append(f"- error: {payload.get('error')}")
|
|
1242
|
+
return "\n".join(lines)
|
|
1243
|
+
fields = payload.get("changed_fields") or []
|
|
1244
|
+
lines.append(f"- changed_fields: `{', '.join(fields) if fields else 'none'}`")
|
|
1245
|
+
global_sync = payload.get("global_sync")
|
|
1246
|
+
if isinstance(global_sync, dict):
|
|
1247
|
+
selected_target = (
|
|
1248
|
+
global_sync.get("selected_target")
|
|
1249
|
+
if isinstance(global_sync.get("selected_target"), dict)
|
|
1250
|
+
else {}
|
|
1251
|
+
)
|
|
1252
|
+
readback = (
|
|
1253
|
+
global_sync.get("readback")
|
|
1254
|
+
if isinstance(global_sync.get("readback"), dict)
|
|
1255
|
+
else {}
|
|
1256
|
+
)
|
|
1257
|
+
lines.extend(
|
|
1258
|
+
[
|
|
1259
|
+
f"- global_sync_enabled: `{global_sync.get('enabled')}`",
|
|
1260
|
+
f"- global_sync_executed: `{global_sync.get('executed')}`",
|
|
1261
|
+
f"- global_sync_target: `{selected_target.get('global_registry')}`",
|
|
1262
|
+
f"- global_sync_readback: `{readback.get('status')}`",
|
|
1263
|
+
]
|
|
1264
|
+
)
|
|
1265
|
+
if payload.get("control_plane_summary"):
|
|
1266
|
+
lines.append(f"- control_plane: {payload.get('control_plane_summary')}")
|
|
1267
|
+
if payload.get("orchestration_summary"):
|
|
1268
|
+
lines.append(f"- orchestration: {payload.get('orchestration_summary')}")
|
|
1269
|
+
feature_summary = payload.get("feature_summary")
|
|
1270
|
+
if isinstance(feature_summary, dict):
|
|
1271
|
+
lines.append(f"- feature_multi_subagent: `{feature_summary.get('multi_subagent')}`")
|
|
1272
|
+
graph = feature_summary.get("explore_graph")
|
|
1273
|
+
if isinstance(graph, dict):
|
|
1274
|
+
lines.append(
|
|
1275
|
+
f"- feature_explore_graph: `{'on' if graph.get('enabled') else 'off'}`"
|
|
1276
|
+
)
|
|
1277
|
+
harness = feature_summary.get("explore_harness")
|
|
1278
|
+
if isinstance(harness, dict):
|
|
1279
|
+
harness_state = "on" if harness.get("enabled") else "off"
|
|
1280
|
+
if harness.get("profile"):
|
|
1281
|
+
harness_state += f"({harness.get('profile')})"
|
|
1282
|
+
lines.append(f"- feature_explore_harness: `{harness_state}`")
|
|
1283
|
+
supervisor = feature_summary.get("peer_supervisor")
|
|
1284
|
+
if isinstance(supervisor, dict):
|
|
1285
|
+
supervisor_state = "on" if supervisor.get("enabled") else "off"
|
|
1286
|
+
if supervisor.get("agent_id"):
|
|
1287
|
+
supervisor_state += f"({supervisor.get('agent_id')})"
|
|
1288
|
+
lines.append(f"- feature_peer_supervisor: `{supervisor_state}`")
|
|
1289
|
+
catalog = payload.get("configuration_catalog")
|
|
1290
|
+
if isinstance(catalog, dict):
|
|
1291
|
+
disclosure = catalog.get("disclosure_policy") or {}
|
|
1292
|
+
lines.extend(
|
|
1293
|
+
[
|
|
1294
|
+
"",
|
|
1295
|
+
"## Optional Features (On Demand)",
|
|
1296
|
+
"",
|
|
1297
|
+
"First run requires no optional feature configuration.",
|
|
1298
|
+
"",
|
|
1299
|
+
f"- inspect: `{disclosure.get('inspect_command')}`",
|
|
1300
|
+
f"- all_settings_help: `{catalog.get('all_settings_help_command')}`",
|
|
1301
|
+
"- apply_policy: preview first; add `--execute` only after review",
|
|
1302
|
+
]
|
|
1303
|
+
)
|
|
1304
|
+
for feature in catalog.get("features") or []:
|
|
1305
|
+
if not isinstance(feature, dict):
|
|
1306
|
+
continue
|
|
1307
|
+
current = feature.get("current") or {}
|
|
1308
|
+
state = "on" if current.get("enabled") else "off"
|
|
1309
|
+
commands = feature.get("commands") or {}
|
|
1310
|
+
verify_commands = commands.get("verify") or []
|
|
1311
|
+
documentation = feature.get("documentation") or {}
|
|
1312
|
+
lines.extend(
|
|
1313
|
+
[
|
|
1314
|
+
f"- `{feature.get('feature_id')}` ({feature.get('display_name')}): `{state}`",
|
|
1315
|
+
f" - consider: {feature.get('consider_when')}",
|
|
1316
|
+
f" - preview: `{commands.get('preview_enable')}`",
|
|
1317
|
+
f" - apply: `{commands.get('apply_enable')}`",
|
|
1318
|
+
f" - preview_disable: `{commands.get('preview_disable')}`",
|
|
1319
|
+
f" - apply_disable: `{commands.get('apply_disable')}`",
|
|
1320
|
+
f" - verify: {'; '.join(f'`{command}`' for command in verify_commands)}",
|
|
1321
|
+
f" - docs: [{documentation.get('path')}]({documentation.get('url')})",
|
|
1322
|
+
]
|
|
1323
|
+
)
|
|
1324
|
+
migration = payload.get("heartbeat_prompt_migration")
|
|
1325
|
+
if isinstance(migration, dict):
|
|
1326
|
+
lines.append(f"- heartbeat_prompt_migration: {migration.get('action')}")
|
|
1327
|
+
for command in migration.get("commands") or []:
|
|
1328
|
+
if not isinstance(command, dict):
|
|
1329
|
+
continue
|
|
1330
|
+
lines.append(
|
|
1331
|
+
f" - {command.get('agent_id')}: `{command.get('command')}`"
|
|
1332
|
+
)
|
|
1333
|
+
supervisor_prompt = payload.get("supervisor_prompt")
|
|
1334
|
+
if isinstance(supervisor_prompt, dict):
|
|
1335
|
+
lines.append(f"- supervisor_prompt_status: `{supervisor_prompt.get('status')}`")
|
|
1336
|
+
if supervisor_prompt.get("command"):
|
|
1337
|
+
lines.append(f"- supervisor_prompt: `{supervisor_prompt.get('command')}`")
|
|
1338
|
+
activation = payload.get("host_loop_activation")
|
|
1339
|
+
if isinstance(activation, dict):
|
|
1340
|
+
lines.append(
|
|
1341
|
+
f"- host_loop_activation: `{activation.get('host_surface')}` "
|
|
1342
|
+
f"status=`{activation.get('status')}` "
|
|
1343
|
+
f"activated=`{activation.get('activated')}`"
|
|
1344
|
+
)
|
|
1345
|
+
if activation.get("activated") is not True:
|
|
1346
|
+
lines.append(f"- host_loop_action: {activation.get('recommended_action')}")
|
|
1347
|
+
if payload.get("changed"):
|
|
1348
|
+
lines.extend(
|
|
1349
|
+
[
|
|
1350
|
+
"",
|
|
1351
|
+
"## Before",
|
|
1352
|
+
"",
|
|
1353
|
+
"```json",
|
|
1354
|
+
json.dumps(payload.get("before") or {}, ensure_ascii=False, indent=2),
|
|
1355
|
+
"```",
|
|
1356
|
+
"",
|
|
1357
|
+
"## After",
|
|
1358
|
+
"",
|
|
1359
|
+
"```json",
|
|
1360
|
+
json.dumps(payload.get("after") or {}, ensure_ascii=False, indent=2),
|
|
1361
|
+
"```",
|
|
1362
|
+
]
|
|
1363
|
+
)
|
|
1364
|
+
else:
|
|
1365
|
+
lines.extend(
|
|
1366
|
+
[
|
|
1367
|
+
"",
|
|
1368
|
+
"## Current Settings",
|
|
1369
|
+
"",
|
|
1370
|
+
"```json",
|
|
1371
|
+
json.dumps(payload.get("after") or {}, ensure_ascii=False, indent=2),
|
|
1372
|
+
"```",
|
|
1373
|
+
]
|
|
1374
|
+
)
|
|
1375
|
+
return "\n".join(lines)
|