delegate-agent-cli 0.27.0__tar.gz → 0.28.0__tar.gz
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.
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/CHANGELOG.md +31 -1
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/PKG-INFO +3 -1
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/README.md +2 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/docs/agent-setup.md +1 -1
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/docs/cli-reference.md +10 -3
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/docs/configuration.md +1 -1
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/pyproject.toml +1 -1
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/__init__.py +1 -1
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/cli.py +52 -35
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/cli_parser.py +6 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/command_help.py +17 -4
- delegate_agent_cli-0.28.0/src/delegate_agent/failover_state.py +201 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/inspection_commands.py +26 -1
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/profiles.py +18 -1
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/request_build.py +2 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/request_models.py +2 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/run_metadata.py +58 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/run_status.py +4 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/runner.py +186 -27
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/worktree_execution.py +26 -9
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent_cli.egg-info/PKG-INFO +3 -1
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent_cli.egg-info/SOURCES.txt +2 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_command_help.py +16 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_execution_worktree_run.py +135 -0
- delegate_agent_cli-0.28.0/tests/test_failover_state.py +105 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_inspection_commands.py +91 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_profiles.py +177 -1
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_runner_capture.py +363 -31
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_utility_modules.py +2 -1
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/CONTRIBUTING.md +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/LICENSE +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/MANIFEST.in +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/SECURITY.md +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/bin/delegate-profile-shim +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/bin/delegate.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/config.example.json +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/docs/assets/delegate-agent-header.png +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/docs/delegate-workflows.md +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/docs/development.md +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/docs/live-runtime.md +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/docs/publishing-checklist.md +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/docs/security-model.md +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/docs/troubleshooting.md +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/docs/worktrees.md +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/examples/task.claude.json +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/examples/task.codex.json +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/examples/task.cursor.json +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/examples/task.droid.json +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/examples/task.grok.json +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/examples/task.judge.json +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/examples/task.kimi.json +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/setup.cfg +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/archived_logs.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/argv_builders.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/argv_utils.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/bundled_models.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/capability_commands.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/child_failures.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/command_errors.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/config.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/config_commands.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/constants.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/describe_payload.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/errors.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/git_utils.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/harness_discovery.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/harness_events.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/isolation.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/json_types.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/log_output.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/mail.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/mail_core.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/mail_push.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/model_discovery.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/personas.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/private_io.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/profile_commands.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/profile_guard.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/prompt_instructions.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/prompt_transport.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/reasoning.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/redaction.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/rendering.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/resume_command.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/retention.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/run_output_commands.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/run_registry.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/safe_workspace.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/seatbelt.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/setup_commands.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/snapshot_view.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/structured_output.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/wait_cancel_commands.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/workflows/__init__.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/workflows/commands.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/workflows/registry.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/workflows/runtime.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/workflows/schema.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/workflows/script.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/worktree_commands.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/worktree_gc.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/worktree_mgmt.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/worktree_records.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/worktree_remove.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/worktree_summary.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/wsl.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent_cli.egg-info/dependency_links.txt +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent_cli.egg-info/entry_points.txt +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent_cli.egg-info/requires.txt +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent_cli.egg-info/top_level.txt +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_capability_commands.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_child_failures.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_codex_pure_sandbox.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_codex_thread_resilience.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_config_commands.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_delegate_help_cli.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_delegate_isolation.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_delegate_parser.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_delegate_validation.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_end_to_end_tracking.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_engine_argv.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_execution_argv_and_prompt.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_execution_dry_run.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_execution_worktree_failure_cleanup.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_execution_worktree_preflight.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_harness_discovery.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_harness_events.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_kimi_commands.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_mail_bounds.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_mail_contract.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_mail_delivery.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_mail_eligibility.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_mail_framing.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_mail_gating.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_mail_identity.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_mail_io.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_mail_parser.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_mail_prune.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_mail_push.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_mail_push_adapters.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_mail_push_batch.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_mail_push_cursor.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_mail_push_gating.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_mail_push_seams.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_mail_reply.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_mail_resolution.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_mail_rules.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_model_discovery.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_model_selection_wave1a.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_model_selection_wave1b.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_omp_read_only_behavior.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_packaging.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_persona_capability.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_persona_claude.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_persona_contract.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_persona_framer.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_persona_opencode.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_persona_parser.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_persona_records.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_persona_resolution.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_persona_size_guard.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_persona_validation.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_persona_workflow.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_pure_call.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_reasoning_capabilities.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_resume_attachment.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_resume_capture.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_resume_inheritance.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_resume_parser.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_resume_projections.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_resume_refusals.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_resume_size_guard.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_resume_stale.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_resume_trust.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_retention.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_run_registry.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_safe_workspace_isolation.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_setup_commands.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_slash_passthrough.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_snapshot_redaction.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_snapshot_rendering.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_snapshot_run_output.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_snapshot_view.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_structured_output.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_tracked_timeout.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_wait_cancel_commands.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_wave4_launch_features.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_workflow_commands.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_worktree_list_show.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_worktree_prune_gc.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_worktree_remove.py +0 -0
- {delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/tests/test_wsl_guardrails.py +0 -0
|
@@ -5,7 +5,36 @@ All notable changes to this project are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [
|
|
8
|
+
## [0.28.0] - 2026-08-05
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- Added `delegate runs --structural` and `delegate ps --structural` for
|
|
13
|
+
content-free local status collection, including lifecycle, model, group,
|
|
14
|
+
status, timestamp, and root initiator provenance.
|
|
15
|
+
- Tracked runs now retain a validated root initiator across nested Delegate
|
|
16
|
+
launches and persistent-worktree execution.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- Codex quota fallback is now explicitly configured with
|
|
21
|
+
`codex.fallbackProfile`, supports arbitrary profile definitions, and keeps
|
|
22
|
+
temporary blocks by resolved credential identity rather than profile alias.
|
|
23
|
+
Default work/personal credential homes continue to interoperate with legacy
|
|
24
|
+
profile launchers without allowing remapped aliases to share state.
|
|
25
|
+
- Codex-only failover no longer depends on private Claude shim names or hidden
|
|
26
|
+
runtime controls. Retry and persistent-worktree paths retain the account
|
|
27
|
+
that is actually serving the run.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- Structural run listings now exclude raw terminal/provider events and apply
|
|
32
|
+
normal secret redaction to retained fields.
|
|
33
|
+
- Fallback failures persist the correct account block and provider-reported
|
|
34
|
+
reset time; a blocked fallback is skipped, and retries no longer return to a
|
|
35
|
+
blocked primary after a preflight swap.
|
|
36
|
+
- Failover tests isolate their state from the user's home directory, and
|
|
37
|
+
`delegate ps --help` now advertises its supported structural view.
|
|
9
38
|
|
|
10
39
|
## [0.27.0] - 2026-08-01
|
|
11
40
|
|
|
@@ -888,6 +917,7 @@ Usage-audit fix wave: 82 sessions and 1,241 delegate invocations from one week o
|
|
|
888
917
|
|
|
889
918
|
- Releases before 0.1.3 predate this changelog.
|
|
890
919
|
|
|
920
|
+
[0.28.0]: https://github.com/treygoff24/delegate-agent/compare/v0.27.0...v0.28.0
|
|
891
921
|
[0.27.0]: https://github.com/treygoff24/delegate-agent/compare/v0.26.0...v0.27.0
|
|
892
922
|
[0.26.0]: https://github.com/treygoff24/delegate-agent/compare/v0.25.0...v0.26.0
|
|
893
923
|
[0.25.0]: https://github.com/treygoff24/delegate-agent/compare/v0.24.0...v0.25.0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: delegate-agent-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.28.0
|
|
4
4
|
Summary: A tiny CLI for delegating bounded agent tasks across coding-agent runtimes.
|
|
5
5
|
Author: Trey Goff
|
|
6
6
|
License-Expression: MIT
|
|
@@ -167,6 +167,8 @@ delegate --json capabilities refresh # refresh the active profile's discovery
|
|
|
167
167
|
|
|
168
168
|
Discover commands as you go: `delegate <command> --help` prints focused help for any command path, and `delegate --json <command> --help` returns an agent-friendly spec of its usage, arguments, and options. For launch and `dry-run` commands, `--json` and `--isolation` may also appear before inline prompt text begins, for example `delegate codex work --prompt-file task.md --json`. `delegate --json describe` includes a `commands` catalog of the whole surface. Cached `models` and `capabilities` reads launch no child process. `models <engine> --live` performs a one-off probe without updating the cache; rerun `delegate setup` or `delegate capabilities refresh` when ordinary launches should consume newly discovered models or effort levels.
|
|
169
169
|
|
|
170
|
+
Codex tracked runs can opt into a one-run quota fallback with `codex.fallbackProfile`; hashed credential namespace is canonical, while default work/personal credential homes also mirror compatible legacy alias keys so existing launchers share blocks. Remapped aliases remain isolated. See [Configuration](docs/configuration.md).
|
|
171
|
+
|
|
170
172
|
From this development checkout, use `python3 bin/delegate.py ...` instead of an installed `delegate` shim.
|
|
171
173
|
|
|
172
174
|
### WSL setup notes
|
|
@@ -134,6 +134,8 @@ delegate --json capabilities refresh # refresh the active profile's discovery
|
|
|
134
134
|
|
|
135
135
|
Discover commands as you go: `delegate <command> --help` prints focused help for any command path, and `delegate --json <command> --help` returns an agent-friendly spec of its usage, arguments, and options. For launch and `dry-run` commands, `--json` and `--isolation` may also appear before inline prompt text begins, for example `delegate codex work --prompt-file task.md --json`. `delegate --json describe` includes a `commands` catalog of the whole surface. Cached `models` and `capabilities` reads launch no child process. `models <engine> --live` performs a one-off probe without updating the cache; rerun `delegate setup` or `delegate capabilities refresh` when ordinary launches should consume newly discovered models or effort levels.
|
|
136
136
|
|
|
137
|
+
Codex tracked runs can opt into a one-run quota fallback with `codex.fallbackProfile`; hashed credential namespace is canonical, while default work/personal credential homes also mirror compatible legacy alias keys so existing launchers share blocks. Remapped aliases remain isolated. See [Configuration](docs/configuration.md).
|
|
138
|
+
|
|
137
139
|
From this development checkout, use `python3 bin/delegate.py ...` instead of an installed `delegate` shim.
|
|
138
140
|
|
|
139
141
|
### WSL setup notes
|
|
@@ -7,7 +7,7 @@ This guide covers both human setup and non-interactive setup for agents or CI jo
|
|
|
7
7
|
1. Install Delegate from the source repository:
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
python3 -m pip install "delegate-agent @ git+https://github.com/treygoff24/delegate-agent.git"
|
|
10
|
+
python3 -m pip install "delegate-agent-cli @ git+https://github.com/treygoff24/delegate-agent.git"
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
For a development checkout, prefer:
|
|
@@ -1008,7 +1008,7 @@ repo copy or persistent worktree itself.
|
|
|
1008
1008
|
Tracked runs return bounded parent-facing output and store local metadata under `.delegate/` in the source workspace.
|
|
1009
1009
|
|
|
1010
1010
|
```bash
|
|
1011
|
-
delegate runs [--active|--running|--stale|--recent] [--harness HARNESS] [--group NAME] [--limit N]
|
|
1011
|
+
delegate runs [--active|--running|--stale|--recent] [--harness HARNESS] [--group NAME] [--limit N] [--structural]
|
|
1012
1012
|
delegate runs prune [--older-than DAYS] [--dry-run]
|
|
1013
1013
|
delegate ps [--harness HARNESS] [--group NAME] [--limit N]
|
|
1014
1014
|
delegate snapshot [--latest HARNESS] [--no-redact] <handle>
|
|
@@ -1055,8 +1055,10 @@ creating a Run or writing a prompt record.
|
|
|
1055
1055
|
| Include dirty | `includeDirty` | `--include-dirty` | Source and explicit values are creation-only and are dropped for ordinary resume with a note. | Retained only as a drop decision; on a persistent/attached source, explicit `--include-dirty` is rejected because attachment does not create or sync a worktree. |
|
|
1056
1056
|
|
|
1057
1057
|
`delegate runs` defaults to recent runs. `--active` preserves the legacy active view and includes both live `running` runs and `stale` runs. Use `--running` for only live tracked processes and `--stale` for runs recorded as running whose PID is missing or dead. `--active`, `--running`, `--stale`, and `--recent` are mutually exclusive. `--group NAME` filters by launch group and the runs table shows a `group` column when any visible run has one.
|
|
1058
|
+
`--structural` omits content-bearing run fields and emits only identity, lifecycle, model,
|
|
1059
|
+
timestamp, group/mode, and `initiatorRoot` metadata. It is intended for local status collectors.
|
|
1058
1060
|
`delegate ps` is the shorter first-class form of `delegate runs --active` and
|
|
1059
|
-
accepts the same harness, group, and
|
|
1061
|
+
accepts the same harness, group, limit, and structural selectors.
|
|
1060
1062
|
|
|
1061
1063
|
`delegate runs prune` removes old terminal Run records from the workspace Registry so they stop accumulating forever. Only runs whose effective status is terminal (`succeeded`, `failed`, `cancelled`, or `stale` from a dead child) and whose last Registry activity is older than the threshold (default 30 days; override with `--older-than DAYS`) are eligible. Effectively running runs are always skipped, and persistent-worktree runs are skipped unless their worktree is recorded as removed or missing — pruning the record of a live worktree would orphan it from `worktree list`/`show`/`remove`. Pruning deletes the per-Run directory (Snapshot, Manifest, logs, events, Completion Report) and the retained raw-log archive; worktree paths on disk are never touched. `--dry-run` reports what would be removed without changing anything. JSON output uses schema `delegate.runs-prune.v1` with `planned`, `removed`, `skipped` (each entry carries a `reason`), and `errors` sections.
|
|
1062
1064
|
|
|
@@ -1114,7 +1116,12 @@ Persistent worktree completions also include `branch`, `worktree`, a
|
|
|
1114
1116
|
state, changed file count, diff stat, and commits created by the child. When a
|
|
1115
1117
|
Codex usage-limit fallback fires, the completion payload also includes
|
|
1116
1118
|
`codexAuthFallback` metadata (reason, the primary and fallback profile names,
|
|
1117
|
-
both exit codes, and a redacted primary stderr tail).
|
|
1119
|
+
both exit codes, and a redacted primary stderr tail). The fallback is Codex-only
|
|
1120
|
+
and is enabled only by `codex.fallbackProfile`; Delegate remembers temporary
|
|
1121
|
+
blocks by hashed credential namespace (`CODEX_HOME/auth.json` plus
|
|
1122
|
+
`codex.profile`) as canonical state. Default work/personal credential homes
|
|
1123
|
+
also mirror compatible legacy alias keys so existing launchers share blocks;
|
|
1124
|
+
remapped aliases remain isolated.
|
|
1118
1125
|
|
|
1119
1126
|
Snapshot JSON uses schema `delegate.snapshot.v1` and includes fields such as `alias`, `runId`, `harness`, `status`, `rawStatus`, `effectiveStatus`, `staleReason`, `nextActions`, `cwd`, `executionCwd`, `workspaceRoot`, `assistantText`, `recentEvents`, `warnings`, `exitCode`, reasoning metadata, terminal metadata, and isolation/worktree metadata when applicable. `workspaceRoot` is also exported to the child as `WORKSPACE_ROOT`, so commands can anchor workspace-relative paths after changing directories. Inspection commands do not rewrite a stale run's recorded state; they expose the raw recorded status plus the effective status computed from the current PID check. Run-output and worktree show output include `requestedHandle`, `resolvedHandle`, and `resolutionKind` (`literal`, `latest`, or `latest_model`) when a handle resolves indirectly. For bare harness handles, snapshot, run-output, and wait also report `resolvedRunId`, `resolvedAlias`, `resolvedWorkspace`, `resolvedAge`, and `resolvedAgeSeconds`. Resolutions older than 24 hours add a `bare_handle_stale` warning suggesting `--cwd` or an explicit handle.
|
|
1120
1127
|
|
|
@@ -336,7 +336,7 @@ Controls local run recording.
|
|
|
336
336
|
- `models`: optional map of local aliases to Codex model IDs for `--model` / JSON `model`. Alias keys must not collide with mode names, equal the engine's own name, or start with `-`.
|
|
337
337
|
- `defaultReasoningEffort`: optional non-empty effort string. When a Codex model resolves (run input or `codex.defaultModel`) and supports the level, Delegate emits a Codex config override; otherwise the run proceeds without reasoning effort and records a warning. An explicit `--reasoning-effort` flag fails closed for unsupported levels, but can target the Codex harness default model when no model is configured.
|
|
338
338
|
- `profile`: optional Codex CLI config overlay name. It is config-only; JSON run input cannot set it.
|
|
339
|
-
- `fallbackProfile`: optional top-level `profiles.definitions` name for
|
|
339
|
+
- `fallbackProfile`: optional top-level `profiles.definitions` name for Codex-only quota fallback. The fallback profile must define `env.CODEX_HOME`. Tracked Codex runs persist temporary usage-limit blocks under `~/.ai-profiles/runtime/failover/`; hashed credential namespace (`CODEX_HOME/auth.json` plus `codex.profile`) is canonical, while default work/personal credential homes also mirror compatible legacy alias keys so existing launchers share blocks. Remapped aliases remain isolated. If both the primary and fallback credential namespaces are blocked, Delegate keeps the primary attempt and does not launch the known-blocked fallback.
|
|
340
340
|
- `workSandbox`: `read-only`, `workspace-write`, or `danger-full-access` for Codex work mode when full bypass is not enabled.
|
|
341
341
|
- `ephemeral`: include Codex `--ephemeral` in JSON-streaming runs.
|
|
342
342
|
- `ignoreUserConfig`: include Codex `--ignore-user-config`.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "delegate-agent-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.28.0"
|
|
8
8
|
description = "A tiny CLI for delegating bounded agent tasks across coding-agent runtimes."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -635,6 +635,8 @@ def make_run_context(
|
|
|
635
635
|
),
|
|
636
636
|
auth_profile=request.auth_profile,
|
|
637
637
|
fallback_auth_profile=request.fallback_auth_profile,
|
|
638
|
+
codex_failover_identity=request.codex_failover_identity,
|
|
639
|
+
codex_fallback_failover_identity=request.codex_fallback_failover_identity,
|
|
638
640
|
include_dirty=request.include_dirty,
|
|
639
641
|
mail_push=request.mail_push,
|
|
640
642
|
group=request.group,
|
|
@@ -675,6 +677,12 @@ def _set_child_root_env(request: Request, source_workspace: ResolvedWorkspace) -
|
|
|
675
677
|
request.env_overrides = env
|
|
676
678
|
|
|
677
679
|
|
|
680
|
+
def _apply_initiator_root(request: Request) -> str | None:
|
|
681
|
+
initiator_root, env = run_metadata.apply_initiator_root_env(request.env_overrides)
|
|
682
|
+
request.env_overrides = env
|
|
683
|
+
return initiator_root
|
|
684
|
+
|
|
685
|
+
|
|
678
686
|
def _artifact_files(root: Path) -> list[str]:
|
|
679
687
|
return sorted(str(item) for item in root.rglob("*") if item.is_file() or item.is_symlink())
|
|
680
688
|
|
|
@@ -786,22 +794,26 @@ def _execute_attached_worktree(
|
|
|
786
794
|
if request.mode == MODE_WORK and delegate_config.mail_enabled(config):
|
|
787
795
|
mail.prepare_mail_storage(registry_root)
|
|
788
796
|
mail.sanitize_inherited_mail_identity(request.env_overrides)
|
|
797
|
+
metadata = {
|
|
798
|
+
"mode": request.mode,
|
|
799
|
+
"model": request.model,
|
|
800
|
+
"modelAlias": request.model_alias,
|
|
801
|
+
"modelResolved": request.model,
|
|
802
|
+
"group": request.group,
|
|
803
|
+
"workflowAgentKey": request.workflow_agent_key,
|
|
804
|
+
"cwd": source_workspace.path,
|
|
805
|
+
"worktreeAttachment": {
|
|
806
|
+
**(iso.attachment or {}),
|
|
807
|
+
"startHeadOid": start_head_oid,
|
|
808
|
+
},
|
|
809
|
+
}
|
|
810
|
+
run_metadata.add_initiator_metadata(
|
|
811
|
+
metadata, run_metadata.resolve_initiator_root(request.env_overrides or {})
|
|
812
|
+
)
|
|
789
813
|
run_id, alias = run_registry.register_run(
|
|
790
814
|
registry_root,
|
|
791
815
|
harness=request.engine,
|
|
792
|
-
metadata=
|
|
793
|
-
"mode": request.mode,
|
|
794
|
-
"model": request.model,
|
|
795
|
-
"modelAlias": request.model_alias,
|
|
796
|
-
"modelResolved": request.model,
|
|
797
|
-
"group": request.group,
|
|
798
|
-
"workflowAgentKey": request.workflow_agent_key,
|
|
799
|
-
"cwd": source_workspace.path,
|
|
800
|
-
"worktreeAttachment": {
|
|
801
|
-
**(iso.attachment or {}),
|
|
802
|
-
"startHeadOid": start_head_oid,
|
|
803
|
-
},
|
|
804
|
-
},
|
|
816
|
+
metadata=metadata,
|
|
805
817
|
)
|
|
806
818
|
child_env = request.env_overrides or {}
|
|
807
819
|
if request.mode == MODE_WORK:
|
|
@@ -986,6 +998,7 @@ def execute_request(
|
|
|
986
998
|
stderr: TextIO,
|
|
987
999
|
) -> tuple[int, JsonObject | None]:
|
|
988
1000
|
_set_child_root_env(request, source_workspace)
|
|
1001
|
+
initiator_root = _apply_initiator_root(request)
|
|
989
1002
|
profiles.strip_mail_identity(request.env_overrides)
|
|
990
1003
|
if request.mode == MODE_CALL:
|
|
991
1004
|
call_response: tuple[int, JsonObject | None] | None = None
|
|
@@ -1013,18 +1026,20 @@ def execute_request(
|
|
|
1013
1026
|
workspace_kind=source_workspace.kind,
|
|
1014
1027
|
)
|
|
1015
1028
|
maybe_run_retention_pass(registry_root, config)
|
|
1029
|
+
metadata = {
|
|
1030
|
+
"mode": request.mode,
|
|
1031
|
+
"model": request.model,
|
|
1032
|
+
"modelAlias": request.model_alias,
|
|
1033
|
+
"modelResolved": request.model,
|
|
1034
|
+
"group": request.group,
|
|
1035
|
+
"workflowAgentKey": request.workflow_agent_key,
|
|
1036
|
+
"cwd": source_workspace.path,
|
|
1037
|
+
}
|
|
1038
|
+
run_metadata.add_initiator_metadata(metadata, initiator_root)
|
|
1016
1039
|
run_id, alias = run_registry.register_run(
|
|
1017
1040
|
registry_root,
|
|
1018
1041
|
harness=request.engine,
|
|
1019
|
-
metadata=
|
|
1020
|
-
"mode": request.mode,
|
|
1021
|
-
"model": request.model,
|
|
1022
|
-
"modelAlias": request.model_alias,
|
|
1023
|
-
"modelResolved": request.model,
|
|
1024
|
-
"group": request.group,
|
|
1025
|
-
"workflowAgentKey": request.workflow_agent_key,
|
|
1026
|
-
"cwd": source_workspace.path,
|
|
1027
|
-
},
|
|
1042
|
+
metadata=metadata,
|
|
1028
1043
|
)
|
|
1029
1044
|
artifact_id = run_id
|
|
1030
1045
|
ctx_runner = make_run_context(
|
|
@@ -1282,22 +1297,24 @@ def execute_request(
|
|
|
1282
1297
|
if isolated_request.mode == MODE_WORK and delegate_config.mail_enabled(config):
|
|
1283
1298
|
mail.prepare_mail_storage(registry_root)
|
|
1284
1299
|
mail.sanitize_inherited_mail_identity(isolated_request.env_overrides)
|
|
1300
|
+
metadata = {
|
|
1301
|
+
"mode": isolated_request.mode,
|
|
1302
|
+
"model": isolated_request.model,
|
|
1303
|
+
"modelAlias": isolated_request.model_alias,
|
|
1304
|
+
"modelResolved": isolated_request.model,
|
|
1305
|
+
"group": isolated_request.group,
|
|
1306
|
+
"workflowAgentKey": isolated_request.workflow_agent_key,
|
|
1307
|
+
"cwd": (
|
|
1308
|
+
isolated_request.isolation_context.source_workspace
|
|
1309
|
+
if isolated_request.isolation_context is not None
|
|
1310
|
+
else source_workspace.path
|
|
1311
|
+
),
|
|
1312
|
+
}
|
|
1313
|
+
run_metadata.add_initiator_metadata(metadata, initiator_root)
|
|
1285
1314
|
run_id, alias = run_registry.register_run(
|
|
1286
1315
|
registry_root,
|
|
1287
1316
|
harness=isolated_request.engine,
|
|
1288
|
-
metadata=
|
|
1289
|
-
"mode": isolated_request.mode,
|
|
1290
|
-
"model": isolated_request.model,
|
|
1291
|
-
"modelAlias": isolated_request.model_alias,
|
|
1292
|
-
"modelResolved": isolated_request.model,
|
|
1293
|
-
"group": isolated_request.group,
|
|
1294
|
-
"workflowAgentKey": isolated_request.workflow_agent_key,
|
|
1295
|
-
"cwd": (
|
|
1296
|
-
isolated_request.isolation_context.source_workspace
|
|
1297
|
-
if isolated_request.isolation_context is not None
|
|
1298
|
-
else source_workspace.path
|
|
1299
|
-
),
|
|
1300
|
-
},
|
|
1317
|
+
metadata=metadata,
|
|
1301
1318
|
)
|
|
1302
1319
|
child_env = isolated_request.env_overrides or {}
|
|
1303
1320
|
if isolated_request.mode == MODE_WORK:
|
|
@@ -1875,6 +1875,7 @@ def parse_runs(
|
|
|
1875
1875
|
recent = False
|
|
1876
1876
|
running = False
|
|
1877
1877
|
stale = False
|
|
1878
|
+
structural = False
|
|
1878
1879
|
harness: str | None = None
|
|
1879
1880
|
group: str | None = None
|
|
1880
1881
|
limit: int | None = None
|
|
@@ -1897,6 +1898,10 @@ def parse_runs(
|
|
|
1897
1898
|
recent = True
|
|
1898
1899
|
i += 1
|
|
1899
1900
|
continue
|
|
1901
|
+
if token == "--structural":
|
|
1902
|
+
structural = True
|
|
1903
|
+
i += 1
|
|
1904
|
+
continue
|
|
1900
1905
|
if token == "--harness":
|
|
1901
1906
|
if i + 1 >= len(rest):
|
|
1902
1907
|
raise DelegateError(
|
|
@@ -1953,6 +1958,7 @@ def parse_runs(
|
|
|
1953
1958
|
harness=harness,
|
|
1954
1959
|
group=group,
|
|
1955
1960
|
limit=limit,
|
|
1961
|
+
structural=structural,
|
|
1956
1962
|
json_mode=json_mode,
|
|
1957
1963
|
),
|
|
1958
1964
|
)
|
|
@@ -875,7 +875,7 @@ COMMAND_SPECS: dict[str, CommandSpec] = {
|
|
|
875
875
|
summary="List tracked runs, optionally filtered by activity, recency, or harness.",
|
|
876
876
|
usage=(
|
|
877
877
|
"delegate [--json] runs [--active|--running|--stale|--recent] "
|
|
878
|
-
"[--harness HARNESS] [--group NAME] [--limit N]",
|
|
878
|
+
"[--harness HARNESS] [--group NAME] [--limit N] [--structural]",
|
|
879
879
|
),
|
|
880
880
|
options=(
|
|
881
881
|
OptionSpec(
|
|
@@ -893,6 +893,11 @@ COMMAND_SPECS: dict[str, CommandSpec] = {
|
|
|
893
893
|
),
|
|
894
894
|
OptionSpec("--limit", "N", "Cap the number of runs listed (positive integer)."),
|
|
895
895
|
OptionSpec("--group", "NAME", "Filter by launch group."),
|
|
896
|
+
OptionSpec(
|
|
897
|
+
"--structural",
|
|
898
|
+
None,
|
|
899
|
+
"Emit only lifecycle, model, provenance, and identity metadata; omit run content.",
|
|
900
|
+
),
|
|
896
901
|
),
|
|
897
902
|
examples=(
|
|
898
903
|
"delegate runs --active",
|
|
@@ -900,6 +905,7 @@ COMMAND_SPECS: dict[str, CommandSpec] = {
|
|
|
900
905
|
"delegate runs --stale",
|
|
901
906
|
"delegate runs --harness cursor --limit 5",
|
|
902
907
|
"delegate runs --group wave4",
|
|
908
|
+
"delegate --json runs --recent --structural",
|
|
903
909
|
),
|
|
904
910
|
notes=("--active, --running, --stale, and --recent are mutually exclusive.",),
|
|
905
911
|
see_also=("ps", "runs prune", "snapshot", "run-output"),
|
|
@@ -1101,13 +1107,20 @@ COMMAND_SPECS: dict[str, CommandSpec] = {
|
|
|
1101
1107
|
"ps": CommandSpec(
|
|
1102
1108
|
name="ps",
|
|
1103
1109
|
summary="List active tracked runs (alias for runs --active).",
|
|
1104
|
-
usage=(
|
|
1110
|
+
usage=(
|
|
1111
|
+
"delegate [--json] ps [--harness HARNESS] [--group NAME] [--limit N] [--structural]",
|
|
1112
|
+
),
|
|
1105
1113
|
options=(
|
|
1106
1114
|
OptionSpec("--harness", "HARNESS", f"Filter by harness: {ENGINES_PROSE}."),
|
|
1107
1115
|
OptionSpec("--limit", "N", "Cap the number of runs listed (positive integer)."),
|
|
1108
1116
|
OptionSpec("--group", "NAME", "Filter by launch group."),
|
|
1117
|
+
OptionSpec(
|
|
1118
|
+
"--structural",
|
|
1119
|
+
None,
|
|
1120
|
+
"Emit only lifecycle, model, provenance, and identity metadata; omit run content.",
|
|
1121
|
+
),
|
|
1109
1122
|
),
|
|
1110
|
-
examples=("delegate ps", "delegate ps --harness codex"),
|
|
1123
|
+
examples=("delegate ps", "delegate ps --harness codex", "delegate --json ps --structural"),
|
|
1111
1124
|
notes=("Equivalent to delegate runs --active.",),
|
|
1112
1125
|
see_also=("runs", "snapshot", "run-output"),
|
|
1113
1126
|
unsupported_global_options=("--auth-profile",),
|
|
@@ -2101,7 +2114,7 @@ def render_overview_text() -> str:
|
|
|
2101
2114
|
"[--active|--running|--stale|--recent] [--harness HARNESS] [--limit N]",
|
|
2102
2115
|
"delegate [--cwd PATH] [--json] runs prune [--older-than DAYS] [--dry-run]",
|
|
2103
2116
|
"delegate [--cwd PATH] [--json] mail {send,inbox,read,status,watch,prune} ...",
|
|
2104
|
-
"delegate [--cwd PATH] [--json] ps [--harness HARNESS] [--group NAME] [--limit N]",
|
|
2117
|
+
"delegate [--cwd PATH] [--json] ps [--harness HARNESS] [--group NAME] [--limit N] [--structural]",
|
|
2105
2118
|
"delegate [--cwd PATH] [--json] run-output <handle> "
|
|
2106
2119
|
"[--completion-report] [--stdout] [--stderr] [--tail N] [--max-chars N] "
|
|
2107
2120
|
"[--raw] [--no-redact]",
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
"""Persistent usage-limit state shared by Delegate and profile launchers."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import contextlib
|
|
6
|
+
import datetime
|
|
7
|
+
import hashlib
|
|
8
|
+
import os
|
|
9
|
+
import re
|
|
10
|
+
import time
|
|
11
|
+
from collections.abc import Generator
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
_VALID_TOOLS = frozenset({"codex"})
|
|
15
|
+
_LEGACY_PROFILES = frozenset({"work", "personal"})
|
|
16
|
+
_DEFAULT_COOLDOWN_SEC = 1800
|
|
17
|
+
_LOCK_WAIT_SEC = 5.0
|
|
18
|
+
_LOCK_STALE_SEC = 30.0
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _root() -> Path:
|
|
22
|
+
return Path.home() / ".ai-profiles" / "runtime" / "failover"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _state_slug(tool: str, identity: str) -> str:
|
|
26
|
+
digest = hashlib.sha256(f"{tool}\0{identity}".encode()).hexdigest()
|
|
27
|
+
return digest[:32]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _state_file(tool: str, identity: str) -> Path:
|
|
31
|
+
return _root() / f"{tool}-{_state_slug(tool, identity)}.blocked-until"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@contextlib.contextmanager
|
|
35
|
+
def _lock_path(lock: Path) -> Generator[bool, None, None]:
|
|
36
|
+
deadline = time.monotonic() + _LOCK_WAIT_SEC
|
|
37
|
+
acquired = False
|
|
38
|
+
while not acquired:
|
|
39
|
+
try:
|
|
40
|
+
lock.mkdir()
|
|
41
|
+
acquired = True
|
|
42
|
+
except FileExistsError:
|
|
43
|
+
try:
|
|
44
|
+
if time.time() - lock.stat().st_mtime > _LOCK_STALE_SEC:
|
|
45
|
+
lock.rmdir()
|
|
46
|
+
continue
|
|
47
|
+
except OSError:
|
|
48
|
+
pass
|
|
49
|
+
if time.monotonic() >= deadline:
|
|
50
|
+
break
|
|
51
|
+
time.sleep(0.1)
|
|
52
|
+
except OSError:
|
|
53
|
+
break
|
|
54
|
+
try:
|
|
55
|
+
yield acquired
|
|
56
|
+
finally:
|
|
57
|
+
if acquired:
|
|
58
|
+
with contextlib.suppress(OSError):
|
|
59
|
+
lock.rmdir()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _lock(tool: str, identity: str) -> Generator[bool, None, None]:
|
|
63
|
+
return _lock_path(_root() / f"{tool}-{_state_slug(tool, identity)}.lock")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def _valid(tool: str, identity: str | None) -> bool:
|
|
67
|
+
return tool in _VALID_TOOLS and isinstance(identity, str) and bool(identity)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _legacy_identity(profile_alias: str) -> str:
|
|
71
|
+
homes = {
|
|
72
|
+
"work": Path.home() / ".codex",
|
|
73
|
+
"personal": Path.home() / ".ai-profiles" / "runtime" / "codex" / "personal",
|
|
74
|
+
}
|
|
75
|
+
return f"auth={(homes[profile_alias] / 'auth.json').resolve(strict=False)}\0profile="
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _legacy_profile(tool: str, identity: str, profile_alias: str | None) -> str | None:
|
|
79
|
+
if tool != "codex" or profile_alias not in _LEGACY_PROFILES:
|
|
80
|
+
return None
|
|
81
|
+
if identity != _legacy_identity(profile_alias):
|
|
82
|
+
return None
|
|
83
|
+
return profile_alias
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def _legacy_state_file(tool: str, profile_alias: str) -> Path:
|
|
87
|
+
return _root() / f"{tool}-{profile_alias}.blocked-until"
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _legacy_lock(tool: str, profile_alias: str) -> Generator[bool, None, None]:
|
|
91
|
+
return _lock_path(_root() / f"{tool}-{profile_alias}.lock")
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
def _read_block(state: Path, lock: Generator[bool, None, None]) -> int | None:
|
|
95
|
+
with lock as acquired:
|
|
96
|
+
if not acquired or not state.exists():
|
|
97
|
+
return None
|
|
98
|
+
raw = state.read_text().strip()
|
|
99
|
+
if not raw.isdigit() or int(raw) <= int(time.time()):
|
|
100
|
+
state.unlink(missing_ok=True)
|
|
101
|
+
return None
|
|
102
|
+
return int(raw)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _write_state(state: Path, lock: Generator[bool, None, None], proposed: int) -> None:
|
|
106
|
+
with lock as acquired:
|
|
107
|
+
if not acquired:
|
|
108
|
+
return
|
|
109
|
+
current = state.read_text().strip() if state.exists() else ""
|
|
110
|
+
expiry = max(proposed, int(current) if current.isdigit() else 0)
|
|
111
|
+
temp = state.with_name(f"{state.name}.tmp.{os.getpid()}")
|
|
112
|
+
temp.write_text(f"{expiry}\n")
|
|
113
|
+
os.chmod(temp, 0o600)
|
|
114
|
+
os.replace(temp, state)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def check_blocked(
|
|
118
|
+
tool: str, identity: str | None, *, profile_alias: str | None = None
|
|
119
|
+
) -> tuple[bool, int | None]:
|
|
120
|
+
if not _valid(tool, identity):
|
|
121
|
+
return False, None
|
|
122
|
+
assert identity is not None
|
|
123
|
+
try:
|
|
124
|
+
_root().mkdir(parents=True, exist_ok=True, mode=0o700)
|
|
125
|
+
expiries = [_read_block(_state_file(tool, identity), _lock(tool, identity))]
|
|
126
|
+
legacy = _legacy_profile(tool, identity, profile_alias)
|
|
127
|
+
if legacy is not None:
|
|
128
|
+
expiries.append(
|
|
129
|
+
_read_block(_legacy_state_file(tool, legacy), _legacy_lock(tool, legacy))
|
|
130
|
+
)
|
|
131
|
+
active = [expiry for expiry in expiries if expiry is not None]
|
|
132
|
+
if not active:
|
|
133
|
+
return False, None
|
|
134
|
+
return True, max(active)
|
|
135
|
+
except (OSError, ValueError):
|
|
136
|
+
return False, None
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def write_block(
|
|
140
|
+
tool: str,
|
|
141
|
+
identity: str | None,
|
|
142
|
+
reset_epoch: int | None = None,
|
|
143
|
+
*,
|
|
144
|
+
profile_alias: str | None = None,
|
|
145
|
+
) -> None:
|
|
146
|
+
if not _valid(tool, identity):
|
|
147
|
+
return
|
|
148
|
+
assert identity is not None
|
|
149
|
+
try:
|
|
150
|
+
root = _root()
|
|
151
|
+
root.mkdir(parents=True, exist_ok=True, mode=0o700)
|
|
152
|
+
os.chmod(root, 0o700)
|
|
153
|
+
proposed = reset_epoch or int(time.time()) + _DEFAULT_COOLDOWN_SEC
|
|
154
|
+
_write_state(_state_file(tool, identity), _lock(tool, identity), proposed)
|
|
155
|
+
legacy = _legacy_profile(tool, identity, profile_alias)
|
|
156
|
+
if legacy is not None:
|
|
157
|
+
_write_state(_legacy_state_file(tool, legacy), _legacy_lock(tool, legacy), proposed)
|
|
158
|
+
except (OSError, ValueError):
|
|
159
|
+
pass
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def clear_block(tool: str, identity: str | None, *, profile_alias: str | None = None) -> None:
|
|
163
|
+
if not _valid(tool, identity):
|
|
164
|
+
return
|
|
165
|
+
assert identity is not None
|
|
166
|
+
try:
|
|
167
|
+
with _lock(tool, identity) as acquired:
|
|
168
|
+
if acquired:
|
|
169
|
+
_state_file(tool, identity).unlink(missing_ok=True)
|
|
170
|
+
legacy = _legacy_profile(tool, identity, profile_alias)
|
|
171
|
+
if legacy is not None:
|
|
172
|
+
with _legacy_lock(tool, legacy) as acquired:
|
|
173
|
+
if acquired:
|
|
174
|
+
_legacy_state_file(tool, legacy).unlink(missing_ok=True)
|
|
175
|
+
except OSError:
|
|
176
|
+
pass
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def parse_reset_epoch(text: str) -> int | None:
|
|
180
|
+
match = re.search(r"try again at (\d{1,2}):(\d{2})\s*(AM|PM)", text, re.IGNORECASE)
|
|
181
|
+
if not match:
|
|
182
|
+
return None
|
|
183
|
+
hour, minute = int(match.group(1)), int(match.group(2))
|
|
184
|
+
if not 1 <= hour <= 12 or not 0 <= minute <= 59:
|
|
185
|
+
return None
|
|
186
|
+
if match.group(3).upper() == "PM" and hour != 12:
|
|
187
|
+
hour += 12
|
|
188
|
+
elif match.group(3).upper() == "AM" and hour == 12:
|
|
189
|
+
hour = 0
|
|
190
|
+
now = datetime.datetime.now()
|
|
191
|
+
reset = now.replace(hour=hour, minute=minute, second=0, microsecond=0)
|
|
192
|
+
if reset <= now:
|
|
193
|
+
reset += datetime.timedelta(days=1)
|
|
194
|
+
return int(reset.timestamp())
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def epoch_to_human(epoch: int) -> str:
|
|
198
|
+
try:
|
|
199
|
+
return datetime.datetime.fromtimestamp(epoch).strftime("%H:%M")
|
|
200
|
+
except (OSError, OverflowError, ValueError):
|
|
201
|
+
return "?:??"
|
{delegate_agent_cli-0.27.0 → delegate_agent_cli-0.28.0}/src/delegate_agent/inspection_commands.py
RENAMED
|
@@ -8,6 +8,22 @@ from delegate_agent import command_errors, redaction, run_registry, snapshot_vie
|
|
|
8
8
|
from delegate_agent import rendering as delegate_rendering
|
|
9
9
|
from delegate_agent.json_types import JsonObject
|
|
10
10
|
|
|
11
|
+
STRUCTURAL_RUN_KEYS = (
|
|
12
|
+
"runId",
|
|
13
|
+
"alias",
|
|
14
|
+
"harness",
|
|
15
|
+
"group",
|
|
16
|
+
"mode",
|
|
17
|
+
"modelAlias",
|
|
18
|
+
"modelResolved",
|
|
19
|
+
"rawStatus",
|
|
20
|
+
"effectiveStatus",
|
|
21
|
+
"status",
|
|
22
|
+
"terminalStatus",
|
|
23
|
+
"activityAt",
|
|
24
|
+
"initiatorRoot",
|
|
25
|
+
)
|
|
26
|
+
|
|
11
27
|
|
|
12
28
|
@dataclass(frozen=True)
|
|
13
29
|
class SnapshotCommand:
|
|
@@ -28,6 +44,7 @@ class RunsCommand:
|
|
|
28
44
|
limit: int | None = None
|
|
29
45
|
older_than_days: int | None = None
|
|
30
46
|
dry_run: bool = False
|
|
47
|
+
structural: bool = False
|
|
31
48
|
json_mode: bool = False
|
|
32
49
|
|
|
33
50
|
|
|
@@ -121,7 +138,15 @@ def emit_runs(command: RunsCommand, *, workspace_path: str, stdout: TextIO) -> i
|
|
|
121
138
|
group=command.group,
|
|
122
139
|
limit=limit,
|
|
123
140
|
)
|
|
124
|
-
|
|
141
|
+
if command.structural:
|
|
142
|
+
summaries = [
|
|
143
|
+
redaction.redact_value(
|
|
144
|
+
{key: summary[key] for key in STRUCTURAL_RUN_KEYS if key in summary}
|
|
145
|
+
)
|
|
146
|
+
for summary in summaries
|
|
147
|
+
]
|
|
148
|
+
else:
|
|
149
|
+
summaries = [redaction.redact_value(summary) for summary in summaries]
|
|
125
150
|
if command.json_mode:
|
|
126
151
|
delegate_rendering.print_json(
|
|
127
152
|
delegate_rendering.runs_json_payload(summaries, limit=limit, mode=mode),
|