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
|
@@ -0,0 +1,1311 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from .agent_registry import normalize_registered_agents
|
|
6
|
+
from .control_plane.scheduler.execution_context import SchedulerRuntimeProfile
|
|
7
|
+
from .control_plane.todos.contract import (
|
|
8
|
+
normalize_required_capabilities,
|
|
9
|
+
normalize_todo_claimed_by,
|
|
10
|
+
)
|
|
11
|
+
from .project_prompt import (
|
|
12
|
+
render_heartbeat_prompt_command,
|
|
13
|
+
render_heartbeat_prompt_json_command,
|
|
14
|
+
shell_arg,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
SCHEMA_VERSION = "loopx_host_loop_activation_v1"
|
|
18
|
+
AGENT_TYPE_CATALOG_SCHEMA_VERSION = "loopx_agent_type_catalog_v0"
|
|
19
|
+
IDENTITY_SELECTION_SCHEMA_VERSION = "loopx_host_loop_identity_selection_v0"
|
|
20
|
+
HOST_MANAGED_SKILL_AGENT_TYPES = frozenset(
|
|
21
|
+
{
|
|
22
|
+
"ark-managed-agent",
|
|
23
|
+
"traex-cli",
|
|
24
|
+
"other-agent",
|
|
25
|
+
}
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def scheduler_command_binding_for_agent_type(
|
|
30
|
+
agent_type: str,
|
|
31
|
+
) -> dict[str, Any]:
|
|
32
|
+
canonical = normalize_agent_type(agent_type)
|
|
33
|
+
runtime_profile = {
|
|
34
|
+
"ark-managed-agent": SchedulerRuntimeProfile.ARK_MANAGED_AGENT_GOAL,
|
|
35
|
+
"codex-app": SchedulerRuntimeProfile.CODEX_APP_HEARTBEAT,
|
|
36
|
+
"codex-app-ssh": SchedulerRuntimeProfile.CODEX_APP_SSH_VISIBLE,
|
|
37
|
+
"codex-cli": SchedulerRuntimeProfile.CODEX_CLI_VISIBLE,
|
|
38
|
+
"codex-ide-plugin": SchedulerRuntimeProfile.CODEX_CLI_VISIBLE,
|
|
39
|
+
"claude-code": SchedulerRuntimeProfile.CLAUDE_CODE_VISIBLE,
|
|
40
|
+
"opencode": SchedulerRuntimeProfile.GENERIC_CLI_AGENT_LOOP,
|
|
41
|
+
"opencode2": SchedulerRuntimeProfile.GENERIC_CLI_AGENT_LOOP,
|
|
42
|
+
"traex-cli": SchedulerRuntimeProfile.GENERIC_CLI_AGENT_LOOP,
|
|
43
|
+
"pi": SchedulerRuntimeProfile.GENERIC_CLI_AGENT_LOOP,
|
|
44
|
+
"gemini-cli": SchedulerRuntimeProfile.GENERIC_CLI_AGENT_LOOP,
|
|
45
|
+
"cursor-agent": SchedulerRuntimeProfile.GENERIC_CLI_AGENT_LOOP,
|
|
46
|
+
"deepseek-harness": SchedulerRuntimeProfile.GENERIC_CLI_AGENT_LOOP,
|
|
47
|
+
}.get(canonical)
|
|
48
|
+
if runtime_profile is not None:
|
|
49
|
+
return {"runtime_profile": runtime_profile.value}
|
|
50
|
+
return {}
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def agent_type_uses_host_managed_skills(agent_type: str) -> bool:
|
|
54
|
+
return normalize_agent_type(agent_type) in HOST_MANAGED_SKILL_AGENT_TYPES
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
SUPPORTED_AGENT_TYPES = [
|
|
58
|
+
"ark-managed-agent",
|
|
59
|
+
"codex-app",
|
|
60
|
+
"codex-app-ssh",
|
|
61
|
+
"codex-ide-plugin",
|
|
62
|
+
"codex-cli",
|
|
63
|
+
"claude-code",
|
|
64
|
+
"opencode",
|
|
65
|
+
"opencode2",
|
|
66
|
+
"traex-cli",
|
|
67
|
+
"pi",
|
|
68
|
+
"gemini-cli",
|
|
69
|
+
"cursor-agent",
|
|
70
|
+
"deepseek-harness",
|
|
71
|
+
"manual",
|
|
72
|
+
"other-agent",
|
|
73
|
+
]
|
|
74
|
+
|
|
75
|
+
AGENT_TYPE_CATALOG: dict[str, dict[str, Any]] = {
|
|
76
|
+
"ark-managed-agent": {
|
|
77
|
+
"display_name": "Ark Managed Agent",
|
|
78
|
+
"host_loop": "one-shot Goal activation owned by the Goal runtime",
|
|
79
|
+
"entry": "submit the generated task_body as one Goal",
|
|
80
|
+
"accepted_inputs": [
|
|
81
|
+
"ark-managed-agent",
|
|
82
|
+
"ark_managed_agent",
|
|
83
|
+
"ark managed agent",
|
|
84
|
+
"managed-agent",
|
|
85
|
+
"managed_agent",
|
|
86
|
+
"managed agent",
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
"codex-app": {
|
|
90
|
+
"display_name": "Codex App",
|
|
91
|
+
"host_loop": "Codex App heartbeat automation",
|
|
92
|
+
"entry": "$loopx <task> or the explicit LoopX skill from /skills",
|
|
93
|
+
"accepted_inputs": ["codex-app", "codex_app", "codex app", "codex-desktop", "codex desktop"],
|
|
94
|
+
},
|
|
95
|
+
"codex-app-ssh": {
|
|
96
|
+
"display_name": "Codex App over SSH",
|
|
97
|
+
"host_loop": "visible Codex App /goal when host automation is unavailable over SSH",
|
|
98
|
+
"entry": "$loopx <task> or the explicit LoopX skill from /skills",
|
|
99
|
+
"accepted_inputs": [
|
|
100
|
+
"codex-app-ssh",
|
|
101
|
+
"codex_app_ssh",
|
|
102
|
+
"codex app ssh",
|
|
103
|
+
"codex-ssh",
|
|
104
|
+
"codex ssh",
|
|
105
|
+
"codex-app-remote",
|
|
106
|
+
"codex app remote",
|
|
107
|
+
],
|
|
108
|
+
},
|
|
109
|
+
"codex-cli": {
|
|
110
|
+
"display_name": "Codex CLI TUI",
|
|
111
|
+
"host_loop": "visible Codex CLI /goal",
|
|
112
|
+
"entry": "$loopx <task> or the explicit LoopX skill from /skills",
|
|
113
|
+
"accepted_inputs": [
|
|
114
|
+
"codex-cli",
|
|
115
|
+
"codex_cli",
|
|
116
|
+
"codex cli",
|
|
117
|
+
"codex-cli-tui",
|
|
118
|
+
"codex_cli_tui",
|
|
119
|
+
"codex tui",
|
|
120
|
+
],
|
|
121
|
+
},
|
|
122
|
+
"codex-ide-plugin": {
|
|
123
|
+
"display_name": "Codex IDE plugin",
|
|
124
|
+
"host_loop": "visible Codex IDE plugin /goal",
|
|
125
|
+
"entry": "$loopx <task> or the explicit LoopX skill from /skills",
|
|
126
|
+
"accepted_inputs": [
|
|
127
|
+
"codex-ide-plugin",
|
|
128
|
+
"codex_ide_plugin",
|
|
129
|
+
"codex ide plugin",
|
|
130
|
+
"codex-ide",
|
|
131
|
+
"codex_ide",
|
|
132
|
+
"codex ide",
|
|
133
|
+
"codex-ide-extension",
|
|
134
|
+
"codex ide extension",
|
|
135
|
+
"codex-vscode",
|
|
136
|
+
"codex vscode",
|
|
137
|
+
"vscode-codex",
|
|
138
|
+
"vscode codex",
|
|
139
|
+
],
|
|
140
|
+
},
|
|
141
|
+
"claude-code": {
|
|
142
|
+
"display_name": "Claude Code",
|
|
143
|
+
"host_loop": "native /loop gated by LoopX",
|
|
144
|
+
"entry": "/loopx <task> then /loop",
|
|
145
|
+
"accepted_inputs": ["claude-code", "claude_code", "claude code", "cc"],
|
|
146
|
+
},
|
|
147
|
+
"opencode": {
|
|
148
|
+
"display_name": "OpenCode",
|
|
149
|
+
"host_loop": "visible OpenCode goal plugin gated by LoopX",
|
|
150
|
+
"entry": "/loopx <task> with the LoopX OpenCode bridge installed",
|
|
151
|
+
"accepted_inputs": ["opencode", "open-code", "open_code", "open code"],
|
|
152
|
+
},
|
|
153
|
+
"opencode2": {
|
|
154
|
+
"display_name": "OpenCode 2",
|
|
155
|
+
"host_loop": "visible OpenCode 2 session driven by the LoopX goal worker",
|
|
156
|
+
"entry": "/loopx <task> with OpenCode 2 and the LoopX goal worker",
|
|
157
|
+
"accepted_inputs": [
|
|
158
|
+
"opencode2",
|
|
159
|
+
"opencode-2",
|
|
160
|
+
"opencode_2",
|
|
161
|
+
"open-code-2",
|
|
162
|
+
"open code 2",
|
|
163
|
+
],
|
|
164
|
+
},
|
|
165
|
+
"traex-cli": {
|
|
166
|
+
"display_name": "TraeX CLI TUI",
|
|
167
|
+
"host_loop": "visible TraeX /goal gated by LoopX",
|
|
168
|
+
"entry": "$loopx <task> or the explicit LoopX skill from /skills",
|
|
169
|
+
"accepted_inputs": [
|
|
170
|
+
"traex-cli",
|
|
171
|
+
"traex_cli",
|
|
172
|
+
"traex cli",
|
|
173
|
+
"traex",
|
|
174
|
+
"traex-cli-tui",
|
|
175
|
+
"traex tui",
|
|
176
|
+
"trae-cli",
|
|
177
|
+
"trae_cli",
|
|
178
|
+
"trae cli",
|
|
179
|
+
],
|
|
180
|
+
},
|
|
181
|
+
"pi": {
|
|
182
|
+
"display_name": "Pi",
|
|
183
|
+
"host_loop": "visible Pi goal extension gated by LoopX",
|
|
184
|
+
"entry": "/loopx <task> with the LoopX Pi extension installed",
|
|
185
|
+
"accepted_inputs": [
|
|
186
|
+
"pi",
|
|
187
|
+
"pi-agent",
|
|
188
|
+
"pi_agent",
|
|
189
|
+
"pi agent",
|
|
190
|
+
"earendil-pi",
|
|
191
|
+
"earendil pi",
|
|
192
|
+
],
|
|
193
|
+
},
|
|
194
|
+
"gemini-cli": {
|
|
195
|
+
"display_name": "Gemini CLI",
|
|
196
|
+
"host_loop": "agent-driven Gemini CLI loop gated by LoopX quota should-run",
|
|
197
|
+
"entry": "the LoopX skill installed in GEMINI_HOME/skills",
|
|
198
|
+
"accepted_inputs": [
|
|
199
|
+
"gemini-cli",
|
|
200
|
+
"gemini_cli",
|
|
201
|
+
"gemini cli",
|
|
202
|
+
"gemini",
|
|
203
|
+
"gemini-code",
|
|
204
|
+
"gemini code",
|
|
205
|
+
"google-gemini",
|
|
206
|
+
"google gemini",
|
|
207
|
+
],
|
|
208
|
+
},
|
|
209
|
+
"cursor-agent": {
|
|
210
|
+
"display_name": "Cursor Agent CLI",
|
|
211
|
+
"host_loop": "agent-driven cursor-agent loop gated by LoopX quota should-run",
|
|
212
|
+
"entry": "the LoopX skill installed in CURSOR_HOME/skills, with the LoopX MCP server registered",
|
|
213
|
+
"accepted_inputs": [
|
|
214
|
+
"cursor-agent",
|
|
215
|
+
"cursor_agent",
|
|
216
|
+
"cursor agent",
|
|
217
|
+
"cursor",
|
|
218
|
+
"cursor-cli",
|
|
219
|
+
"cursor cli",
|
|
220
|
+
],
|
|
221
|
+
},
|
|
222
|
+
"deepseek-harness": {
|
|
223
|
+
"display_name": "DeepSeek Harness",
|
|
224
|
+
"host_loop": "DeepSeek Harness headless/automation loop gated by LoopX quota",
|
|
225
|
+
"entry": "loopx turn run-once with scripts/dsh_turn_host_adapter.py",
|
|
226
|
+
"accepted_inputs": [
|
|
227
|
+
"deepseek-harness",
|
|
228
|
+
"deepseek_harness",
|
|
229
|
+
"deepseek harness",
|
|
230
|
+
"dsh",
|
|
231
|
+
],
|
|
232
|
+
},
|
|
233
|
+
"manual": {
|
|
234
|
+
"display_name": "Manual shell / external scheduler",
|
|
235
|
+
"host_loop": "external scheduler or manual quota/status loop",
|
|
236
|
+
"entry": "CLI packet plus an external loop driver",
|
|
237
|
+
"accepted_inputs": ["manual", "shell", "manual-shell", "external-scheduler"],
|
|
238
|
+
},
|
|
239
|
+
"other-agent": {
|
|
240
|
+
"display_name": "Other explicit agent host",
|
|
241
|
+
"host_loop": "custom agent loop driver",
|
|
242
|
+
"entry": "@loopx <task>, $loopx <task>, or another host facade",
|
|
243
|
+
"accepted_inputs": ["other-agent", "other_agent", "custom-agent", "custom agent"],
|
|
244
|
+
},
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
AMBIGUOUS_AGENT_TYPE_INPUTS: dict[str, list[str]] = {
|
|
248
|
+
"codex": ["codex-app", "codex-app-ssh", "codex-ide-plugin", "codex-cli"],
|
|
249
|
+
"openai-codex": ["codex-app", "codex-app-ssh", "codex-ide-plugin", "codex-cli"],
|
|
250
|
+
"openai codex": ["codex-app", "codex-app-ssh", "codex-ide-plugin", "codex-cli"],
|
|
251
|
+
"cli": ["codex-cli", "manual", "other-agent"],
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def _agent_type_key(value: str | None) -> str:
|
|
256
|
+
return (value or "").strip().lower().replace("_", "-")
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
AGENT_TYPE_ALIASES = {
|
|
260
|
+
_agent_type_key(alias): canonical
|
|
261
|
+
for canonical, metadata in AGENT_TYPE_CATALOG.items()
|
|
262
|
+
for alias in metadata["accepted_inputs"]
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
class AgentTypeError(ValueError):
|
|
267
|
+
def __init__(self, *, value: str | None, reason: str, suggestions: list[str] | None = None) -> None:
|
|
268
|
+
self.value = value
|
|
269
|
+
self.reason = reason
|
|
270
|
+
self.suggestions = suggestions or []
|
|
271
|
+
super().__init__(reason)
|
|
272
|
+
|
|
273
|
+
def to_payload(self) -> dict[str, Any]:
|
|
274
|
+
return {
|
|
275
|
+
"ok": False,
|
|
276
|
+
"schema_version": "loopx_agent_type_error_v0",
|
|
277
|
+
"error_kind": "ambiguous_or_unsupported_agent_type",
|
|
278
|
+
"agent_type": self.value,
|
|
279
|
+
"reason": self.reason,
|
|
280
|
+
"suggestions": self.suggestions,
|
|
281
|
+
"agent_type_catalog": build_agent_type_catalog(),
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
HOST_SURFACE_TO_AGENT_TYPE = {
|
|
286
|
+
"ark-managed-agent": "ark-managed-agent",
|
|
287
|
+
"ark_managed_agent": "ark-managed-agent",
|
|
288
|
+
"codex-app": "codex-app",
|
|
289
|
+
"codex-app-ssh": "codex-app-ssh",
|
|
290
|
+
"chat-box": "codex-app",
|
|
291
|
+
"codex-ide-plugin": "codex-ide-plugin",
|
|
292
|
+
"codex-ide": "codex-ide-plugin",
|
|
293
|
+
"codex-cli-tui": "codex-cli",
|
|
294
|
+
"claude-code": "claude-code",
|
|
295
|
+
"opencode": "opencode",
|
|
296
|
+
"opencode2": "opencode2",
|
|
297
|
+
"opencode-v2": "opencode2",
|
|
298
|
+
"opencode_2": "opencode2",
|
|
299
|
+
"traex-cli": "traex-cli",
|
|
300
|
+
"traex-cli-tui": "traex-cli",
|
|
301
|
+
"traex": "traex-cli",
|
|
302
|
+
"pi": "pi",
|
|
303
|
+
"pi-tui": "pi",
|
|
304
|
+
"gemini-cli": "gemini-cli",
|
|
305
|
+
"gemini": "gemini-cli",
|
|
306
|
+
"cursor-agent": "cursor-agent",
|
|
307
|
+
"cursor": "cursor-agent",
|
|
308
|
+
"deepseek-harness": "deepseek-harness",
|
|
309
|
+
"dsh": "deepseek-harness",
|
|
310
|
+
"shell": "manual",
|
|
311
|
+
"http": "other-agent",
|
|
312
|
+
"worker-bridge": "other-agent",
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
|
|
316
|
+
def build_agent_type_catalog() -> dict[str, Any]:
|
|
317
|
+
return {
|
|
318
|
+
"ok": True,
|
|
319
|
+
"schema_version": AGENT_TYPE_CATALOG_SCHEMA_VERSION,
|
|
320
|
+
"canonical_agent_types": [
|
|
321
|
+
{
|
|
322
|
+
"agent_type": agent_type,
|
|
323
|
+
"display_name": metadata["display_name"],
|
|
324
|
+
"host_loop": metadata["host_loop"],
|
|
325
|
+
"entry": metadata["entry"],
|
|
326
|
+
"accepted_inputs": metadata["accepted_inputs"],
|
|
327
|
+
}
|
|
328
|
+
for agent_type, metadata in AGENT_TYPE_CATALOG.items()
|
|
329
|
+
],
|
|
330
|
+
"ambiguous_inputs": [
|
|
331
|
+
{"input": value, "use_one_of": choices}
|
|
332
|
+
for value, choices in AMBIGUOUS_AGENT_TYPE_INPUTS.items()
|
|
333
|
+
],
|
|
334
|
+
"selection_rule": (
|
|
335
|
+
"Agents should pass a canonical agent_type. Ambiguous values such as "
|
|
336
|
+
"`codex` are rejected because Codex App automation, Codex App over SSH, "
|
|
337
|
+
"the Codex IDE plugin, and Codex CLI have different "
|
|
338
|
+
"host-loop activation paths."
|
|
339
|
+
),
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def render_agent_type_catalog_markdown(payload: dict[str, Any]) -> str:
|
|
344
|
+
if not payload.get("ok") and isinstance(payload.get("agent_type_catalog"), dict):
|
|
345
|
+
catalog = payload["agent_type_catalog"]
|
|
346
|
+
header = [
|
|
347
|
+
"# LoopX Agent Type Error",
|
|
348
|
+
"",
|
|
349
|
+
f"- ok: `{payload.get('ok')}`",
|
|
350
|
+
f"- error_kind: `{payload.get('error_kind')}`",
|
|
351
|
+
f"- agent_type: `{payload.get('agent_type')}`",
|
|
352
|
+
f"- reason: {payload.get('reason')}",
|
|
353
|
+
f"- suggestions: `{', '.join(payload.get('suggestions') or [])}`",
|
|
354
|
+
"",
|
|
355
|
+
]
|
|
356
|
+
return "\n".join(header) + render_agent_type_catalog_markdown(catalog)
|
|
357
|
+
lines = [
|
|
358
|
+
"# LoopX Agent Types",
|
|
359
|
+
"",
|
|
360
|
+
str(payload.get("selection_rule") or ""),
|
|
361
|
+
"",
|
|
362
|
+
"| agent_type | Host loop | Entry | Accepted inputs |",
|
|
363
|
+
"| --- | --- | --- | --- |",
|
|
364
|
+
]
|
|
365
|
+
for item in payload.get("canonical_agent_types") or []:
|
|
366
|
+
if not isinstance(item, dict):
|
|
367
|
+
continue
|
|
368
|
+
aliases = ", ".join(f"`{value}`" for value in item.get("accepted_inputs") or [])
|
|
369
|
+
lines.append(
|
|
370
|
+
"| "
|
|
371
|
+
f"`{item.get('agent_type')}` | "
|
|
372
|
+
f"{item.get('host_loop')} | "
|
|
373
|
+
f"{item.get('entry')} | "
|
|
374
|
+
f"{aliases} |"
|
|
375
|
+
)
|
|
376
|
+
lines.extend(["", "Ambiguous inputs:"])
|
|
377
|
+
for item in payload.get("ambiguous_inputs") or []:
|
|
378
|
+
if isinstance(item, dict):
|
|
379
|
+
choices = ", ".join(f"`{value}`" for value in item.get("use_one_of") or [])
|
|
380
|
+
lines.append(f"- `{item.get('input')}` -> use one of {choices}")
|
|
381
|
+
return "\n".join(lines)
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def normalize_agent_type(value: str | None) -> str:
|
|
385
|
+
key = _agent_type_key(value)
|
|
386
|
+
if not key:
|
|
387
|
+
raise AgentTypeError(
|
|
388
|
+
value=value,
|
|
389
|
+
reason="agent_type is required",
|
|
390
|
+
suggestions=SUPPORTED_AGENT_TYPES,
|
|
391
|
+
)
|
|
392
|
+
if key in AMBIGUOUS_AGENT_TYPE_INPUTS:
|
|
393
|
+
suggestions = AMBIGUOUS_AGENT_TYPE_INPUTS[key]
|
|
394
|
+
raise AgentTypeError(
|
|
395
|
+
value=value,
|
|
396
|
+
reason=(
|
|
397
|
+
f"agent_type {value!r} is ambiguous; choose the exact host runtime "
|
|
398
|
+
f"because each one has a different host_loop_activation"
|
|
399
|
+
),
|
|
400
|
+
suggestions=suggestions,
|
|
401
|
+
)
|
|
402
|
+
try:
|
|
403
|
+
return AGENT_TYPE_ALIASES[key]
|
|
404
|
+
except KeyError as exc:
|
|
405
|
+
raise AgentTypeError(
|
|
406
|
+
value=value,
|
|
407
|
+
reason=f"unsupported agent_type {value!r}",
|
|
408
|
+
suggestions=SUPPORTED_AGENT_TYPES,
|
|
409
|
+
) from exc
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def agent_type_for_host_surface(value: str | None) -> str:
|
|
413
|
+
key = (value or "codex-app").strip().lower()
|
|
414
|
+
if key in HOST_SURFACE_TO_AGENT_TYPE:
|
|
415
|
+
return HOST_SURFACE_TO_AGENT_TYPE[key]
|
|
416
|
+
return normalize_agent_type(key)
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
def _heartbeat_commands(
|
|
420
|
+
*,
|
|
421
|
+
goal_id: str,
|
|
422
|
+
agent_type: str,
|
|
423
|
+
cli_bin: str,
|
|
424
|
+
agent_id: str | None,
|
|
425
|
+
available_capabilities: list[str] | None = None,
|
|
426
|
+
) -> dict[str, str]:
|
|
427
|
+
scope_by_type = {
|
|
428
|
+
"ark-managed-agent": "Ark Managed Agent one-shot Goal activation",
|
|
429
|
+
"codex-app": "Codex App heartbeat automation",
|
|
430
|
+
"codex-app-ssh": "Codex App SSH /goal visible task loop",
|
|
431
|
+
"codex-ide-plugin": "Codex IDE plugin /goal visible task loop",
|
|
432
|
+
"codex-cli": "Codex CLI /goal visible TUI loop",
|
|
433
|
+
"claude-code": "Claude Code native /loop gated by LoopX",
|
|
434
|
+
"opencode": "OpenCode visible goal loop gated by LoopX",
|
|
435
|
+
"opencode2": "OpenCode 2 visible goal loop driven by the LoopX worker",
|
|
436
|
+
"traex-cli": "TraeX CLI /goal visible TUI loop gated by LoopX",
|
|
437
|
+
"pi": "Pi visible goal loop gated by LoopX",
|
|
438
|
+
"gemini-cli": "Gemini CLI agent loop gated by LoopX",
|
|
439
|
+
"cursor-agent": "Cursor Agent CLI loop gated by LoopX",
|
|
440
|
+
"deepseek-harness": "DeepSeek Harness automation loop gated by LoopX",
|
|
441
|
+
"manual": "External scheduler or manual shell LoopX poll",
|
|
442
|
+
"other-agent": "Custom agent host loop gated by LoopX",
|
|
443
|
+
}
|
|
444
|
+
agent_scope = scope_by_type.get(agent_type, scope_by_type["other-agent"])
|
|
445
|
+
scheduler_binding = scheduler_command_binding_for_agent_type(agent_type)
|
|
446
|
+
renderer_binding = (
|
|
447
|
+
{"visible_goal_host": "traex-cli"}
|
|
448
|
+
if agent_type == "traex-cli"
|
|
449
|
+
else {}
|
|
450
|
+
)
|
|
451
|
+
commands = {
|
|
452
|
+
"heartbeat_prompt_json": render_heartbeat_prompt_json_command(
|
|
453
|
+
goal_id,
|
|
454
|
+
cli_bin=cli_bin,
|
|
455
|
+
agent_id=agent_id,
|
|
456
|
+
agent_scope=agent_scope,
|
|
457
|
+
available_capabilities=available_capabilities,
|
|
458
|
+
**scheduler_binding,
|
|
459
|
+
**renderer_binding,
|
|
460
|
+
),
|
|
461
|
+
"heartbeat_prompt": render_heartbeat_prompt_command(
|
|
462
|
+
goal_id,
|
|
463
|
+
cli_bin=cli_bin,
|
|
464
|
+
agent_id=agent_id,
|
|
465
|
+
agent_scope=agent_scope,
|
|
466
|
+
available_capabilities=available_capabilities,
|
|
467
|
+
**scheduler_binding,
|
|
468
|
+
**renderer_binding,
|
|
469
|
+
),
|
|
470
|
+
}
|
|
471
|
+
if renderer_binding:
|
|
472
|
+
commands["visible_goal_prompt_json"] = commands["heartbeat_prompt_json"]
|
|
473
|
+
return commands
|
|
474
|
+
|
|
475
|
+
|
|
476
|
+
def _identity_state(
|
|
477
|
+
*,
|
|
478
|
+
agent_id: str | None,
|
|
479
|
+
registered_agents: list[str] | None,
|
|
480
|
+
fresh_agent_default: bool,
|
|
481
|
+
thread_binding: dict[str, Any] | None = None,
|
|
482
|
+
) -> dict[str, Any]:
|
|
483
|
+
registered = normalize_registered_agents(registered_agents)
|
|
484
|
+
selected = normalize_todo_claimed_by(agent_id)
|
|
485
|
+
|
|
486
|
+
def identity_payload(values: dict[str, Any]) -> dict[str, Any]:
|
|
487
|
+
return {
|
|
488
|
+
"schema_version": IDENTITY_SELECTION_SCHEMA_VERSION,
|
|
489
|
+
"agent_model": "peer_v1",
|
|
490
|
+
**values,
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
binding = thread_binding if isinstance(thread_binding, dict) else {}
|
|
494
|
+
binding_status = str(binding.get("status") or "")
|
|
495
|
+
bound_agent = normalize_todo_claimed_by(binding.get("agent_id"))
|
|
496
|
+
if binding_status == "conflict":
|
|
497
|
+
return identity_payload(
|
|
498
|
+
{
|
|
499
|
+
"state": "thread_binding_conflict",
|
|
500
|
+
"activation_allowed": False,
|
|
501
|
+
"selected_agent_id": None,
|
|
502
|
+
"requested_agent_id": selected,
|
|
503
|
+
"registered_agents": registered,
|
|
504
|
+
"action_required": True,
|
|
505
|
+
"thread_binding": binding,
|
|
506
|
+
"reason": (
|
|
507
|
+
"the current host thread is bound to conflicting agent lanes; repair the "
|
|
508
|
+
"binding before host-loop activation"
|
|
509
|
+
),
|
|
510
|
+
"required_cli_arg": "--agent-id <registered-agent-id>",
|
|
511
|
+
}
|
|
512
|
+
)
|
|
513
|
+
if binding_status == "bound":
|
|
514
|
+
if not bound_agent or bound_agent not in registered:
|
|
515
|
+
return identity_payload(
|
|
516
|
+
{
|
|
517
|
+
"state": "thread_binding_invalid",
|
|
518
|
+
"activation_allowed": False,
|
|
519
|
+
"selected_agent_id": None,
|
|
520
|
+
"requested_agent_id": selected,
|
|
521
|
+
"registered_agents": registered,
|
|
522
|
+
"action_required": True,
|
|
523
|
+
"thread_binding": binding,
|
|
524
|
+
"reason": "the current host thread binding points to an unregistered agent",
|
|
525
|
+
"required_cli_arg": "--agent-id <registered-agent-id>",
|
|
526
|
+
}
|
|
527
|
+
)
|
|
528
|
+
if selected and selected != bound_agent:
|
|
529
|
+
if selected not in registered:
|
|
530
|
+
return identity_payload(
|
|
531
|
+
{
|
|
532
|
+
"state": "invalid_selection",
|
|
533
|
+
"activation_allowed": False,
|
|
534
|
+
"selected_agent_id": None,
|
|
535
|
+
"requested_agent_id": selected,
|
|
536
|
+
"registered_agents": registered,
|
|
537
|
+
"action_required": True,
|
|
538
|
+
"thread_binding": binding,
|
|
539
|
+
"reason": f"agent_id={selected!r} is not registered for this goal",
|
|
540
|
+
"required_cli_arg": "--agent-id <registered-agent-id>",
|
|
541
|
+
}
|
|
542
|
+
)
|
|
543
|
+
return identity_payload(
|
|
544
|
+
{
|
|
545
|
+
"state": "explicit_agent_selected",
|
|
546
|
+
"activation_allowed": True,
|
|
547
|
+
"selected_agent_id": selected,
|
|
548
|
+
"registered_agents": registered,
|
|
549
|
+
"action_required": False,
|
|
550
|
+
"thread_binding": binding,
|
|
551
|
+
"binding_override": True,
|
|
552
|
+
}
|
|
553
|
+
)
|
|
554
|
+
return identity_payload(
|
|
555
|
+
{
|
|
556
|
+
"state": "thread_binding_selected",
|
|
557
|
+
"activation_allowed": True,
|
|
558
|
+
"selected_agent_id": bound_agent,
|
|
559
|
+
"registered_agents": registered,
|
|
560
|
+
"action_required": False,
|
|
561
|
+
"thread_binding": binding,
|
|
562
|
+
}
|
|
563
|
+
)
|
|
564
|
+
if (
|
|
565
|
+
(binding_status == "missing" or binding.get("selection_required"))
|
|
566
|
+
and not selected
|
|
567
|
+
and not fresh_agent_default
|
|
568
|
+
):
|
|
569
|
+
return identity_payload(
|
|
570
|
+
{
|
|
571
|
+
"state": "thread_binding_selection_required",
|
|
572
|
+
"activation_allowed": False,
|
|
573
|
+
"selected_agent_id": None,
|
|
574
|
+
"registered_agents": registered,
|
|
575
|
+
"action_required": True,
|
|
576
|
+
"thread_binding": binding,
|
|
577
|
+
"reason": (
|
|
578
|
+
"current host thread has no stored agent binding or stable thread id; "
|
|
579
|
+
"select an existing lane and do not register a new one unless a new "
|
|
580
|
+
"peer/session was explicitly requested"
|
|
581
|
+
),
|
|
582
|
+
"required_cli_arg": "--agent-id <registered-agent-id>",
|
|
583
|
+
}
|
|
584
|
+
)
|
|
585
|
+
|
|
586
|
+
if fresh_agent_default and not selected:
|
|
587
|
+
return identity_payload(
|
|
588
|
+
{
|
|
589
|
+
"state": "fresh_agent_registration_required",
|
|
590
|
+
"activation_allowed": False,
|
|
591
|
+
"selected_agent_id": None,
|
|
592
|
+
"registered_agents": registered,
|
|
593
|
+
"action_required": True,
|
|
594
|
+
"reason": (
|
|
595
|
+
"new agent onboarding has no explicit identity; register a fresh "
|
|
596
|
+
"public-safe agent id by default. Reuse an existing identity only "
|
|
597
|
+
"when the user explicitly requests takeover of that exact agent"
|
|
598
|
+
),
|
|
599
|
+
"required_cli_arg": "--agent-id <freshly-registered-agent-id>",
|
|
600
|
+
}
|
|
601
|
+
)
|
|
602
|
+
if fresh_agent_default and selected not in registered:
|
|
603
|
+
return identity_payload(
|
|
604
|
+
{
|
|
605
|
+
"state": "invalid_selection",
|
|
606
|
+
"activation_allowed": False,
|
|
607
|
+
"selected_agent_id": None,
|
|
608
|
+
"requested_agent_id": selected,
|
|
609
|
+
"registered_agents": registered,
|
|
610
|
+
"action_required": True,
|
|
611
|
+
"reason": (
|
|
612
|
+
f"agent_id={selected!r} is not registered for this goal; register "
|
|
613
|
+
"that fresh identity before host-loop activation"
|
|
614
|
+
),
|
|
615
|
+
"required_cli_arg": "--agent-id <freshly-registered-agent-id>",
|
|
616
|
+
}
|
|
617
|
+
)
|
|
618
|
+
if not registered:
|
|
619
|
+
return identity_payload(
|
|
620
|
+
{
|
|
621
|
+
"state": "legacy_unscoped",
|
|
622
|
+
"activation_allowed": True,
|
|
623
|
+
"selected_agent_id": selected,
|
|
624
|
+
"registered_agents": [],
|
|
625
|
+
"action_required": False,
|
|
626
|
+
}
|
|
627
|
+
)
|
|
628
|
+
if not selected and len(registered) == 1:
|
|
629
|
+
selected = registered[0]
|
|
630
|
+
return identity_payload(
|
|
631
|
+
{
|
|
632
|
+
"state": "single_registered_agent_selected",
|
|
633
|
+
"activation_allowed": True,
|
|
634
|
+
"selected_agent_id": selected,
|
|
635
|
+
"registered_agents": registered,
|
|
636
|
+
"action_required": False,
|
|
637
|
+
}
|
|
638
|
+
)
|
|
639
|
+
if selected in registered:
|
|
640
|
+
return identity_payload(
|
|
641
|
+
{
|
|
642
|
+
"state": "selected",
|
|
643
|
+
"activation_allowed": True,
|
|
644
|
+
"selected_agent_id": selected,
|
|
645
|
+
"registered_agents": registered,
|
|
646
|
+
"action_required": False,
|
|
647
|
+
}
|
|
648
|
+
)
|
|
649
|
+
return identity_payload(
|
|
650
|
+
{
|
|
651
|
+
"state": "invalid_selection" if selected else "selection_required",
|
|
652
|
+
"activation_allowed": False,
|
|
653
|
+
"selected_agent_id": None,
|
|
654
|
+
"requested_agent_id": selected,
|
|
655
|
+
"registered_agents": registered,
|
|
656
|
+
"action_required": True,
|
|
657
|
+
"reason": (
|
|
658
|
+
f"agent_id={selected!r} is not registered for this goal"
|
|
659
|
+
if selected
|
|
660
|
+
else "multiple registered agent lanes exist; select one before host-loop activation"
|
|
661
|
+
),
|
|
662
|
+
"required_cli_arg": "--agent-id <registered-agent-id>",
|
|
663
|
+
}
|
|
664
|
+
)
|
|
665
|
+
|
|
666
|
+
|
|
667
|
+
def _codex_app_activation(commands: dict[str, str]) -> dict[str, Any]:
|
|
668
|
+
return {
|
|
669
|
+
"host_surface": "codex_app_heartbeat_automation",
|
|
670
|
+
"entry_command_hint": "$loopx <task> or the explicit LoopX skill from /skills",
|
|
671
|
+
"activation_method": "create_or_update_codex_app_automation",
|
|
672
|
+
"activation_input_command": commands["heartbeat_prompt_json"],
|
|
673
|
+
"host_mutation": {
|
|
674
|
+
"owner": "Codex App host",
|
|
675
|
+
"preferred_tool": "automation_update",
|
|
676
|
+
"cli_can_mutate_directly": False,
|
|
677
|
+
"missing_host_tool_gate": (
|
|
678
|
+
"Codex App automation_update is unavailable; surface a pasteable "
|
|
679
|
+
"heartbeat task_body gate instead of claiming autonomous setup."
|
|
680
|
+
),
|
|
681
|
+
},
|
|
682
|
+
"activation_steps": [
|
|
683
|
+
"Run the heartbeat-prompt JSON command after project state and todos are written.",
|
|
684
|
+
"Read task_body from the JSON payload.",
|
|
685
|
+
"Create or update a Codex App heartbeat automation starting at 3 minutes.",
|
|
686
|
+
"On later ticks, follow quota should-run scheduler_hint for backoff, reset, and scheduler-ack.",
|
|
687
|
+
],
|
|
688
|
+
"success_criteria": [
|
|
689
|
+
"A Codex App heartbeat automation exists for this goal and uses the generated task_body.",
|
|
690
|
+
"The next wakeup starts from LoopX quota/status/state, not stale chat memory.",
|
|
691
|
+
],
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
|
|
695
|
+
def _ark_managed_agent_activation(commands: dict[str, str]) -> dict[str, Any]:
|
|
696
|
+
return {
|
|
697
|
+
"host_surface": "ark_managed_agent_goal_mode",
|
|
698
|
+
"entry_command_hint": "submit the generated task_body as one Goal",
|
|
699
|
+
"activation_method": "submit_goal_once",
|
|
700
|
+
"activation_input_command": commands["heartbeat_prompt_json"],
|
|
701
|
+
"host_mutation": {
|
|
702
|
+
"owner": "Ark Managed Agent Goal host",
|
|
703
|
+
"transport_contract": "goal_prompt_v0",
|
|
704
|
+
"prompt_field": "task_body",
|
|
705
|
+
"cli_can_mutate_directly": False,
|
|
706
|
+
"missing_host_tool_gate": (
|
|
707
|
+
"No Goal transport is available; surface the generated task_body "
|
|
708
|
+
"without claiming host activation."
|
|
709
|
+
),
|
|
710
|
+
},
|
|
711
|
+
"activation_steps": [
|
|
712
|
+
"Run the heartbeat-prompt JSON command after project state and todos are written.",
|
|
713
|
+
"Read task_body from the JSON payload.",
|
|
714
|
+
"Submit that exact task_body once through either the local-development or cloud Goal transport.",
|
|
715
|
+
"Let the Goal runtime own inner iterations; do not wrap them in LoopX Turn.",
|
|
716
|
+
"Read runtime_capability_reentry_v0 from quota tool results; do not rewrite task_body.",
|
|
717
|
+
],
|
|
718
|
+
"success_criteria": [
|
|
719
|
+
"The selected transport submitted the generated task_body exactly once.",
|
|
720
|
+
"The Goal runtime owns continuation while LoopX state remains authoritative.",
|
|
721
|
+
"Runtime capability repair remains outside the Goal prompt.",
|
|
722
|
+
],
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
|
|
726
|
+
def _codex_goal_activation(
|
|
727
|
+
commands: dict[str, str],
|
|
728
|
+
*,
|
|
729
|
+
host_label: str,
|
|
730
|
+
host_surface: str,
|
|
731
|
+
) -> dict[str, Any]:
|
|
732
|
+
return {
|
|
733
|
+
"host_surface": host_surface,
|
|
734
|
+
"entry_command_hint": "$loopx <task> or the explicit LoopX skill from /skills",
|
|
735
|
+
"activation_method": "set_visible_goal",
|
|
736
|
+
"activation_input_command": commands["heartbeat_prompt_json"],
|
|
737
|
+
"host_mutation": {
|
|
738
|
+
"owner": host_label,
|
|
739
|
+
"host_command": "/goal <task_body>",
|
|
740
|
+
"cli_can_mutate_directly": False,
|
|
741
|
+
"missing_host_tool_gate": (
|
|
742
|
+
f"Current session cannot set {host_label} /goal; show the exact "
|
|
743
|
+
"`/goal <task_body>` text for the user to paste."
|
|
744
|
+
),
|
|
745
|
+
},
|
|
746
|
+
"activation_steps": [
|
|
747
|
+
"Run the heartbeat-prompt JSON command after project state and todos are written.",
|
|
748
|
+
"Read task_body from the JSON payload.",
|
|
749
|
+
f"Set the visible {host_label} goal to `/goal <task_body>`.",
|
|
750
|
+
"Keep delivery in the visible task; do not switch to hidden headless execution.",
|
|
751
|
+
],
|
|
752
|
+
"success_criteria": [
|
|
753
|
+
f"The visible {host_label} has `/goal <task_body>` active for this goal.",
|
|
754
|
+
"Future goal turns enter through LoopX quota/status/state before delivery work.",
|
|
755
|
+
],
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
|
|
759
|
+
def _codex_cli_activation(commands: dict[str, str]) -> dict[str, Any]:
|
|
760
|
+
return _codex_goal_activation(
|
|
761
|
+
commands,
|
|
762
|
+
host_label="Codex CLI TUI",
|
|
763
|
+
host_surface="codex_cli_visible_goal_mode",
|
|
764
|
+
)
|
|
765
|
+
|
|
766
|
+
|
|
767
|
+
def _codex_app_ssh_activation(commands: dict[str, str]) -> dict[str, Any]:
|
|
768
|
+
activation = _codex_goal_activation(
|
|
769
|
+
commands,
|
|
770
|
+
host_label="Codex App SSH task",
|
|
771
|
+
host_surface="codex_app_ssh_visible_goal_mode",
|
|
772
|
+
)
|
|
773
|
+
activation["success_criteria"].append(
|
|
774
|
+
"After three unchanged blocked turns, native update_goal marks only the "
|
|
775
|
+
"host Goal blocked; LoopX remains active until user /goal resume."
|
|
776
|
+
)
|
|
777
|
+
return activation
|
|
778
|
+
|
|
779
|
+
|
|
780
|
+
def _codex_ide_activation(commands: dict[str, str]) -> dict[str, Any]:
|
|
781
|
+
return _codex_goal_activation(
|
|
782
|
+
commands,
|
|
783
|
+
host_label="Codex IDE plugin composer",
|
|
784
|
+
host_surface="codex_ide_visible_goal_mode",
|
|
785
|
+
)
|
|
786
|
+
|
|
787
|
+
|
|
788
|
+
def _claude_code_activation(commands: dict[str, str], cli_bin: str) -> dict[str, Any]:
|
|
789
|
+
return {
|
|
790
|
+
"host_surface": "claude_code_native_loop",
|
|
791
|
+
"entry_command_hint": "/loopx <task> then /loop",
|
|
792
|
+
"activation_method": "arm_loopx_then_run_native_loop",
|
|
793
|
+
"activation_input_command": commands["heartbeat_prompt_json"],
|
|
794
|
+
"setup_command": f"{cli_bin} slash-commands --install --surface claude-code",
|
|
795
|
+
"host_mutation": {
|
|
796
|
+
"owner": "Claude Code",
|
|
797
|
+
"host_command": "/loop",
|
|
798
|
+
"cli_can_mutate_directly": False,
|
|
799
|
+
"missing_host_tool_gate": (
|
|
800
|
+
"Claude Code adapter or native /loop is unavailable; install the "
|
|
801
|
+
"Claude Code LoopX surface or report the exact gate."
|
|
802
|
+
),
|
|
803
|
+
},
|
|
804
|
+
"activation_steps": [
|
|
805
|
+
"Install or refresh the Claude Code LoopX surface when needed.",
|
|
806
|
+
"Run `/loopx <task>` to arm LoopX state for the task.",
|
|
807
|
+
"Run native `/loop`; the adapter gates each tick through LoopX should_run.",
|
|
808
|
+
],
|
|
809
|
+
"success_criteria": [
|
|
810
|
+
"Claude Code has the LoopX command surface installed.",
|
|
811
|
+
"Native `/loop` is running with LoopX should_run gating, not an unrelated free-running loop.",
|
|
812
|
+
],
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
|
|
816
|
+
def _pi_activation(commands: dict[str, str], cli_bin: str) -> dict[str, Any]:
|
|
817
|
+
return {
|
|
818
|
+
"host_surface": "pi_visible_goal_mode",
|
|
819
|
+
"entry_command_hint": "/loopx <task>",
|
|
820
|
+
"activation_method": "activate_loopx_pi_goal_extension",
|
|
821
|
+
"activation_input_command": commands["heartbeat_prompt_json"],
|
|
822
|
+
"setup_command": f"{cli_bin} slash-commands --install --surface pi",
|
|
823
|
+
"host_mutation": {
|
|
824
|
+
"owner": "Pi LoopX goal extension",
|
|
825
|
+
"host_tool": "loopx_goal_activate",
|
|
826
|
+
"tool_argument_mapping": {
|
|
827
|
+
"goalId": "heartbeat_prompt.goal_id",
|
|
828
|
+
"objective": "heartbeat_prompt.task_body",
|
|
829
|
+
"agentId": "heartbeat_prompt.agent_id when present",
|
|
830
|
+
"registryPath": "explicit registry path when present",
|
|
831
|
+
"availableCapabilities": "declared host capabilities when present",
|
|
832
|
+
},
|
|
833
|
+
"cli_can_mutate_directly": False,
|
|
834
|
+
"missing_host_tool_gate": (
|
|
835
|
+
"The LoopX Pi extension or loopx_goal_activate tool is unavailable; "
|
|
836
|
+
"install the Pi surface and restart Pi before claiming autonomous "
|
|
837
|
+
"heartbeat support."
|
|
838
|
+
),
|
|
839
|
+
},
|
|
840
|
+
"activation_steps": [
|
|
841
|
+
"Install or refresh the LoopX Pi surface when needed.",
|
|
842
|
+
"Run the heartbeat-prompt JSON command after project state and todos are written.",
|
|
843
|
+
"Call loopx_goal_activate with goalId from goal_id, objective from task_body, and optional agentId, registryPath, or availableCapabilities when those values are present.",
|
|
844
|
+
"Let the extension gate every settled continuation and timer wake through LoopX quota should-run.",
|
|
845
|
+
],
|
|
846
|
+
"success_criteria": [
|
|
847
|
+
"The visible Pi session has a LoopX-backed goal bound through loopx_goal_activate.",
|
|
848
|
+
"Quiet waits make no model call, active work auto-continues, and validated terminal no-follow-up stops the goal.",
|
|
849
|
+
],
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
|
|
853
|
+
def _opencode_activation(commands: dict[str, str], cli_bin: str) -> dict[str, Any]:
|
|
854
|
+
return {
|
|
855
|
+
"host_surface": "opencode_visible_goal_mode",
|
|
856
|
+
"entry_command_hint": "/loopx <task>",
|
|
857
|
+
"activation_method": "activate_loopx_opencode_goal_bridge",
|
|
858
|
+
"activation_input_command": commands["heartbeat_prompt_json"],
|
|
859
|
+
"setup_command": (
|
|
860
|
+
f"{cli_bin} slash-commands --install --surface opencode --with-goal-bridge"
|
|
861
|
+
),
|
|
862
|
+
"host_mutation": {
|
|
863
|
+
"owner": "OpenCode LoopX goal bridge",
|
|
864
|
+
"host_tool": "loopx_goal_activate",
|
|
865
|
+
"tool_argument_mapping": {
|
|
866
|
+
"goalId": "heartbeat_prompt.goal_id",
|
|
867
|
+
"objective": "heartbeat_prompt.task_body",
|
|
868
|
+
"agentId": "heartbeat_prompt.agent_id when present",
|
|
869
|
+
"registryPath": "explicit registry path when present",
|
|
870
|
+
"availableCapabilities": "declared host capabilities when present",
|
|
871
|
+
},
|
|
872
|
+
"cli_can_mutate_directly": False,
|
|
873
|
+
"missing_host_tool_gate": (
|
|
874
|
+
"The LoopX OpenCode bridge or loopx_goal_activate tool is unavailable; "
|
|
875
|
+
"install the OpenCode surface and restart OpenCode before claiming "
|
|
876
|
+
"autonomous heartbeat support."
|
|
877
|
+
),
|
|
878
|
+
},
|
|
879
|
+
"activation_steps": [
|
|
880
|
+
"Install or refresh the LoopX OpenCode surface when needed.",
|
|
881
|
+
"Run the heartbeat-prompt JSON command after project state and todos are written.",
|
|
882
|
+
"Call loopx_goal_activate with goalId from goal_id, objective from task_body, and optional agentId, registryPath, or availableCapabilities when those values are present.",
|
|
883
|
+
"Let the bridge gate every idle continuation and timer wake through LoopX quota should-run.",
|
|
884
|
+
],
|
|
885
|
+
"success_criteria": [
|
|
886
|
+
"The visible OpenCode session has a LoopX-backed goal bound through loopx_goal_activate.",
|
|
887
|
+
"Quiet waits make no model call, active work auto-continues, and validated terminal no-follow-up stops the goal.",
|
|
888
|
+
],
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
|
|
892
|
+
def _opencode2_activation(commands: dict[str, str], cli_bin: str) -> dict[str, Any]:
|
|
893
|
+
return {
|
|
894
|
+
"host_surface": "opencode2_goal_worker_mode",
|
|
895
|
+
"entry_command_hint": "/loopx <task>",
|
|
896
|
+
"activation_method": "start_opencode2_goal_worker",
|
|
897
|
+
"activation_input_command": commands["heartbeat_prompt_json"],
|
|
898
|
+
"setup_command": None,
|
|
899
|
+
"host_mutation": {
|
|
900
|
+
"owner": "LoopX OpenCode 2 goal worker",
|
|
901
|
+
"host_tool": "opencode2-goal-worker",
|
|
902
|
+
"tool_argument_mapping": {
|
|
903
|
+
"goalId": "heartbeat_prompt.goal_id",
|
|
904
|
+
"directory": "the project directory",
|
|
905
|
+
"agentId": "heartbeat_prompt.agent_id when present",
|
|
906
|
+
"registryPath": "explicit registry path when present",
|
|
907
|
+
"availableCapabilities": "declared host capabilities when present",
|
|
908
|
+
"taskBody": "heartbeat_prompt.task_body",
|
|
909
|
+
"sessionId": "an existing OpenCode 2 session id when reattaching",
|
|
910
|
+
},
|
|
911
|
+
"cli_can_mutate_directly": True,
|
|
912
|
+
"missing_host_tool_gate": (
|
|
913
|
+
"The loopx opencode2-goal-worker command or the opencode2 binary "
|
|
914
|
+
"is unavailable; install LoopX and OpenCode 2 before claiming "
|
|
915
|
+
"autonomous heartbeat support."
|
|
916
|
+
),
|
|
917
|
+
},
|
|
918
|
+
"activation_steps": [
|
|
919
|
+
"Run the heartbeat-prompt JSON command after project state and todos are written.",
|
|
920
|
+
"Start the worker from the project directory: loopx opencode2-goal-worker --goal-id <goal_id> --directory . --task-body <task_body>, with --agent-id and --capability flags when those values are present.",
|
|
921
|
+
"Let the worker create or attach the visible OpenCode 2 session, gate every turn through LoopX quota should-run, and keep quiet waits free of model calls.",
|
|
922
|
+
],
|
|
923
|
+
"success_criteria": [
|
|
924
|
+
"A visible OpenCode 2 session runs the goal and the worker survives TUI close because it owns the timers.",
|
|
925
|
+
"Quiet waits make no model call, active work auto-continues, user intervention pauses visibly, and validated terminal no-follow-up stops the worker.",
|
|
926
|
+
],
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
|
|
930
|
+
def _traex_activation(commands: dict[str, str]) -> dict[str, Any]:
|
|
931
|
+
return {
|
|
932
|
+
"host_surface": "traex_visible_goal_mode",
|
|
933
|
+
"entry_command_hint": "$loopx <task> or the explicit LoopX skill from /skills",
|
|
934
|
+
"activation_method": "set_visible_goal",
|
|
935
|
+
"activation_input_command": commands["visible_goal_prompt_json"],
|
|
936
|
+
"host_mutation": {
|
|
937
|
+
"owner": "TraeX CLI TUI",
|
|
938
|
+
"host_command": "/goal <task_body>",
|
|
939
|
+
"cli_can_mutate_directly": False,
|
|
940
|
+
"requires_host_feature_flag": "[features] goals = true in ~/.trae/traecli.toml",
|
|
941
|
+
"missing_host_tool_gate": (
|
|
942
|
+
"TraeX /goal is unavailable; if goal mode is disabled, show the exact "
|
|
943
|
+
"`/goal <task_body>` text for the user to paste after enabling "
|
|
944
|
+
"`[features] goals = true`. Do not claim another host-loop surface "
|
|
945
|
+
"without a verified LoopX adapter."
|
|
946
|
+
),
|
|
947
|
+
},
|
|
948
|
+
"activation_steps": [
|
|
949
|
+
"Run the visible-goal prompt JSON command after project state and todos are written.",
|
|
950
|
+
"Read task_body from the JSON payload.",
|
|
951
|
+
"Set the visible TraeX goal to `/goal <task_body>`; enable `[features] goals = true` first if goal mode is off.",
|
|
952
|
+
"Keep delivery in the visible TUI turn; gate each continuation through LoopX quota should-run and do not switch to hidden headless execution.",
|
|
953
|
+
],
|
|
954
|
+
"success_criteria": [
|
|
955
|
+
"The visible TraeX TUI has `/goal <task_body>` active for this goal.",
|
|
956
|
+
"Future goal turns enter through LoopX quota/status/state before delivery work.",
|
|
957
|
+
],
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
|
|
961
|
+
def _skill_facade_cli_activation(
|
|
962
|
+
commands: dict[str, str],
|
|
963
|
+
cli_bin: str,
|
|
964
|
+
*,
|
|
965
|
+
host_label: str,
|
|
966
|
+
host_surface: str,
|
|
967
|
+
install_surface: str,
|
|
968
|
+
skills_root: str,
|
|
969
|
+
extra_host_mutation: dict[str, Any] | None = None,
|
|
970
|
+
extra_activation_steps: list[str] | None = None,
|
|
971
|
+
) -> dict[str, Any]:
|
|
972
|
+
"""Activation for a CLI host that LoopX reaches through a skill facade only.
|
|
973
|
+
|
|
974
|
+
Gemini CLI and cursor-agent have no goal primitive to bind: no `/goal` to
|
|
975
|
+
set, no extension tool to call, no host automation to schedule. What they do
|
|
976
|
+
have is skill discovery, so the loop driver is the agent's own turn loop and
|
|
977
|
+
LoopX gates it the only way it can — every continuation has to enter through
|
|
978
|
+
quota should-run. That is a weaker guarantee than a host-owned loop and is
|
|
979
|
+
stated as such, because claiming autonomous heartbeat support these hosts
|
|
980
|
+
cannot deliver is worse than admitting the agent drives itself.
|
|
981
|
+
"""
|
|
982
|
+
return {
|
|
983
|
+
"host_surface": host_surface,
|
|
984
|
+
"entry_command_hint": f"the LoopX skill installed in {skills_root}",
|
|
985
|
+
"activation_method": "run_agent_cli_loop_gated_by_quota",
|
|
986
|
+
"activation_input_command": commands["heartbeat_prompt_json"],
|
|
987
|
+
"setup_command": (
|
|
988
|
+
f"{cli_bin} slash-commands --install --surface {install_surface}"
|
|
989
|
+
),
|
|
990
|
+
"host_mutation": {
|
|
991
|
+
"owner": f"{host_label} session",
|
|
992
|
+
"host_loop_primitive": None,
|
|
993
|
+
"cli_can_mutate_directly": False,
|
|
994
|
+
"loop_driver": "agent_cli_turn_loop",
|
|
995
|
+
"missing_host_tool_gate": (
|
|
996
|
+
f"{host_label} exposes no goal or automation primitive for LoopX to "
|
|
997
|
+
"bind. If the session cannot keep entering through quota should-run, "
|
|
998
|
+
"show the exact heartbeat-prompt command for the user to run and do "
|
|
999
|
+
"not claim autonomous heartbeat support."
|
|
1000
|
+
),
|
|
1001
|
+
**(extra_host_mutation or {}),
|
|
1002
|
+
},
|
|
1003
|
+
"activation_steps": [
|
|
1004
|
+
f"Install or refresh the LoopX {host_label} surface when needed.",
|
|
1005
|
+
"Run the heartbeat-prompt JSON command after project state and todos are written.",
|
|
1006
|
+
"Read task_body from the JSON payload and carry it as the session objective.",
|
|
1007
|
+
*(extra_activation_steps or []),
|
|
1008
|
+
"Start every following turn with quota should-run and stop when it says stop; "
|
|
1009
|
+
"there is no host scheduler to fall back on.",
|
|
1010
|
+
],
|
|
1011
|
+
"success_criteria": [
|
|
1012
|
+
f"The {host_label} session has the LoopX skill facade installed and the "
|
|
1013
|
+
"generated task_body as its objective.",
|
|
1014
|
+
"Each continuation enters through LoopX quota/status/state, and a stop "
|
|
1015
|
+
"decision ends the session loop instead of free-running.",
|
|
1016
|
+
],
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
|
|
1020
|
+
def _gemini_cli_activation(commands: dict[str, str], cli_bin: str) -> dict[str, Any]:
|
|
1021
|
+
return _skill_facade_cli_activation(
|
|
1022
|
+
commands,
|
|
1023
|
+
cli_bin,
|
|
1024
|
+
host_label="Gemini CLI",
|
|
1025
|
+
host_surface="gemini_cli_agent_loop",
|
|
1026
|
+
install_surface="gemini",
|
|
1027
|
+
skills_root="GEMINI_HOME/skills",
|
|
1028
|
+
)
|
|
1029
|
+
|
|
1030
|
+
|
|
1031
|
+
def _cursor_agent_activation(commands: dict[str, str], cli_bin: str) -> dict[str, Any]:
|
|
1032
|
+
return _skill_facade_cli_activation(
|
|
1033
|
+
commands,
|
|
1034
|
+
cli_bin,
|
|
1035
|
+
host_label="Cursor Agent CLI",
|
|
1036
|
+
host_surface="cursor_agent_loop",
|
|
1037
|
+
install_surface="cursor",
|
|
1038
|
+
skills_root="CURSOR_HOME/skills",
|
|
1039
|
+
extra_host_mutation={
|
|
1040
|
+
# The MCP server is how a cursor-agent session reads LoopX state
|
|
1041
|
+
# without shelling out; the loop is still the agent's own turns.
|
|
1042
|
+
"host_mcp_server": "loopx",
|
|
1043
|
+
"host_mcp_config": "CURSOR_HOME/mcp.json",
|
|
1044
|
+
},
|
|
1045
|
+
extra_activation_steps=[
|
|
1046
|
+
"Confirm the `loopx` MCP server is enabled in this session "
|
|
1047
|
+
"(`cursor-agent mcp`); it is registered by the surface installer.",
|
|
1048
|
+
],
|
|
1049
|
+
)
|
|
1050
|
+
|
|
1051
|
+
|
|
1052
|
+
def _deepseek_harness_activation(commands: dict[str, str]) -> dict[str, Any]:
|
|
1053
|
+
return {
|
|
1054
|
+
"host_surface": "deepseek_harness_automation_loop",
|
|
1055
|
+
"entry_command_hint": "loopx turn run-once with scripts/dsh_turn_host_adapter.py",
|
|
1056
|
+
"activation_method": "external_loop_driver",
|
|
1057
|
+
"activation_input_command": commands["heartbeat_prompt_json"],
|
|
1058
|
+
"host_mutation": {
|
|
1059
|
+
"owner": "DeepSeek Harness adapter",
|
|
1060
|
+
"host_loop_primitive": "deepseek-harness-sdk",
|
|
1061
|
+
"cli_can_mutate_directly": False,
|
|
1062
|
+
"missing_host_tool_gate": (
|
|
1063
|
+
"DeepSeek Harness SDK or dsh runtime is unavailable; install "
|
|
1064
|
+
"`loopx[deepseek-harness]` and verify the dsh cordis configuration "
|
|
1065
|
+
"before claiming an automation loop."
|
|
1066
|
+
),
|
|
1067
|
+
},
|
|
1068
|
+
"activation_steps": [
|
|
1069
|
+
"Install the optional DeepSeek Harness SDK (`loopx[deepseek-harness]`).",
|
|
1070
|
+
"Prepare a dsh cordis.yml and any DEEPSEEK_API_KEY / DEEPSEEK_BASE_URL settings.",
|
|
1071
|
+
"Run the heartbeat-prompt JSON command after project state and todos are written.",
|
|
1072
|
+
"Wire `loopx turn run-once` with `--host generic-cli` and "
|
|
1073
|
+
"the `scripts/dsh_turn_host_adapter.py` host adapter command.",
|
|
1074
|
+
"Start every automatic tick from quota should-run and stop when it says stop.",
|
|
1075
|
+
],
|
|
1076
|
+
"success_criteria": [
|
|
1077
|
+
"The DeepSeek Harness adapter returns a typed loopx_turn_result_v0.",
|
|
1078
|
+
"Independent validation passes before LoopX writes state or spends quota.",
|
|
1079
|
+
"Opaque dsh session roots stay outside public LoopX evidence.",
|
|
1080
|
+
],
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
|
|
1084
|
+
def _manual_activation(commands: dict[str, str]) -> dict[str, Any]:
|
|
1085
|
+
return {
|
|
1086
|
+
"host_surface": "external_scheduler_or_manual_shell",
|
|
1087
|
+
"entry_command_hint": "run loopx agent-onboard, then wire a scheduler or invoke quota manually",
|
|
1088
|
+
"activation_method": "external_loop_driver",
|
|
1089
|
+
"activation_input_command": commands["heartbeat_prompt_json"],
|
|
1090
|
+
"host_mutation": {
|
|
1091
|
+
"owner": "external agent or operator",
|
|
1092
|
+
"cli_can_mutate_directly": False,
|
|
1093
|
+
"missing_host_tool_gate": (
|
|
1094
|
+
"No host loop is declared. Wire a cron/task/agent loop that starts "
|
|
1095
|
+
"from quota should-run, or run LoopX manually."
|
|
1096
|
+
),
|
|
1097
|
+
},
|
|
1098
|
+
"activation_steps": [
|
|
1099
|
+
"Generate the heartbeat-prompt JSON task body or equivalent lifecycle prompt.",
|
|
1100
|
+
"Configure the external loop driver to call quota should-run before each delivery slice.",
|
|
1101
|
+
"Record evidence/writeback and spend quota only after validated delivery work.",
|
|
1102
|
+
],
|
|
1103
|
+
"success_criteria": [
|
|
1104
|
+
"An external loop driver reliably starts from the LoopX quota/status contract.",
|
|
1105
|
+
"The driver has an explicit stop/backoff policy for no-progress or unchanged polls.",
|
|
1106
|
+
],
|
|
1107
|
+
}
|
|
1108
|
+
|
|
1109
|
+
|
|
1110
|
+
def build_host_loop_activation_packet(
|
|
1111
|
+
*,
|
|
1112
|
+
agent_type: str,
|
|
1113
|
+
goal_id: str,
|
|
1114
|
+
cli_bin: str = "loopx",
|
|
1115
|
+
agent_id: str | None = None,
|
|
1116
|
+
registered_agents: list[str] | None = None,
|
|
1117
|
+
available_capabilities: list[str] | None = None,
|
|
1118
|
+
fresh_agent_default: bool = False,
|
|
1119
|
+
thread_binding: dict[str, Any] | None = None,
|
|
1120
|
+
) -> dict[str, Any]:
|
|
1121
|
+
canonical = normalize_agent_type(agent_type)
|
|
1122
|
+
identity = _identity_state(
|
|
1123
|
+
agent_id=agent_id,
|
|
1124
|
+
registered_agents=registered_agents,
|
|
1125
|
+
fresh_agent_default=fresh_agent_default,
|
|
1126
|
+
thread_binding=thread_binding,
|
|
1127
|
+
)
|
|
1128
|
+
selected_agent_id = identity.get("selected_agent_id")
|
|
1129
|
+
activation_allowed = bool(identity.get("activation_allowed"))
|
|
1130
|
+
normalized_available_capabilities = normalize_required_capabilities(
|
|
1131
|
+
available_capabilities
|
|
1132
|
+
)
|
|
1133
|
+
commands: dict[str, Any] = (
|
|
1134
|
+
_heartbeat_commands(
|
|
1135
|
+
goal_id=goal_id,
|
|
1136
|
+
agent_type=canonical,
|
|
1137
|
+
cli_bin=cli_bin,
|
|
1138
|
+
agent_id=str(selected_agent_id) if selected_agent_id else None,
|
|
1139
|
+
available_capabilities=normalized_available_capabilities,
|
|
1140
|
+
)
|
|
1141
|
+
if activation_allowed
|
|
1142
|
+
else {
|
|
1143
|
+
"heartbeat_prompt_json": None,
|
|
1144
|
+
"heartbeat_prompt": None,
|
|
1145
|
+
"visible_goal_prompt_json": None,
|
|
1146
|
+
}
|
|
1147
|
+
)
|
|
1148
|
+
if canonical == "ark-managed-agent":
|
|
1149
|
+
surface = _ark_managed_agent_activation(commands)
|
|
1150
|
+
elif canonical == "codex-app":
|
|
1151
|
+
surface = _codex_app_activation(commands)
|
|
1152
|
+
elif canonical == "codex-app-ssh":
|
|
1153
|
+
surface = _codex_app_ssh_activation(commands)
|
|
1154
|
+
elif canonical == "codex-ide-plugin":
|
|
1155
|
+
surface = _codex_ide_activation(commands)
|
|
1156
|
+
elif canonical == "codex-cli":
|
|
1157
|
+
surface = _codex_cli_activation(commands)
|
|
1158
|
+
elif canonical == "claude-code":
|
|
1159
|
+
surface = _claude_code_activation(commands, cli_bin)
|
|
1160
|
+
elif canonical == "opencode":
|
|
1161
|
+
surface = _opencode_activation(commands, cli_bin)
|
|
1162
|
+
elif canonical == "opencode2":
|
|
1163
|
+
surface = _opencode2_activation(commands, cli_bin)
|
|
1164
|
+
elif canonical == "traex-cli":
|
|
1165
|
+
surface = _traex_activation(commands)
|
|
1166
|
+
elif canonical == "pi":
|
|
1167
|
+
surface = _pi_activation(commands, cli_bin)
|
|
1168
|
+
elif canonical == "gemini-cli":
|
|
1169
|
+
surface = _gemini_cli_activation(commands, cli_bin)
|
|
1170
|
+
elif canonical == "cursor-agent":
|
|
1171
|
+
surface = _cursor_agent_activation(commands, cli_bin)
|
|
1172
|
+
elif canonical == "deepseek-harness":
|
|
1173
|
+
surface = _deepseek_harness_activation(commands)
|
|
1174
|
+
else:
|
|
1175
|
+
surface = _manual_activation(commands)
|
|
1176
|
+
if canonical == "other-agent":
|
|
1177
|
+
surface["entry_command_hint"] = "@loopx <task>, $loopx <task>, or another explicit host command facade"
|
|
1178
|
+
surface["host_surface"] = "custom_agent_loop_driver"
|
|
1179
|
+
identity_selection_gate = None
|
|
1180
|
+
if not activation_allowed:
|
|
1181
|
+
choices = []
|
|
1182
|
+
for candidate in identity["registered_agents"]:
|
|
1183
|
+
candidate_commands = _heartbeat_commands(
|
|
1184
|
+
goal_id=goal_id,
|
|
1185
|
+
agent_type=canonical,
|
|
1186
|
+
cli_bin=cli_bin,
|
|
1187
|
+
agent_id=candidate,
|
|
1188
|
+
available_capabilities=normalized_available_capabilities,
|
|
1189
|
+
)
|
|
1190
|
+
choice: dict[str, Any] = {
|
|
1191
|
+
"agent_id": candidate,
|
|
1192
|
+
"activation_input_command": (
|
|
1193
|
+
candidate_commands["visible_goal_prompt_json"]
|
|
1194
|
+
if canonical == "traex-cli"
|
|
1195
|
+
else candidate_commands["heartbeat_prompt_json"]
|
|
1196
|
+
),
|
|
1197
|
+
}
|
|
1198
|
+
if canonical != "traex-cli":
|
|
1199
|
+
choice.update(
|
|
1200
|
+
{
|
|
1201
|
+
"heartbeat_prompt_json": candidate_commands[
|
|
1202
|
+
"heartbeat_prompt_json"
|
|
1203
|
+
],
|
|
1204
|
+
"heartbeat_prompt": candidate_commands["heartbeat_prompt"],
|
|
1205
|
+
}
|
|
1206
|
+
)
|
|
1207
|
+
choice.update(
|
|
1208
|
+
{
|
|
1209
|
+
"mode": "takeover_existing_agent",
|
|
1210
|
+
"requires_explicit_takeover_intent": True,
|
|
1211
|
+
}
|
|
1212
|
+
)
|
|
1213
|
+
choices.append(choice)
|
|
1214
|
+
requested_agent_id = identity.get("requested_agent_id")
|
|
1215
|
+
fresh_agent_id = (
|
|
1216
|
+
str(requested_agent_id)
|
|
1217
|
+
if requested_agent_id
|
|
1218
|
+
else "<new-public-safe-agent-id>"
|
|
1219
|
+
)
|
|
1220
|
+
register_command = (
|
|
1221
|
+
f"{shell_arg(cli_bin)} register-agent --goal-id {shell_arg(goal_id)} "
|
|
1222
|
+
f"--agent-id {shell_arg(fresh_agent_id)} --require-new"
|
|
1223
|
+
)
|
|
1224
|
+
fresh_registration = (
|
|
1225
|
+
{
|
|
1226
|
+
"mode": "register_fresh_agent",
|
|
1227
|
+
"recommended": True,
|
|
1228
|
+
"agent_id": fresh_agent_id,
|
|
1229
|
+
"preview_command": register_command,
|
|
1230
|
+
"execute_command": f"{register_command} --execute",
|
|
1231
|
+
"continuation_contract": {
|
|
1232
|
+
"schema_version": "loopx_fresh_agent_registration_continuation_v0",
|
|
1233
|
+
"requires_execute_result": True,
|
|
1234
|
+
"required_result": {
|
|
1235
|
+
"ok": True,
|
|
1236
|
+
"changed": True,
|
|
1237
|
+
"written": True,
|
|
1238
|
+
"global_sync": {"ok": True},
|
|
1239
|
+
"registration_readback": {"verified": True},
|
|
1240
|
+
},
|
|
1241
|
+
},
|
|
1242
|
+
"continuation": (
|
|
1243
|
+
"treat preview as advisory; continue only when the execute result "
|
|
1244
|
+
"reports ok=true, changed=true, written=true, global_sync.ok=true, "
|
|
1245
|
+
"and registration_readback.verified=true, then rerun onboarding with "
|
|
1246
|
+
"the newly registered --agent-id before todo writeback or host-loop "
|
|
1247
|
+
"activation"
|
|
1248
|
+
),
|
|
1249
|
+
}
|
|
1250
|
+
if fresh_agent_default
|
|
1251
|
+
else None
|
|
1252
|
+
)
|
|
1253
|
+
identity_selection_gate = {
|
|
1254
|
+
**identity,
|
|
1255
|
+
"choices": choices,
|
|
1256
|
+
"default_action": (
|
|
1257
|
+
"register_fresh_agent" if fresh_registration else "select_agent_identity"
|
|
1258
|
+
),
|
|
1259
|
+
"fresh_agent_registration": fresh_registration,
|
|
1260
|
+
"external_write_required": bool(fresh_registration),
|
|
1261
|
+
}
|
|
1262
|
+
surface["activation_method"] = (
|
|
1263
|
+
"register_fresh_agent_or_explicit_takeover_before_host_loop_activation"
|
|
1264
|
+
if fresh_registration
|
|
1265
|
+
else "select_agent_identity_before_host_loop_activation"
|
|
1266
|
+
)
|
|
1267
|
+
surface["activation_input_command"] = None
|
|
1268
|
+
if fresh_registration:
|
|
1269
|
+
gate_steps = [
|
|
1270
|
+
"Register a fresh public-safe agent id from identity_selection_gate by default.",
|
|
1271
|
+
"Only select an existing lane when the user explicitly requests takeover of that exact agent.",
|
|
1272
|
+
"Rerun onboarding with the selected --agent-id.",
|
|
1273
|
+
]
|
|
1274
|
+
gate_criterion = (
|
|
1275
|
+
"A fresh registered agent identity is selected, or exact takeover intent is recorded."
|
|
1276
|
+
)
|
|
1277
|
+
else:
|
|
1278
|
+
gate_steps = [
|
|
1279
|
+
"Select one registered agent lane from identity_selection_gate.",
|
|
1280
|
+
"Run that choice's host-specific activation_input_command.",
|
|
1281
|
+
]
|
|
1282
|
+
gate_criterion = "One registered agent identity is explicitly selected."
|
|
1283
|
+
surface["activation_steps"] = [
|
|
1284
|
+
*gate_steps,
|
|
1285
|
+
*surface["activation_steps"][1:],
|
|
1286
|
+
]
|
|
1287
|
+
surface["success_criteria"] = [
|
|
1288
|
+
gate_criterion,
|
|
1289
|
+
*surface["success_criteria"],
|
|
1290
|
+
]
|
|
1291
|
+
return {
|
|
1292
|
+
"schema_version": SCHEMA_VERSION,
|
|
1293
|
+
"agent_type": canonical,
|
|
1294
|
+
"agent_model": "peer_v1",
|
|
1295
|
+
"goal_id": goal_id,
|
|
1296
|
+
"agent_id": selected_agent_id,
|
|
1297
|
+
"requested_agent_id": normalize_todo_claimed_by(agent_id),
|
|
1298
|
+
"available_capabilities": normalized_available_capabilities,
|
|
1299
|
+
"activation_state": identity["state"],
|
|
1300
|
+
"activation_allowed": activation_allowed,
|
|
1301
|
+
"identity_contract": identity,
|
|
1302
|
+
"identity_selection_gate": identity_selection_gate,
|
|
1303
|
+
"activation_required_after_todo_write": True,
|
|
1304
|
+
"status_probe_policy": {
|
|
1305
|
+
"check_once_during_onboarding": True,
|
|
1306
|
+
"cheap_recheck_on_loopx": "only when activation is missing, unknown, stale, or the agent is newly installed",
|
|
1307
|
+
"do_not_recompute_every_loopx_turn": True,
|
|
1308
|
+
},
|
|
1309
|
+
"commands": commands,
|
|
1310
|
+
**surface,
|
|
1311
|
+
}
|