delegate-agent-cli 0.31.0__tar.gz → 0.32.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.31.0 → delegate_agent_cli-0.32.0}/CHANGELOG.md +1185 -1
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/CONTRIBUTING.md +7 -6
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/PKG-INFO +26 -12
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/README.md +23 -11
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/bin/delegate-profile-shim +131 -4
- delegate_agent_cli-0.32.0/bin/delegate.py +31 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/config.example.json +14 -1
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/docs/agent-setup.md +1 -2
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/docs/cli-reference.md +639 -134
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/docs/configuration.md +292 -21
- delegate_agent_cli-0.32.0/docs/delegate-workflows.md +784 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/docs/development.md +19 -10
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/docs/live-runtime.md +4 -2
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/docs/publishing-checklist.md +2 -8
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/docs/security-model.md +288 -10
- delegate_agent_cli-0.32.0/docs/troubleshooting.md +1040 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/docs/worktrees.md +257 -5
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/pyproject.toml +8 -1
- delegate_agent_cli-0.32.0/src/delegate_agent/__init__.py +24 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/argv_builders.py +120 -0
- delegate_agent_cli-0.32.0/src/delegate_agent/auth_health.py +295 -0
- delegate_agent_cli-0.32.0/src/delegate_agent/auto_resume.py +227 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/bundled_models.py +9 -10
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/capability_commands.py +27 -3
- delegate_agent_cli-0.32.0/src/delegate_agent/child_failures.py +189 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/cli.py +428 -58
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/cli_parser.py +450 -44
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/command_help.py +543 -77
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/config.py +130 -27
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/constants.py +2 -0
- delegate_agent_cli-0.32.0/src/delegate_agent/degraded.py +450 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/describe_payload.py +141 -41
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/errors.py +2 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/failover_state.py +10 -3
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/followup_command.py +104 -5
- delegate_agent_cli-0.32.0/src/delegate_agent/harness_enabled.py +120 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/harness_events.py +469 -44
- delegate_agent_cli-0.32.0/src/delegate_agent/inspection_commands.py +410 -0
- delegate_agent_cli-0.32.0/src/delegate_agent/lane_health.py +705 -0
- delegate_agent_cli-0.32.0/src/delegate_agent/lane_slice.py +156 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/mail.py +78 -2
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/mail_core.py +150 -1
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/mail_push.py +24 -2
- delegate_agent_cli-0.32.0/src/delegate_agent/model_discovery.py +770 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/notify.py +42 -5
- delegate_agent_cli-0.32.0/src/delegate_agent/outcome.py +279 -0
- delegate_agent_cli-0.32.0/src/delegate_agent/outside_cwd_changes.py +143 -0
- delegate_agent_cli-0.32.0/src/delegate_agent/pending_tool.py +80 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/private_io.py +13 -3
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/profiles.py +67 -22
- delegate_agent_cli-0.32.0/src/delegate_agent/prompt_instructions.py +84 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/prompt_transport.py +13 -0
- delegate_agent_cli-0.32.0/src/delegate_agent/provider_errors.py +816 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/reasoning.py +2 -6
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/record_io.py +28 -2
- delegate_agent_cli-0.32.0/src/delegate_agent/registry_roster.py +279 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/rendering.py +135 -4
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/request_build.py +635 -64
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/request_models.py +80 -4
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/resume_command.py +90 -6
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/retention.py +272 -6
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/run_context.py +18 -1
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/run_metadata.py +3 -1
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/run_output_commands.py +55 -3
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/run_registry.py +143 -19
- delegate_agent_cli-0.32.0/src/delegate_agent/run_scratch.py +554 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/run_status.py +217 -39
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/runner.py +1756 -206
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/safe_workspace.py +111 -3
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/sandbox_bwrap.py +106 -1
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/seatbelt.py +61 -2
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/snapshot_view.py +32 -2
- delegate_agent_cli-0.32.0/src/delegate_agent/stall_watchdog.py +1244 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/stream_capture.py +106 -18
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/structured_output.py +57 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/terminal_states.py +12 -1
- delegate_agent_cli-0.32.0/src/delegate_agent/usage_record.py +67 -0
- delegate_agent_cli-0.32.0/src/delegate_agent/wait_cancel_commands.py +1308 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/workflow_identity.py +14 -4
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/workflow_pinning.py +362 -6
- delegate_agent_cli-0.32.0/src/delegate_agent/workflows/__init__.py +12 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/workflows/commands.py +709 -44
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/workflows/registry.py +47 -6
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/workflows/runtime.py +1915 -183
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/workflows/schema.py +99 -4
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/workflows/script.py +50 -1
- delegate_agent_cli-0.32.0/src/delegate_agent/workflows/stage_guard.py +92 -0
- delegate_agent_cli-0.32.0/src/delegate_agent/workflows/status_reasons.py +163 -0
- delegate_agent_cli-0.32.0/src/delegate_agent/workspace_spec.py +636 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/worktree_commands.py +44 -9
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/worktree_execution.py +322 -12
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/worktree_gc.py +367 -35
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/worktree_mgmt.py +300 -36
- delegate_agent_cli-0.32.0/src/delegate_agent/worktree_procs.py +234 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/worktree_records.py +20 -1
- delegate_agent_cli-0.32.0/src/delegate_agent/worktree_remove.py +868 -0
- delegate_agent_cli-0.32.0/src/delegate_agent/worktree_salvage.py +224 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/worktree_summary.py +98 -32
- delegate_agent_cli-0.32.0/src/delegate_agent/write_guard.py +727 -0
- delegate_agent_cli-0.32.0/src/delegate_agent/write_guard_launch.py +383 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent_cli.egg-info/PKG-INFO +26 -12
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent_cli.egg-info/SOURCES.txt +75 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent_cli.egg-info/requires.txt +2 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_acceptance_toolchain.py +58 -24
- delegate_agent_cli-0.32.0/tests/test_appendix_failure_strings.py +141 -0
- delegate_agent_cli-0.32.0/tests/test_auth_health.py +366 -0
- delegate_agent_cli-0.32.0/tests/test_auto_resume.py +457 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_bundled_models_lane_r.py +6 -19
- delegate_agent_cli-0.32.0/tests/test_c5_validation.py +502 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_cancel_terminal_override.py +179 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_capability_commands.py +60 -86
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_child_failures.py +19 -12
- delegate_agent_cli-0.32.0/tests/test_cli_ergonomics.py +308 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_cli_simplification.py +11 -28
- delegate_agent_cli-0.32.0/tests/test_cli_surface_fixes.py +108 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_codex_pure_sandbox.py +68 -1
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_codex_scratch.py +222 -20
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_codex_thread_resilience.py +1 -1
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_command_help.py +31 -85
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_config_commands.py +179 -15
- delegate_agent_cli-0.32.0/tests/test_cross_registry_lookup.py +310 -0
- delegate_agent_cli-0.32.0/tests/test_degraded.py +585 -0
- delegate_agent_cli-0.32.0/tests/test_degraded_runs.py +787 -0
- delegate_agent_cli-0.32.0/tests/test_delegate_audit_shim.py +33 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_delegate_help_cli.py +42 -112
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_delegate_isolation.py +25 -16
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_delegate_parser.py +194 -274
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_delegate_validation.py +128 -79
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_describe_models_binary.py +57 -0
- delegate_agent_cli-0.32.0/tests/test_describe_workflow_dsl.py +93 -0
- delegate_agent_cli-0.32.0/tests/test_devin_bounded_output.py +181 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_devin_read_only_behavior.py +26 -14
- delegate_agent_cli-0.32.0/tests/test_docs_parser_conformance.py +497 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_end_to_end_tracking.py +25 -1
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_engine_argv.py +34 -12
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_execution_argv_and_prompt.py +657 -20
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_execution_dry_run.py +29 -27
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_execution_worktree_failure_cleanup.py +4 -68
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_execution_worktree_preflight.py +24 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_execution_worktree_run.py +343 -15
- delegate_agent_cli-0.32.0/tests/test_failover_state.py +173 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_followup_after_retry_e2e.py +144 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_followup_capture.py +47 -2
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_followup_parser.py +5 -11
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_followup_refusals.py +86 -1
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_harness_discovery.py +22 -48
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_harness_events.py +292 -173
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_inspection_commands.py +115 -60
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_lane_a_transport.py +109 -72
- delegate_agent_cli-0.32.0/tests/test_lane_b_lifecycle.py +194 -0
- delegate_agent_cli-0.32.0/tests/test_lane_health.py +733 -0
- delegate_agent_cli-0.32.0/tests/test_lane_known_bad.py +274 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_lane_m_mail_defaults.py +1 -1
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_lane_s_structured_retry.py +60 -3
- delegate_agent_cli-0.32.0/tests/test_lane_slice.py +139 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_launch_input_parity.py +50 -11
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_launch_mapping.py +2 -4
- delegate_agent_cli-0.32.0/tests/test_launch_model_resolution.py +276 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_mail_bounds.py +13 -13
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_mail_contract.py +23 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_mail_delivery.py +25 -16
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_mail_eligibility.py +28 -7
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_mail_framing.py +2 -27
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_mail_gating.py +9 -11
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_mail_prune.py +0 -12
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_mail_push.py +21 -61
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_mail_push_adapters.py +14 -8
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_mail_push_batch.py +4 -6
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_mail_push_cursor.py +25 -5
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_mail_push_gating.py +1 -1
- delegate_agent_cli-0.32.0/tests/test_mail_push_inheritance.py +120 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_mail_push_seams.py +12 -3
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_mail_resolution.py +25 -17
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_mail_rules.py +7 -0
- delegate_agent_cli-0.32.0/tests/test_mail_unread_steering.py +267 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_model_discovery.py +206 -198
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_model_selection_wave1a.py +20 -126
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_model_selection_wave1b.py +13 -36
- delegate_agent_cli-0.32.0/tests/test_no_page_ask.py +75 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_notify.py +72 -26
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_omp_output_capture.py +48 -5
- delegate_agent_cli-0.32.0/tests/test_omp_provider_pin.py +959 -0
- delegate_agent_cli-0.32.0/tests/test_omp_tool_update_compaction.py +243 -0
- delegate_agent_cli-0.32.0/tests/test_outcome_contract.py +718 -0
- delegate_agent_cli-0.32.0/tests/test_output_cap_off.py +236 -0
- delegate_agent_cli-0.32.0/tests/test_outside_cwd_changes.py +119 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_packaging.py +10 -3
- delegate_agent_cli-0.32.0/tests/test_partial_output_recovery.py +547 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_persona_capability.py +9 -26
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_persona_claude.py +0 -11
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_persona_framer.py +44 -70
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_persona_opencode.py +0 -10
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_persona_parser.py +0 -12
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_persona_records.py +11 -13
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_persona_resolution.py +45 -113
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_persona_size_guard.py +12 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_persona_workflow.py +104 -95
- delegate_agent_cli-0.32.0/tests/test_private_io_dir_race.py +57 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_proc_harness.py +23 -15
- delegate_agent_cli-0.32.0/tests/test_process_guard.py +149 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_profiles.py +108 -7
- delegate_agent_cli-0.32.0/tests/test_provider_error_docs.py +95 -0
- delegate_agent_cli-0.32.0/tests/test_provider_error_records.py +395 -0
- delegate_agent_cli-0.32.0/tests/test_provider_errors.py +265 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_provider_outcomes.py +12 -14
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_pure_call.py +7 -2
- delegate_agent_cli-0.32.0/tests/test_python_version_guard.py +253 -0
- delegate_agent_cli-0.32.0/tests/test_real_binary_contracts.py +499 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_reasoning_capabilities.py +34 -169
- delegate_agent_cli-0.32.0/tests/test_registry_contention.py +279 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_registry_lock_guard.py +61 -0
- delegate_agent_cli-0.32.0/tests/test_registry_roster.py +183 -0
- delegate_agent_cli-0.32.0/tests/test_resumable_default.py +538 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_resume_attachment.py +30 -7
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_resume_capture.py +6 -1
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_resume_inheritance.py +193 -4
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_resume_parser.py +3 -7
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_resume_trust.py +29 -10
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_retention.py +85 -97
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_run_listing_cost.py +7 -7
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_run_registry.py +28 -71
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_runner_capture.py +984 -84
- delegate_agent_cli-0.32.0/tests/test_runs_deadline.py +191 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_safe_workspace_isolation.py +161 -4
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_sandbox_bwrap.py +211 -21
- delegate_agent_cli-0.32.0/tests/test_scratch_reclaim.py +943 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_simplification_integration.py +12 -4
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_single_run_record.py +4 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_snapshot_redaction.py +10 -6
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_snapshot_rendering.py +29 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_snapshot_run_output.py +260 -6
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_snapshot_view.py +27 -0
- delegate_agent_cli-0.32.0/tests/test_stall_watchdog.py +1454 -0
- delegate_agent_cli-0.32.0/tests/test_stall_watchdog_runner.py +196 -0
- delegate_agent_cli-0.32.0/tests/test_status_truth.py +340 -0
- delegate_agent_cli-0.32.0/tests/test_stream_usage_ingest.py +205 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_structured_output.py +72 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_test_environment.py +2 -1
- delegate_agent_cli-0.32.0/tests/test_test_runner.py +83 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_tracked_output_bounds.py +27 -11
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_tracked_timeout.py +0 -51
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_utility_modules.py +7 -16
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_wait_cancel_commands.py +543 -101
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_wave4_launch_features.py +162 -8
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_workflow_attempts.py +10 -5
- delegate_agent_cli-0.32.0/tests/test_workflow_check_keying.py +121 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_workflow_commands.py +605 -114
- delegate_agent_cli-0.32.0/tests/test_workflow_failure_policy.py +233 -0
- delegate_agent_cli-0.32.0/tests/test_workflow_key_stability.py +68 -0
- delegate_agent_cli-0.32.0/tests/test_workflow_keyed_steps.py +1125 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_workflow_pinning.py +6 -22
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_workflow_profile_identity.py +5 -0
- delegate_agent_cli-0.32.0/tests/test_workflow_provider_stage.py +341 -0
- delegate_agent_cli-0.32.0/tests/test_workflow_repin.py +1264 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_workflow_retry_outcomes.py +598 -25
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_workflow_schema.py +13 -2
- delegate_agent_cli-0.32.0/tests/test_workflow_session_fallback.py +413 -0
- delegate_agent_cli-0.32.0/tests/test_workflow_status_reasons.py +491 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_workflow_watchdog.py +41 -5
- delegate_agent_cli-0.32.0/tests/test_workspace_spec.py +1489 -0
- delegate_agent_cli-0.32.0/tests/test_worktree_effective_dirt.py +502 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_worktree_list_show.py +1 -24
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_worktree_prune_gc.py +92 -33
- delegate_agent_cli-0.32.0/tests/test_worktree_reap_orphan.py +722 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_worktree_remove.py +419 -18
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_worktree_retirement.py +175 -8
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_worktree_safety_policy.py +11 -3
- delegate_agent_cli-0.32.0/tests/test_worktree_salvage.py +416 -0
- delegate_agent_cli-0.32.0/tests/test_worktree_symlink_placeholders.py +191 -0
- delegate_agent_cli-0.32.0/tests/test_write_guard.py +1543 -0
- delegate_agent_cli-0.32.0/tests/test_write_guard_e2e.py +333 -0
- delegate_agent_cli-0.32.0/tests/test_write_guard_live.py +499 -0
- delegate_agent_cli-0.31.0/bin/delegate.py +0 -15
- delegate_agent_cli-0.31.0/docs/delegate-workflows.md +0 -391
- delegate_agent_cli-0.31.0/docs/troubleshooting.md +0 -595
- delegate_agent_cli-0.31.0/src/delegate_agent/__init__.py +0 -5
- delegate_agent_cli-0.31.0/src/delegate_agent/child_failures.py +0 -133
- delegate_agent_cli-0.31.0/src/delegate_agent/inspection_commands.py +0 -190
- delegate_agent_cli-0.31.0/src/delegate_agent/model_discovery.py +0 -350
- delegate_agent_cli-0.31.0/src/delegate_agent/prompt_instructions.py +0 -58
- delegate_agent_cli-0.31.0/src/delegate_agent/run_scratch.py +0 -229
- delegate_agent_cli-0.31.0/src/delegate_agent/stall_watchdog.py +0 -607
- delegate_agent_cli-0.31.0/src/delegate_agent/wait_cancel_commands.py +0 -709
- delegate_agent_cli-0.31.0/src/delegate_agent/workflows/__init__.py +0 -6
- delegate_agent_cli-0.31.0/src/delegate_agent/worktree_remove.py +0 -465
- delegate_agent_cli-0.31.0/tests/test_failover_state.py +0 -105
- delegate_agent_cli-0.31.0/tests/test_lane_b_lifecycle.py +0 -73
- delegate_agent_cli-0.31.0/tests/test_process_guard.py +0 -42
- delegate_agent_cli-0.31.0/tests/test_registry_contention.py +0 -145
- delegate_agent_cli-0.31.0/tests/test_stall_watchdog.py +0 -445
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/LICENSE +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/MANIFEST.in +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/SECURITY.md +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/docs/assets/delegate-agent-header.png +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/examples/task.claude.json +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/examples/task.codex.json +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/examples/task.cursor.json +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/examples/task.droid.json +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/examples/task.grok.json +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/examples/task.judge.json +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/examples/task.kimi.json +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/setup.cfg +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/account_binding.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/archived_logs.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/argv_utils.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/command_errors.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/config_commands.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/git_utils.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/harness_discovery.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/isolation.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/json_types.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/log_output.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/personas.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/profile_commands.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/profile_guard.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/redaction.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/setup_commands.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/workflow_attempts.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent/wsl.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent_cli.egg-info/dependency_links.txt +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent_cli.egg-info/entry_points.txt +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/src/delegate_agent_cli.egg-info/top_level.txt +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_account_binding.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_call_capture_contract.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_call_text_fixtures.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_child_root_env.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_discovery_contracts.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_engine_universe.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_harness_json_limits.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_isolation_plans.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_kimi_commands.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_lane_r_public_surface.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_launch_error_hints.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_mail_identity.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_mail_io.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_mail_parser.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_mail_reply.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_mail_tempdir.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_nested_workflow_threads.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_omp_read_only_behavior.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_persona_contract.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_persona_validation.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_private_directory_publish.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_private_io_bounded.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_resume_projections.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_resume_refusals.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_resume_size_guard.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_resume_stale.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_run_record_imports.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_setup_commands.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_slash_passthrough.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_stall_watchdog_cursor.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_terminal_states.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_workflow_gate_binding.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_workflow_journal_reader.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_workflow_jsonl_cli.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_workflow_simplification.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_workflow_watchdog_unit.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_worktree_record_bundle.py +0 -0
- {delegate_agent_cli-0.31.0 → delegate_agent_cli-0.32.0}/tests/test_wsl_guardrails.py +0 -0
|
@@ -5,6 +5,1189 @@ 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
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.32.0] - 2026-10-01
|
|
11
|
+
|
|
12
|
+
This release adds controls for launching and managing agents and resuming
|
|
13
|
+
workflows: `--force-launch`, `--writable`, `--kill-live`, and `--repin`.
|
|
14
|
+
It also improves progress reports, coordinator mail, failure recovery, and
|
|
15
|
+
write protection, and makes optional local profile tooling work on other
|
|
16
|
+
machines without requiring the maintainer's setup.
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
- **Every work and safe Run carries the lane test rule.** After the operator's
|
|
20
|
+
task, the framed prompt adds a "Delegate lane test rule" section: lanes run
|
|
21
|
+
targeted tests only (the files they changed and their neighbours, through
|
|
22
|
+
`testrun` with worker caps), and the full suite and the project gate stay
|
|
23
|
+
with the coordinator (Trey, 2026-09-30). Call mode, unmoded prompts, and
|
|
24
|
+
slash passthrough are untouched; reframing never repeats it.
|
|
25
|
+
- `runs` and `ps` show how long a running Run has left: a running Run with a
|
|
26
|
+
timeout carries `deadlineAt` (recorded by the runner when its timeout clock
|
|
27
|
+
starts, after workspace setup) and `remainingSeconds`, and the text listing
|
|
28
|
+
prefixes `current` with `12m left`. Terminal Runs carry neither.
|
|
29
|
+
- **A live Run shows the tool call it is waiting on.** While a tool call is in
|
|
30
|
+
flight, `runs` and `snapshot` carry `pendingTool` (`name`, optional
|
|
31
|
+
`target`, `startedAt`, `seconds`; the oldest pending call, redacted like
|
|
32
|
+
`current`), and after a minute `current` reads `waiting on tool <name> for
|
|
33
|
+
13m`. It is refreshed whenever the pending set changes, tracked even with
|
|
34
|
+
`--stall-minutes 0`, and dropped from terminal records. The stall watchdog
|
|
35
|
+
still never fires while a tool is pending: a hung tool is surfaced, not
|
|
36
|
+
killed.
|
|
37
|
+
- A followup or session resume whose child exits non-zero within 15 seconds
|
|
38
|
+
with no stdout or stderr stays `child_failed`, but the message says the
|
|
39
|
+
saved native session probably could not be loaded and `nextActions` offers
|
|
40
|
+
`delegate resume <handle>`.
|
|
41
|
+
- **Early-stop detection.** A succeeded Run whose last sentence announces a
|
|
42
|
+
next step ("Now let me write my report.") carries `degraded:
|
|
43
|
+
ended_announcing_next_step`, and a work Run that parks on an approval
|
|
44
|
+
question with no changed files carries `degraded: ended_awaiting_input`.
|
|
45
|
+
Status stays `succeeded`; the warning names `delegate resume <handle>`. A
|
|
46
|
+
finished deliverable that ends with "Should I proceed?" is not flagged.
|
|
47
|
+
- **Unread coordinator mail is surfaced at run end.** A terminal work Run
|
|
48
|
+
records `unreadMail` (count, up to three id/sender/subject rows, and an
|
|
49
|
+
`unreadable` count for damaged or non-regular inbox entries) plus a warning
|
|
50
|
+
on the state, envelope, completion report, snapshot, `wait`, and `cancel`
|
|
51
|
+
reply. Messages a push hook already injected count as read. The mail prompt
|
|
52
|
+
suffix now asks lanes to read their inbox before final verification or
|
|
53
|
+
commit. Launch, dry-run, and manifest carry `mailInbox` (host and mail
|
|
54
|
+
root).
|
|
55
|
+
- `followup` and `resume` inherit `--mail-push` from the source Run and accept
|
|
56
|
+
`--no-mail-push`; `followup` also accepts `--mail-push`.
|
|
57
|
+
- **One automatic fresh rerun for transient safe-mode failures.** A `safe`
|
|
58
|
+
Run that fails with `stream_disconnected` or `provider_unavailable`, or a
|
|
59
|
+
`safe` launch the broker refuses (`broker_binding_inactive`) with no child
|
|
60
|
+
output, is rerun once as a new Run recorded under `autoResume` with `kind:
|
|
61
|
+
"rerun"` and the first attempt's error. The first broker refusal defers the
|
|
62
|
+
known-bad lane marker (`laneMarkerDeferred: "broker_binding_retry"`). Work
|
|
63
|
+
Runs are never rerun this way. `providerErrors.autoResume: false` turns it
|
|
64
|
+
off.
|
|
65
|
+
- **`<engine>.enabled: false`** hides a harness from `models`, `describe`,
|
|
66
|
+
`capabilities`, and `capabilities refresh`, and any launch of it fails with
|
|
67
|
+
`harness_disabled` naming the key.
|
|
68
|
+
- **Claude model typos are refused before launch.** A typed `--model` that is
|
|
69
|
+
a family word plus a version (`opus-5.5`, `Sonnet 5`) or names no model
|
|
70
|
+
(`claude-`, an empty `[]`) fails with `invalid_alias` and the closest valid
|
|
71
|
+
values, on the CLI, dry runs, and `run --input-json`. Provider names
|
|
72
|
+
(Bedrock ARNs, Foundry deployments, gateway strings) still launch, and the
|
|
73
|
+
version-typo refusal is skipped when a Bedrock, Vertex, Foundry, or
|
|
74
|
+
`ANTHROPIC_BASE_URL` gateway variable is set.
|
|
75
|
+
- Workflow status says why and what next: a paused workflow carries a `pause`
|
|
76
|
+
object (gate, actions, last failure, rejection text) or a `softPark` reason
|
|
77
|
+
naming `delegate workflow resume <wfId>`; agent journal rows always carry
|
|
78
|
+
label and item, `agent_started` records `timeout` and `deadlineAt`,
|
|
79
|
+
`agent_timeout` names `nextEngine`, and status lists recent timeouts.
|
|
80
|
+
- Codex structured output: a root-array answer wrapped in a one-key object is
|
|
81
|
+
unwrapped on the prompt path and journaled as `agent_output_unwrapped`.
|
|
82
|
+
- Worktree warnings name the files: `dirty_source_auto_included_paths`,
|
|
83
|
+
`dirty_source_included`/`dirty_source_included_paths` for explicit
|
|
84
|
+
`--include-dirty`, `dirty_source_mirrored` for safe worktree launches, and
|
|
85
|
+
`dirtySourcePreview` in dry runs (up to five paths each).
|
|
86
|
+
- `workflow resume` and `workflow status` say when a workflow's pinned runtime
|
|
87
|
+
differs from the live one. The notice names both runtimes (digest, delegate
|
|
88
|
+
version, and the pin or promotion date), and JSON carries the same facts as
|
|
89
|
+
`runtimePin` (`differs`, `pinned`, `live`; on a resume the notice text is
|
|
90
|
+
also in `warnings`). `status` prints no notice for a finished (`succeeded`
|
|
91
|
+
or `dry_run`) workflow, and a pin that cannot be read reports `checked:
|
|
92
|
+
false` with the reason instead of blocking the resume.
|
|
93
|
+
- `--repin` on `workflow resume`, `workflow run --resume`, and `workflow
|
|
94
|
+
approve` is an opt-in that moves a workflow onto the live runtime; a plain
|
|
95
|
+
resume still keeps the pin. It keeps the journal, step keys, frozen script,
|
|
96
|
+
arguments, config, personas, and profile identity, moves the old runtime
|
|
97
|
+
into `runtimeHistory`, and journals one `runtime_repinned` event. The
|
|
98
|
+
identity check still runs first, and the new pin is written aside and
|
|
99
|
+
validated before it replaces the old one. A failed validation, a failed
|
|
100
|
+
launch, or a resume where no supervisor ever starts on the new pin restores
|
|
101
|
+
the old pin byte for byte and journals `runtime_repin_rolled_back`.
|
|
102
|
+
`--repin` is refused with `repin_children_running` while a child run is
|
|
103
|
+
running or still starting, with `invalid_option_combination` on a new run or
|
|
104
|
+
with `--dry-run`, with `workflow_locked` when another resume holds the lock,
|
|
105
|
+
and when the existing pin cannot be verified. `WORKFLOW_KEY_VERSION` remains
|
|
106
|
+
the guard against step-key drift. A script that branches on `capabilities`
|
|
107
|
+
can take a different branch after a repin, because the map then describes
|
|
108
|
+
the live runtime.
|
|
109
|
+
- `workflow check` warns about unkeyed steps in a script that keys others.
|
|
110
|
+
When some `agent()`, `parallel()`, `pipeline()`, or `workflow()` calls pass
|
|
111
|
+
`key=` and others do not, it emits one `keying warning` per primitive kind
|
|
112
|
+
with the source lines of the unkeyed calls (at most eight), because those
|
|
113
|
+
calls still replay by position and prompt and lose settled work when a
|
|
114
|
+
resumed script shifts. A script that keys nothing is left alone, a call with
|
|
115
|
+
a `**kwargs` expansion is assumed to carry its key, and `judges()` and
|
|
116
|
+
`followup()` are never flagged.
|
|
117
|
+
- A run or workflow id that is not in the current workspace is looked up in a
|
|
118
|
+
small roster of the other workspaces Delegate has launched in
|
|
119
|
+
(`~/.delegate/registries.json`, private, at most 256 entries, most recently
|
|
120
|
+
used first; workspaces whose `.delegate` is gone are skipped). `snapshot`
|
|
121
|
+
and `run-output` read a run id found in exactly one other workspace directly
|
|
122
|
+
and report `resolutionKind: "cross_registry"`, `resolvedWorkspace`, and a
|
|
123
|
+
`cross_registry:` warning. `wait`, `cancel`, `resume`, `followup`, and
|
|
124
|
+
`worktree show` never act across workspaces: they fail with
|
|
125
|
+
`unknown_handle`, name the workspace, and put the exact `delegate --cwd
|
|
126
|
+
<workspace> ...` command first in `nextActions`. The workflow read actions
|
|
127
|
+
`status`, `events`, `watch`, `wait`, and `result` read a workflow found in
|
|
128
|
+
exactly one other workspace, and `status`, `wait`, and `result` add
|
|
129
|
+
`resolvedWorkspace`; `approve`, `reject`, `kill`, and `run --resume` fail
|
|
130
|
+
with `workflow_not_found` and the exact command instead. An id found in
|
|
131
|
+
several workspaces is listed, never guessed, and a numbered alias is never
|
|
132
|
+
auto-resolved: the error lists every workspace that has it. Only workspaces
|
|
133
|
+
used for a launch after the roster shipped are known.
|
|
134
|
+
- OMP `tool_execution_update` records whose `args` or `partialResult` is
|
|
135
|
+
oversized are shrunk to a `{"delegateCompacted": true, "originalBytes": N,
|
|
136
|
+
"head": ..., "tail": ...}` stub. The sub-agent `task` tool re-emits its
|
|
137
|
+
whole shared context on every update; `tool_execution_start` and
|
|
138
|
+
`tool_execution_end` carry the full arguments and result once and are never
|
|
139
|
+
touched. `stdoutCapture` (policy `omp-capture-v2`) reports
|
|
140
|
+
`compactedToolUpdateRecords` and `compactedToolUpdateBytes`, the run gets a
|
|
141
|
+
warning, and `--raw` cannot recover the compacted text.
|
|
142
|
+
- Tracked Pi, OMP, and OpenCode runs ingest token `usage` from the stream,
|
|
143
|
+
summed per message or step and not double counted.
|
|
144
|
+
- Run and call envelopes, run summaries, and snapshots carry `servedModel`
|
|
145
|
+
and, when the harness reports one, `servedProvider`: what the child's stream
|
|
146
|
+
says actually answered. They sit beside `modelResolved` (the request),
|
|
147
|
+
repeat inside `modelProvenance`, and are absent when the harness reported no
|
|
148
|
+
model. A Claude call reports only a per-run `modelUsage` total, so its
|
|
149
|
+
`servedModel` is set only when exactly one model produced output; with
|
|
150
|
+
several it is absent and a pinned call warns `pinned_continuity_unverified`.
|
|
151
|
+
An ungrouped `call` is now held to the same continuity rule as a tracked
|
|
152
|
+
run: a pinned call whose stream names another model or provider fails with
|
|
153
|
+
`model_continuity_paused` (text mode prints no answer text for it; JSON
|
|
154
|
+
keeps any text beside the error), and a fungible or panel call carries the
|
|
155
|
+
`model_substitution` warning, provider included.
|
|
156
|
+
- A tracked run whose child ends its turn mid-job is flagged as degraded. A
|
|
157
|
+
headless child ends when its model stops, and background Bash tasks and
|
|
158
|
+
Monitors die with the session, so a Claude work run that said "Waiting on
|
|
159
|
+
the full gate" used to be recorded `succeeded` with `ok: true` while its
|
|
160
|
+
gate never finished. The status stays `succeeded` so the work can be
|
|
161
|
+
adopted, but the run now carries `degraded: true` (present only when true),
|
|
162
|
+
a `degradedReason`, `degradedEvidence` (bounded, redacted lines saying what
|
|
163
|
+
was seen), and a `degraded=...` warning. They appear on the launch envelope,
|
|
164
|
+
in state.json, on `wait` (JSON, `--structural`, and a `degraded:` line in
|
|
165
|
+
text), `snapshot`, `runs` and `ps` (the text table prefixes `current` with
|
|
166
|
+
`[degraded]`; `--structural` omits them), the `run-output
|
|
167
|
+
--completion-report` view, and, for a workflow child, the `agent_child`
|
|
168
|
+
journal event and `agent_meta()` (`degraded`, `degradedReason`). Nothing
|
|
169
|
+
fails a run or a workflow step, and `wait` still exits 0. `degradedReason`
|
|
170
|
+
is `ended_waiting_on_background_work` or
|
|
171
|
+
`background_work_unfinished_at_exit`. The first is a final-message check
|
|
172
|
+
that works for every engine: a short message that is not shaped like a
|
|
173
|
+
finished report and says the child is waiting on, or will act after,
|
|
174
|
+
unfinished work of its own. It ignores waiting on the requester ("I'll
|
|
175
|
+
commit when you approve"), a third party's independent result ("Waiting for
|
|
176
|
+
CI to post its result"), denials, and quoted text such as a tool's output; a
|
|
177
|
+
short report-shaped message matches only an explicit present-tense statement
|
|
178
|
+
that the child's own job is still running; runs with `--output-schema` are
|
|
179
|
+
not text-checked. The second reads Claude Code's `background_tasks_changed`
|
|
180
|
+
snapshot at the result event and catches a finished-looking report that
|
|
181
|
+
abandoned a running task; only Claude emits that event.
|
|
182
|
+
- A provider failure is now data. The failed run's envelope, run state, and
|
|
183
|
+
completion report carry a structured `providerError` (`status`,
|
|
184
|
+
`providerCode`, `message`, `engine`, `signature`, `class`, `hint`); the
|
|
185
|
+
message is bounded, credentials are redacted, and email addresses are
|
|
186
|
+
masked. `class` is `persistent`, `transient`, or `unknown`, and a persistent
|
|
187
|
+
signature is either lane-scoped (credentials, billing, or model access are
|
|
188
|
+
wrong) or request-scoped (this prompt is the problem). One engine-keyed
|
|
189
|
+
signature table classifies by HTTP status first, then provider code, and
|
|
190
|
+
uses text patterns only as the fallback, so `failureReason`, `failureKind`,
|
|
191
|
+
the hint, and the class agree, and an OMP HTTP 400 naming an image count or
|
|
192
|
+
size limit is `request_image_limit`, never an auth failure. The signature
|
|
193
|
+
list is in `docs/troubleshooting.md`. Two new `failureKind` values are
|
|
194
|
+
`lane_known_bad` (a launch refused, below) and `provider_exhausted`
|
|
195
|
+
(workflow only).
|
|
196
|
+
- A persistent, lane-scoped provider failure (rejected credentials, no credit,
|
|
197
|
+
usage limit, no access to the model, missing API key) marks its lane
|
|
198
|
+
known-bad for `providerErrors.knownBadLaneMinutes` (default `15`; `0` turns
|
|
199
|
+
markers off). Until the marker expires, another launch on that lane is
|
|
200
|
+
refused in milliseconds, before any worktree or child exists, with
|
|
201
|
+
`lane_known_bad` and the new exit code `4`; the payload carries `signature`,
|
|
202
|
+
`class`, `hint`, `expiresAt`, `secondsLeft`, and `markedRunId`. The global
|
|
203
|
+
`--force-launch` launches anyway (another persistent failure renews the
|
|
204
|
+
marker), a success clears the marker, and `--dry-run` never refuses.
|
|
205
|
+
`delegate doctor` lists live markers as `knownBadLanes`. Transient,
|
|
206
|
+
request-scoped, and unclassified failures never mark a lane, nor do endings
|
|
207
|
+
Delegate established itself (timeout, stall, output cap, cancel), and a
|
|
208
|
+
Codex usage limit with `codex.fallbackProfile` set stays with the profile
|
|
209
|
+
failover. The `providerErrors` config section is new, and an unknown key in
|
|
210
|
+
it fails as `invalid_provider_errors_config`.
|
|
211
|
+
- A known-bad lane is one engine on one provider, model, and account (the
|
|
212
|
+
Codex failover identity, `CLAUDE_CONFIG_DIR`, or the auth profile), plus the
|
|
213
|
+
credentials the child will see: API keys, tokens, and broker, realm,
|
|
214
|
+
account, and endpoint settings in the environment, and for OpenCode the
|
|
215
|
+
provider keys, base URLs, and `Authorization` headers inside
|
|
216
|
+
`OPENCODE_CONFIG_CONTENT`. They are folded in as a salted hash (the salt is
|
|
217
|
+
`~/.delegate/state/lane-health.salt`, mode 0600), so a bad key on one
|
|
218
|
+
account never refuses a healthy account on the same model, and no key value
|
|
219
|
+
is stored or shown. `doctor` and the refusal print only a short `[credential
|
|
220
|
+
xxxxxxxx]` tag, an account label shaped like an email address is masked, and
|
|
221
|
+
a success clears its marker even when the marker store's lock is stuck.
|
|
222
|
+
- A Codex or Claude work run that fails with a transient provider drop
|
|
223
|
+
(`stream_disconnected` or `provider_unavailable`: Codex's websocket closing
|
|
224
|
+
early, or a 5xx or overloaded Claude reply) and saved its native session is
|
|
225
|
+
continued once, automatically, the way `delegate followup` continues one:
|
|
226
|
+
same session, a new run linked to the first by `followupOf`. The
|
|
227
|
+
continuation's envelope and manifest carry `autoResume` (`automatic`,
|
|
228
|
+
`attempt`, `of`, `trigger`), and the final result is the continuation's.
|
|
229
|
+
There is no second automatic attempt, so a drop in the continuation is
|
|
230
|
+
final. It never applies to safe or call mode, `--pass-through`,
|
|
231
|
+
structured-output runs (the workflow supervisor owns that retry), runs that
|
|
232
|
+
saved no session, or runs in a temporary worktree; a worktree run attaches
|
|
233
|
+
to its own worktree and branch. When the continuation cannot be built the
|
|
234
|
+
first run's result is returned unchanged with `autoResume` set to
|
|
235
|
+
`attempted: false` and a `reason`. It is on by default, and
|
|
236
|
+
`providerErrors.autoResume: false` opts out; workflow children are
|
|
237
|
+
unresumable unless the call passes `resumable=True`, so it reaches them only
|
|
238
|
+
then.
|
|
239
|
+
- Workflow `agent()` returns two new typed failure kinds under
|
|
240
|
+
`on_failure="typed"`: `lane_known_bad` (the child refused to launch because
|
|
241
|
+
its lane is marked known-bad; workflows never pass `--force-launch`) and
|
|
242
|
+
`provider_exhausted` (the call was skipped, with no run and a `run_id` of
|
|
243
|
+
`None`, because its stage already stopped launching on the lane).
|
|
244
|
+
`AgentFailure.provider_error` carries the child's `providerError` so a
|
|
245
|
+
script can branch on the signature instead of parsing prose, and
|
|
246
|
+
`capabilities["providerOutcomes"]` advertises the feature. A stage is one
|
|
247
|
+
`phase()` label and a lane is one engine on one model. When the first
|
|
248
|
+
`providerErrors.stageStopAfter` results (default `3`; `0` turns the stop
|
|
249
|
+
off) of a stage on a lane all fail with the same persistent, lane-scoped
|
|
250
|
+
signature, the rest of that stage's calls on that lane are not launched,
|
|
251
|
+
cells already queued behind the agent cap skip too, and a call with an
|
|
252
|
+
engine fallback chain moves on to its next engine. A success or any
|
|
253
|
+
different result among the first results means the lane is not uniformly
|
|
254
|
+
bad, and transient and request-scoped failures never count. The trip is
|
|
255
|
+
journaled once as `stage_lane_stopped` and each skipped call as
|
|
256
|
+
`agent_lane_skipped`; a skipped call still counts against the workflow's
|
|
257
|
+
agent budget.
|
|
258
|
+
- `delegate capabilities refresh` records per-engine auth health. A successful
|
|
259
|
+
refresh runs the read-only probes named in `providerErrors.authProbes` (by
|
|
260
|
+
default `estate-cursor status` for `cursor` and `estate-omp usage` for
|
|
261
|
+
`omp`) and records what they clearly say as `authHealth`, in its JSON and
|
|
262
|
+
text output and in `~/.delegate/state/auth-health.json`: `ok`, `logged_out`
|
|
263
|
+
(Cursor reports it is not signed in), or `limit_reached` (an OMP quota
|
|
264
|
+
window is at 100%). OMP reports quota per provider and account, so its
|
|
265
|
+
reading also carries `lanes` (`<provider>/account <N>` to `ok`,
|
|
266
|
+
`limit_reached`, or `unknown`); the engine reads `limit_reached` only when
|
|
267
|
+
every lane is exhausted, `partial` when some are, and `unknown` for lanes
|
|
268
|
+
the report does not describe. A probe that is missing, slow, exits non-zero
|
|
269
|
+
without a recognisable message, or prints something unexpected records
|
|
270
|
+
`unknown` with a `reason`; `unknown` is never a failure and never refuses a
|
|
271
|
+
launch, and the probe's raw output is not stored. A subset refresh probes
|
|
272
|
+
only the named engines and keeps the others' last reading, and `delegate
|
|
273
|
+
doctor` shows the last reading as `authHealth`. `authProbes` accepts only
|
|
274
|
+
`cursor` and `omp` (each a command and its arguments, or `null` to turn that
|
|
275
|
+
probe off), and `DELEGATE_AUTH_PROBES=off` (or `0`, `false`, `no`) skips
|
|
276
|
+
every probe.
|
|
277
|
+
- A conformance test requires the parser to accept every documented command
|
|
278
|
+
line. It extracts each `delegate` line from `docs/cli-reference.md` and each
|
|
279
|
+
usage and example line in `COMMAND_SPECS`, expands the usage synopses into
|
|
280
|
+
concrete argv lists (minimal, maximal, each alternative alone, and every
|
|
281
|
+
pair of optional groups), and fails on any the parser refuses, so a synopsis
|
|
282
|
+
that shows mutually exclusive options as independent cannot drift again. A
|
|
283
|
+
separate test runs the real launcher on `mail send --to coordinator -` with
|
|
284
|
+
a piped multi-byte UTF-8 stdin and compares the delivered bytes.
|
|
285
|
+
- The stall watchdog stops runs for named reasons besides idle time. A run
|
|
286
|
+
that streams roughly 100k tokens of model output with no tool activity stops
|
|
287
|
+
as `runaway_output`; only model-output payload characters count, and
|
|
288
|
+
text-stream harnesses such as Devin are exempt. The same tool call failing
|
|
289
|
+
the same way five times in a row, with nothing else completing in between,
|
|
290
|
+
stops as `repeated_tool_failure`; each failure counts in exactly one streak,
|
|
291
|
+
and targets compare whole. A repeated identical call no longer resets the
|
|
292
|
+
idle clock and time spent inside tool calls no longer counts as idle, so a
|
|
293
|
+
fast no-op loop is caught while a slow polling loop is left alone. A changed
|
|
294
|
+
`HEAD` in the execution checkout counts as progress. Every idle stall samples
|
|
295
|
+
the child's process group and records `childActivity` (`cpu_active`,
|
|
296
|
+
`waiting`, or `no_processes`) with a process snapshot, and a pending stall
|
|
297
|
+
kill is re-validated after those blocking diagnostics.
|
|
298
|
+
- A Devin child whose output already ends in a Delegate completion report and
|
|
299
|
+
then goes quiet is stopped as finished (`stoppedAfterCompletion`) instead of
|
|
300
|
+
stalled. Only a `Status: completed` report records exit 0; `failed` or
|
|
301
|
+
`blocked` records a failed run, and a child that echoes the prompt's own
|
|
302
|
+
template line (`Status: completed / blocked / failed`) stalls as before.
|
|
303
|
+
- Tracked launches take `--stall-minutes`, which pins the stall threshold over
|
|
304
|
+
config, the silent-harness default, and `DELEGATE_STALL_MINUTES`. The
|
|
305
|
+
manifest records a pinned value as `stallMinutes`, and `resume` and
|
|
306
|
+
`followup` pass it to the continuation (config and environment defaults are
|
|
307
|
+
still re-resolved). Resume refuses an invalid recorded value and followup
|
|
308
|
+
ignores one, as each already treats `timeoutSeconds`. `describe` lists the
|
|
309
|
+
flag with the other tracked-only launch options.
|
|
310
|
+
- With `DELEGATE_CHILD_NO_PAGE_ASK=1` in the launching environment, each
|
|
311
|
+
tracked child finds a stub `ask` first on its `PATH`. The stub prints a
|
|
312
|
+
refusal and exits 2 (the real `ask`'s relay-down code) instead of paging the
|
|
313
|
+
operator's phone and blocking. It is written into the run's own private temp,
|
|
314
|
+
scratch, or run directory; nothing under `$HOME` is written.
|
|
315
|
+
- An `--isolation none` run whose cwd is strictly inside a git work tree
|
|
316
|
+
records the files it changed elsewhere in that repository as
|
|
317
|
+
`outsideCwdChanges` (repository, count, examples) with a warning. The before
|
|
318
|
+
and after snapshots key each path by status, size, and mtime, skip
|
|
319
|
+
Delegate's own registry, and run git with `--no-optional-locks` so they never
|
|
320
|
+
rewrite the user's index. A repo-root cwd, a non-git cwd, and paths outside
|
|
321
|
+
the repository stay undetected, and the warning says so.
|
|
322
|
+
- Safe and dirty-synced workspace copies name, in a run warning, the paths
|
|
323
|
+
they withhold only because `.git/info/exclude` ignores them. The files are
|
|
324
|
+
still not copied, since `info/exclude` is where private scratch usually
|
|
325
|
+
lives.
|
|
326
|
+
- An omp prompt that names image paths warns that omp will not attach them
|
|
327
|
+
(omp attaches files only from `@path` argv, and Delegate sends the prompt on
|
|
328
|
+
stdin) and points at omp's read tool. The scan runs in linear time.
|
|
329
|
+
- `runs --summary` and `ps --summary` print counts by status, harness, and
|
|
330
|
+
group for every matching run, without rows or manifest loads. Each run is
|
|
331
|
+
counted once across registry roots, the listing's empty-scope and
|
|
332
|
+
status-filter warnings apply, and `--limit` and `--structural` are refused.
|
|
333
|
+
- `wait --json --structural` reduces each run to identity, terminal status,
|
|
334
|
+
result quality, and failure fields, and moves the handle-resolution warnings
|
|
335
|
+
(`bare_handle_stale`, `bare_handle_ambiguous`, `run_target_stale`) to the top
|
|
336
|
+
level. `ok` and the exit code still come from the full view; text output
|
|
337
|
+
ignores the flag.
|
|
338
|
+
- `worktree show` and `worktree remove` accept the worktree path as the
|
|
339
|
+
handle, matched canonically against the records' `executionCwd`; a relative
|
|
340
|
+
path resolves against the `--cwd` workspace. An unknown path fails with
|
|
341
|
+
`unknown_worktree_path` and, like an unknown handle, points at
|
|
342
|
+
`worktree reap --path`. `not_worktree_run` names the run that owns the
|
|
343
|
+
worktree and puts `worktree show` on it first in `nextActions`.
|
|
344
|
+
- A bare harness handle warns `bare_handle_ambiguous`, naming up to five other
|
|
345
|
+
runs, when other runs of that harness share its group or are still running.
|
|
346
|
+
- A cursor model that is a bare family word such as `grok`, and is neither a
|
|
347
|
+
configured `cursor.models` alias nor a catalog selector, resolves to the
|
|
348
|
+
family's newest selector (highest version, non-fast, balanced tier) from the
|
|
349
|
+
discovered catalog, or from the bundled table when discovery has none, with
|
|
350
|
+
a warning. Configured defaults and routes resolve the same way before effort
|
|
351
|
+
routing.
|
|
352
|
+
- Launches warn when the resolved model id is absent from the discovered
|
|
353
|
+
catalog, or from the bundled table when discovery is empty; bundled-table
|
|
354
|
+
warnings fire only for a selector that resembles a listed id. Cursor now
|
|
355
|
+
uses this shared check, so a misspelled id warns without a discovery
|
|
356
|
+
snapshot. A Claude `[1m]` suffix compares by its base, and Claude family
|
|
357
|
+
aliases are exempt. The launch is never refused and the selector never
|
|
358
|
+
rewritten.
|
|
359
|
+
- The `--forbid-commit` implied-worktree note and a missing `codex.profile`
|
|
360
|
+
overlay print to stderr when a run launches, not only in the finalization
|
|
361
|
+
report or `doctor`.
|
|
362
|
+
- A failed safe-mode run whose child reports resolver or reachability errors
|
|
363
|
+
(`getaddrinfo`, `ENOTFOUND`, `EAI_AGAIN`, and similar) warns that safe mode
|
|
364
|
+
may block network access and that research belongs in work mode. Only a
|
|
365
|
+
generic nonzero exit gets the hint; status and failure kind are unchanged.
|
|
366
|
+
- `followup` on a run that a later resume or followup already continued prints
|
|
367
|
+
a note naming that run and the command that continues from it, unless the
|
|
368
|
+
later run shares the source's native session.
|
|
369
|
+
- `describe` and `models --json` report `binary` for every engine, droid
|
|
370
|
+
included. The `models` text view omits a retired droid row that has no
|
|
371
|
+
configured or discovered models and no configured default.
|
|
372
|
+
- Workspace spec for work lanes in a persistent worktree: `--base REF` cuts the
|
|
373
|
+
worktree from REF, `--env NAME=VALUE`/`--env-file PATH` set child variables
|
|
374
|
+
that are recorded privately and re-applied on resume and followup, and
|
|
375
|
+
`--setup CMD` runs in the fresh worktree before the child. A failed setup is
|
|
376
|
+
a typed `workspace_setup_failed` error (`failureKind: workspace_setup`) and no
|
|
377
|
+
child launches. `run --input-json` takes `base`, `env`, and `setup`, and
|
|
378
|
+
workflow `agent()` takes `base=`, `env=`, and `setup=` (capability
|
|
379
|
+
`workspaceSpec`). `workflow run --env/--env-file` records a launch env that
|
|
380
|
+
every resume re-applies.
|
|
381
|
+
- A worktree whose run is not terminal while its launching Delegate process is
|
|
382
|
+
alive is leased: prune, auto-prune, reap, and remove skip it with
|
|
383
|
+
`worktree_leased` even when the child pid is dead.
|
|
384
|
+
- `delegate runs` in a repository's main worktree also lists runs registered in
|
|
385
|
+
its linked worktrees, tagged with `registryWorkspace`.
|
|
386
|
+
- Workflow steps can carry a stable caller-supplied identity. `agent(..., key="...")`
|
|
387
|
+
replays by that key inside the enclosing named scope instead of by position and
|
|
388
|
+
prompt text, so a resumed script that skips settled work or embeds new git heads
|
|
389
|
+
in its prompts still hits its cache. A changed prompt under the same key adopts
|
|
390
|
+
the recorded result and journals `key_prompt_mismatch`. `parallel`, `pipeline`,
|
|
391
|
+
and `workflow` accept `key=` to name their scope. Reusing a key within one run
|
|
392
|
+
raises `WorkflowKeyConflict`. Unkeyed calls keep their positional keys, so
|
|
393
|
+
existing journals replay unchanged.
|
|
394
|
+
- Gate actions: `park_gate(key, result, actions=[...])` pauses for an operator
|
|
395
|
+
decision, and `workflow approve <wfId> --gate KEY --action NAME [--note TEXT]
|
|
396
|
+
[--data JSON]` returns that choice to the script. Undeclared actions are
|
|
397
|
+
refused with the allowed list. A bare `approve` behaves as before. A recorded
|
|
398
|
+
action the resumed script no longer declares is not returned: the gate parks
|
|
399
|
+
again (`gate_action_undeclared`) and asks under the current actions. A second
|
|
400
|
+
live `park_gate()` with the same key in one named scope raises
|
|
401
|
+
`WorkflowKeyConflict`.
|
|
402
|
+
- The workflow `capabilities` global adds `agentKey`, `scopeKey`, and
|
|
403
|
+
`gateActions`.
|
|
404
|
+
- Tracked runs persist the harness-reported token `usage` on the run record, so
|
|
405
|
+
`snapshot`, `run-output`, and `runs --json` expose it without re-reading the
|
|
406
|
+
child's stream (previously it reached only call mode's launch response).
|
|
407
|
+
- A fungible or panel run whose harness reports a served model other than the
|
|
408
|
+
resolved one now carries a `model_substitution` warning naming requested,
|
|
409
|
+
resolved, and served models. Pinned runs already pause on a switch; the
|
|
410
|
+
warning closes the silent case for the default continuity mode.
|
|
411
|
+
- `delegate doctor` warns when a configured `<engine>.defaultModel` (resolved
|
|
412
|
+
through `<engine>.models` aliases first) is absent from the selected profile's
|
|
413
|
+
discovery catalog, naming the nearest discovered selectors. It checks the
|
|
414
|
+
unique targets of configured `<engine>.models` aliases the same way, naming the
|
|
415
|
+
alias key, since a selector an operator wrote down is worth knowing about even
|
|
416
|
+
when no current launch selects it. A cursor launch
|
|
417
|
+
adds the same warning for its selected selector, because cursor-agent resolves
|
|
418
|
+
an unknown selector inside the child. All of them are warnings: an empty or
|
|
419
|
+
missing catalog stays silent, and no configured value is rewritten or refused.
|
|
420
|
+
- `workflow run` warns when it detects it is running inside a systemd unit whose
|
|
421
|
+
`KillMode` will reap the detached supervisor (`control-group`/`mixed`), naming
|
|
422
|
+
the unit and the required `-p KillMode=process`. Previously the unit reported
|
|
423
|
+
success while the workflow was already dead.
|
|
424
|
+
|
|
425
|
+
### Fixed
|
|
426
|
+
- Missing local profile directories are silently omitted from write-guard
|
|
427
|
+
launcher reopens. Failover state uses `~/.delegate/failover` when the optional
|
|
428
|
+
profile tree is absent, and missing personal profile homes do not read legacy
|
|
429
|
+
cooldown files. Existing profile trees keep their current behavior. The
|
|
430
|
+
development-only `delegate-audit` shim names its optional checkout when absent.
|
|
431
|
+
- `worktree reap` removes linked orphans on Linux again. Its process scan
|
|
432
|
+
refused every removal as `process_scan_unavailable` because Linux hides the
|
|
433
|
+
working directory of the user's own non-dumpable processes (`systemd
|
|
434
|
+
--user`, `ssh-agent`, `sshd-session`), which every login session has. A
|
|
435
|
+
hidden process whose real and effective user and group ids match the
|
|
436
|
+
caller's is now noted in a warning and does not block (the saved ids may
|
|
437
|
+
differ, as for set-group-id `ssh-agent` on Debian); one with other ids (a
|
|
438
|
+
`newgrp` shell) or an unreadable status still refuses. Shells,
|
|
439
|
+
agents, and bubblewrap-sandboxed lanes stay readable and still block
|
|
440
|
+
removal. The test helper that runs fake children also writes the script to
|
|
441
|
+
a file, since Linux caps one argument at 128 KiB.
|
|
442
|
+
- A stale Run's `nextActions` (in `runs` and `snapshot`) now offers `delegate
|
|
443
|
+
resume <handle>` (not for `call` Runs, which cannot resume). The CLI
|
|
444
|
+
reference and troubleshooting guide say what killing a launch command does:
|
|
445
|
+
the launch is the Run's supervisor, the child keeps running on its own until
|
|
446
|
+
it next writes output and then exits, the Run reads `stale`, and `cancel`,
|
|
447
|
+
`resume`, or `followup` recover it. Launch each lane from its own background
|
|
448
|
+
task.
|
|
449
|
+
- The `runs` and `worktree list` text tables head their time-since-activity
|
|
450
|
+
column `idle`; it was labelled `age`, which agents read as run age. JSON is
|
|
451
|
+
unchanged.
|
|
452
|
+
- Parallel Runs launched at the same moment (workflow `parallel()` children,
|
|
453
|
+
a fan-out) no longer fail at random with `unsafe_scratch_directory` ("File
|
|
454
|
+
exists: 'run-scratch'"). Two launches racing to create the shared scratch
|
|
455
|
+
root now both use it; the symlink, non-directory, and foreign-owner checks
|
|
456
|
+
still run on the directory either way.
|
|
457
|
+
- The Claude model-typo refusal is skipped when `CLAUDE_CODE_USE_BEDROCK`,
|
|
458
|
+
`CLAUDE_CODE_USE_VERTEX`, `CLAUDE_CODE_USE_FOUNDRY`, or `ANTHROPIC_BASE_URL`
|
|
459
|
+
comes from `--env` or `--env-file`, not only from the launching
|
|
460
|
+
environment. The env files are read once per launch, so the check and the
|
|
461
|
+
child see the same values.
|
|
462
|
+
- A Run's `current` no longer keeps showing a provider error after the
|
|
463
|
+
harness has moved on. Pi and OMP show `retrying after provider error
|
|
464
|
+
(429)` while they retry and then the new activity; a Codex `turn.started`
|
|
465
|
+
or OpenCode `step_start` after an error replaces it the same way.
|
|
466
|
+
- `--notify` channel pings work against post 0.9.0, which removed `--anyway`
|
|
467
|
+
from its help. The notifier sends without it and retries once with
|
|
468
|
+
`--anyway` only when post refuses the send as `crossed_send`.
|
|
469
|
+
- The setup-command output tail recorded for a worktree Run can no longer
|
|
470
|
+
reconstruct a secret passed through `--env`/`--env-file`: recorded values
|
|
471
|
+
and their fragments are masked, generic redaction runs between masking
|
|
472
|
+
passes, and the whole pass repeats until nothing changes (a tail that never
|
|
473
|
+
settles is replaced by the mask).
|
|
474
|
+
- The Cursor model-family warning names the selector the operator typed and
|
|
475
|
+
the routed selector actually passed to Cursor.
|
|
476
|
+
- Every harness caps its map of tool calls still waiting on a result at 256
|
|
477
|
+
entries (Claude, Kimi, Grok, and Cursor kept them all), and a completion
|
|
478
|
+
report status line may end in a semicolon (`Status: completed;`).
|
|
479
|
+
- `worktree remove` of a parent no longer orphans worktrees of Runs launched
|
|
480
|
+
inside it: clean, merged nested worktrees are removed first, and dirty or
|
|
481
|
+
unmerged ones refuse with `nested_worktrees_block_remove` and a per-nested
|
|
482
|
+
command (`--force` does not reach them). Reap refuses a pool path holding
|
|
483
|
+
nested worktrees (`nested_worktrees_present`), a nested Registry that
|
|
484
|
+
appears mid-removal refuses with `nested_registry_appeared`, and an
|
|
485
|
+
unreadable nested Registry refuses even with `--kill-live`.
|
|
486
|
+
- A Codex workflow stage whose schema has an object with no declared
|
|
487
|
+
properties (including through a local `$ref`) no longer fails every
|
|
488
|
+
attempt: it uses the prompt path instead of strict mode, which closed the
|
|
489
|
+
object to zero keys. A direct `codex --output-schema` with such a schema is
|
|
490
|
+
refused at preflight with `invalid_output_schema` naming the path.
|
|
491
|
+
- A dry-run `workflow resume` no longer cancels or reaps a stale scope child,
|
|
492
|
+
changes status, persists a budget, or records an approval; it only
|
|
493
|
+
journals. `workflow approve` on a failed workflow points at `workflow
|
|
494
|
+
resume`, and on a running one says the gate is not open yet.
|
|
495
|
+
- A relative `--prompt-file` falls back to `--cwd`, and the not-found error
|
|
496
|
+
names both paths tried.
|
|
497
|
+
- The unread-mail scan cannot block on a FIFO named like mail; the shared
|
|
498
|
+
bounded record reader opens non-blocking.
|
|
499
|
+
- Small surface fixes: the Registry writes `.delegate/.gitignore`;
|
|
500
|
+
`--include-dirty` is a warned no-op in safe mode and reports `includeDirty:
|
|
501
|
+
false`; `runs show` names the real readers; a timed-out `wait --json` warns
|
|
502
|
+
and names `--structural`; `no_matching_worktrees` names the Registry it
|
|
503
|
+
searched.
|
|
504
|
+
- A `--mail-push` Codex run no longer dies before the model starts. Delegate
|
|
505
|
+
injected `-c hooks=true`, which Codex 0.157 rejects at config load because
|
|
506
|
+
`hooks` is a table; it now passes `--enable hooks` (the same as `-c
|
|
507
|
+
features.hooks=true`) and does not add it twice when it is already present.
|
|
508
|
+
- `--notify` room pings work against post 0.9.0. The notifier no longer passes
|
|
509
|
+
`--allow-self`, which post now rejects as an unexpected argument, and the
|
|
510
|
+
message-id match accepts post's room ids (which have no microsecond field)
|
|
511
|
+
as well as channel ids, so `messageId` is no longer empty for a room ping. A
|
|
512
|
+
room ping is a workspace fan-out that never reaches its own sender, so
|
|
513
|
+
`--notify` naming your own room reaches the room's other participants and
|
|
514
|
+
not the launching session; the troubleshooting guide now says so.
|
|
515
|
+
- On a Python older than 3.11 (Xcode's 3.9, for example), `bin/delegate.py`
|
|
516
|
+
and the `delegate_agent` package now exit `2` with a message naming the
|
|
517
|
+
interpreter version and executable they found, instead of a raw
|
|
518
|
+
`ImportError` from inside the package. The tracked
|
|
519
|
+
`bin/delegate-profile-shim` picks a 3.11+ interpreter itself (`python3`,
|
|
520
|
+
then `python3.14` down to `python3.11`, then Homebrew's, then
|
|
521
|
+
`/usr/local/bin/python3`), honors a validated `DELEGATE_PYTHON` (an override
|
|
522
|
+
older than 3.11 is refused, not bypassed), and exits `2` with a plain
|
|
523
|
+
message when none is found.
|
|
524
|
+
- The Codex and `post` argv Delegate builds is now tested against the
|
|
525
|
+
installed binaries (`tests/test_real_binary_contracts.py`), which is how the
|
|
526
|
+
two dead flags above shipped. A `codex` or `post` on `PATH` that fails
|
|
527
|
+
`--version`, times out, or cannot register the throwaway room now fails the
|
|
528
|
+
suite with its command, exit code, and output instead of skipping it; a
|
|
529
|
+
class skips only when its binary is absent or
|
|
530
|
+
`DELEGATE_SKIP_REAL_BINARY_CONTRACTS` is truthy, and says why.
|
|
531
|
+
`pytest-xdist` workers now see the real HOME through
|
|
532
|
+
`DELEGATE_TESTS_ORIGINAL_HOME`.
|
|
533
|
+
- A run that fails keeps what the child had produced. A failed, timed-out, or
|
|
534
|
+
(opt-in) capped run's completion report now quotes the child's last
|
|
535
|
+
substantive assistant text, bounded and redacted, under "Partial output
|
|
536
|
+
recovered before the run stopped. This is not a completion report", as a
|
|
537
|
+
cancelled run's already did. Pi, OMP, and OpenCode keep that text across
|
|
538
|
+
turns and steps, so a long review followed by a tool turn and a provider
|
|
539
|
+
error is still recoverable (OpenCode used to clear it at every step). A
|
|
540
|
+
call-mode timeout or output overflow, including a child that had already
|
|
541
|
+
exited before Delegate noticed the overflow, returns the buffered draft as
|
|
542
|
+
`diagnostics` on the error (also flattened into the JSON error):
|
|
543
|
+
`partialText` (redacted and bounded), `stdoutTail`, and `stderrTail`; text
|
|
544
|
+
mode prints the partial text to stderr.
|
|
545
|
+
- `followup` errors now say what is true. A source with no recorded native
|
|
546
|
+
session (launched with `--no-resumable`, before Codex and Claude work runs
|
|
547
|
+
saved their session by default, or as a workflow `agent()` call without
|
|
548
|
+
`resumable=True`) is refused with `session-missing` and pointed at `delegate
|
|
549
|
+
resume <handle>`, no longer told to relaunch with `--resumable`. A session
|
|
550
|
+
that was saved but cannot be found when resumed fails as `session_expired`
|
|
551
|
+
(`failureKind: session_lost`); the message names the usual cause, a launch
|
|
552
|
+
under a different account than the one holding the session, and offers
|
|
553
|
+
`delegate resume`.
|
|
554
|
+
- `resume` and `followup` refuse a known option of that command that appears
|
|
555
|
+
in the prompt tail (`delegate followup x fix it --dry-run`) with
|
|
556
|
+
`option_after_handle` before anything launches, instead of sending it to the
|
|
557
|
+
child as prompt text while the run launches with the defaults. Put `--`
|
|
558
|
+
before the prompt to send such a token as literal text. `followup` accepts
|
|
559
|
+
options on either side of the handle but only before the prompt text, and
|
|
560
|
+
`resume` only before the handle; an option-shaped token that belongs to
|
|
561
|
+
another command keeps the existing warning.
|
|
562
|
+
- A workflow structured retry on Codex or Claude whose resumed session cannot
|
|
563
|
+
be found (`session_lost`, reason `session_expired`) now falls back to a
|
|
564
|
+
fresh relaunch in the same worktree instead of ending, without spending a
|
|
565
|
+
retry; the journal records `agent_structured_retry` with `strategy:
|
|
566
|
+
"relaunch"` and `fellBackFrom: "resume"`. A fresh child redoes the task, so
|
|
567
|
+
the fallback relaunches only over a worktree known to be untouched, and
|
|
568
|
+
otherwise the call is refused (`agent_structured_retry_refused`) as
|
|
569
|
+
`work_changed_session_missing` when an earlier attempt already changed the
|
|
570
|
+
worktree, or `work_state_unverified` when that could not be checked. The
|
|
571
|
+
work summary now records `fileInspectionStatus` next to
|
|
572
|
+
`commitInspectionStatus` (each `verified` or `unverified`), because a failed
|
|
573
|
+
`git status` used to read as zero changed files; `noChanges` is true only
|
|
574
|
+
when both are `verified` and show nothing. In work mode both the prior
|
|
575
|
+
attempt and the resumed attempt must carry a verified summary that reports
|
|
576
|
+
no changes, and a missing summary, or one from a Delegate that predates
|
|
577
|
+
`fileInspectionStatus`, counts as unverified. Safe and call children have no
|
|
578
|
+
summary and keep the fallback.
|
|
579
|
+
- `worktree reap --path P --older-than N --yes --force` now removes a pool
|
|
580
|
+
entry that has no run record but that Git still links; `reap` used to skip
|
|
581
|
+
it as `live_backlink` even with `--force`, and the only way out was `git
|
|
582
|
+
worktree remove --force` by hand. Every check is re-run under the locks
|
|
583
|
+
immediately before removal. The pool scan must have no warning for the entry
|
|
584
|
+
(an entry with broken-looking metadata changed within the last 15 minutes,
|
|
585
|
+
or whose metadata cannot be read, is refused); Git must still list the path
|
|
586
|
+
as a worktree of its source repository; no Registry may hold a record for it
|
|
587
|
+
(`record_owns_path`, including a run that claimed it after planning, or
|
|
588
|
+
`record_in_other_registry` with the `--cwd` to run from); there must be no
|
|
589
|
+
uncommitted work by the definition above (`dirty`, `dirty_unknown`) unless
|
|
590
|
+
`--discard-uncommitted` is passed; no process may have its current directory
|
|
591
|
+
inside the path (`process_cwd_inside`, from one pass over `/proc` or `lsof
|
|
592
|
+
-d cwd`, never `lsof +D`) unless `--kill-live` is passed; and changed ledger
|
|
593
|
+
files are saved first. A process scan that cannot run, or cannot read one of
|
|
594
|
+
your own processes, refuses as `process_scan_unavailable`; other users'
|
|
595
|
+
processes are noted in a warning and do not block. The branch is kept, and
|
|
596
|
+
without `--force` the entry is still skipped as `live_backlink`, now with a
|
|
597
|
+
hint naming `--force`.
|
|
598
|
+
- Persistent worktrees no longer replace committed symlinks with placeholders.
|
|
599
|
+
The replacement showed up as typechanges that the next `git add -A` would
|
|
600
|
+
commit. A symlink committed to the repository is left as Git checked it out,
|
|
601
|
+
absolute or not; only an untracked symlink that points outside the source,
|
|
602
|
+
or at gitignored content, is replaced by a placeholder as it is mirrored in,
|
|
603
|
+
and the launch warning names it. Safe mode is unchanged and still blocks
|
|
604
|
+
every external symlink in its throwaway copy.
|
|
605
|
+
- `worktree show` on a followup, resume, or attached run names the run that
|
|
606
|
+
owns the worktree, following the lineage up through followups and resumes
|
|
607
|
+
(up to 16 hops, with a cycle guard) and putting `delegate worktree show
|
|
608
|
+
<owner>` first in `nextActions`.
|
|
609
|
+
- Two false or misleading model warnings are gone. Cursor labels are compared
|
|
610
|
+
after the context-window token (`256K`, `1M`) is dropped, so
|
|
611
|
+
`grok-4.7-xhigh` served as `Grok 4.7 256K Extra High` is the requested model
|
|
612
|
+
and not a `model_substitution`. A pinned call whose stream named a served
|
|
613
|
+
model is no longer told it "could not be verified"; that warning now fires
|
|
614
|
+
only when the stream was silent.
|
|
615
|
+
- Usage synopses in `--help`, `describe`, and `docs/cli-reference.md` now
|
|
616
|
+
match what the parser accepts; the parser was right in each case. `claude
|
|
617
|
+
call` takes `[--read-only|--pure]`, not both. `runs` and `ps` take
|
|
618
|
+
`[--summary | [--limit N] [--structural]]`, because `--summary` prints
|
|
619
|
+
counts and no rows and is refused with either. `snapshot` and `run-output`
|
|
620
|
+
take `(<handle>|--latest HARNESS)`. `run-output` takes `[(--stdout|--stderr)
|
|
621
|
+
[--raw | [--tail N] [--max-chars N]]]`, with a second usage line,
|
|
622
|
+
`[--no-redact] (--raw | --tail N)`, for the standalone `--raw` and `--tail
|
|
623
|
+
N` forms. `worktree remove` takes `[--keep-branch | [--force-branch]
|
|
624
|
+
[--force]]`.
|
|
625
|
+
- `describe --summary` is now described as the command index plus config
|
|
626
|
+
resolution, launch options, profiles, and workflows, larger than the default
|
|
627
|
+
and `--overview` views and smaller than `--full`, not as compact.
|
|
628
|
+
- The workflow supervisor relays `SIGINT` to its children the way it does
|
|
629
|
+
`SIGTERM` and `SIGHUP`, and journals `supervisor_signalled`. `SIGINT` used
|
|
630
|
+
to reach the interpreter's default handler, which raised `KeyboardInterrupt`
|
|
631
|
+
and marked the workflow failed without cancelling its children; they ran on
|
|
632
|
+
unowned until a later resume sealed them.
|
|
633
|
+
- The `agent_timeout` journal row written when a `followup()` call times out
|
|
634
|
+
now carries `key`, `label`, and `scope`, as the `agent()` timeout row does,
|
|
635
|
+
so a journal with several follow-ups says which step died.
|
|
636
|
+
- The workflow docs no longer say a resume adopts running children. A resume
|
|
637
|
+
cancels every child the previous attempt still had running and relaunches
|
|
638
|
+
that work; adoption covers only children that already finished (or that this
|
|
639
|
+
supervisor lifetime is itself still launching). Waiting on a live orphan and
|
|
640
|
+
taking its result is deferred work, not current behavior.
|
|
641
|
+
- `describe --full` documents the workflow DSL the runtime actually injects.
|
|
642
|
+
Its `globals` list is read from the runtime's new `WORKFLOW_DSL_GLOBALS`,
|
|
643
|
+
which the injector also checks, so it now names `park_gate`, `reject`,
|
|
644
|
+
`soft_park` and its helpers, `structured_attempt`, and `dry_run` (aliases in
|
|
645
|
+
`globalAliases`). `capabilities` is derived from the runtime's capability
|
|
646
|
+
table, adding the `agentKey`, `scopeKey`, `gateActions`, and `workspaceSpec`
|
|
647
|
+
entries the old literal lacked, and is also given structured as
|
|
648
|
+
`capabilityVersions`. The `agent()` signature is rendered from the runtime
|
|
649
|
+
method, so it gains `resumable`, `on_failure`, `key`, `base`, `env`, and
|
|
650
|
+
`setup`; the `pipeline`, `parallel`, and `workflow` entries document `key=`,
|
|
651
|
+
and `judges` documents per-entry dicts.
|
|
652
|
+
- A stdout or stderr line handler that raises (a registry lock timeout while
|
|
653
|
+
persisting progress, a parser bug) no longer kills the drain thread, which
|
|
654
|
+
stopped consuming the child's pipe and could strand the child. The drain
|
|
655
|
+
keeps reading to EOF, logs a `stream.handler_failed` event, and surfaces a
|
|
656
|
+
run warning that retried attempts carry forward.
|
|
657
|
+
- `cancel` no longer fails with a raw `TimeoutError` while another process
|
|
658
|
+
holds the registry lock. When the lock wait runs out it reads the record
|
|
659
|
+
unlocked, re-reads it with any pending finalize WAL overlaid just before
|
|
660
|
+
signalling, and signals only a launched child generation whose start identity
|
|
661
|
+
matches the run; a terminal result refuses as `run_already_terminal` and a
|
|
662
|
+
changed generation as `cancel_target_changed`. It then makes one bounded
|
|
663
|
+
attempt to record the cancelled outcome, reporting `registryLockBypassed`
|
|
664
|
+
with an unrecorded warning if the lock is still held, and refuses with
|
|
665
|
+
`registry_lock_busy` for an unlaunched record, a setup group, or a stale
|
|
666
|
+
seal.
|
|
667
|
+
- A bwrap safe child in a uv project without a `.venv` gets
|
|
668
|
+
`UV_PROJECT_ENVIRONMENT` pointed inside the run's own writable temp
|
|
669
|
+
directory, so `uv run` no longer fails creating `.venv` under the read-only
|
|
670
|
+
workspace bind. An existing `.venv` or an explicit `UV_PROJECT_ENVIRONMENT`
|
|
671
|
+
is left alone.
|
|
672
|
+
- Resume onto codex or claude keeps a source run's explicit `resumable`
|
|
673
|
+
opt-in, so `followup` on the resumed alias works (any other source now takes
|
|
674
|
+
the resumable-by-default launch setting); a cross-engine resume onto an
|
|
675
|
+
engine that cannot keep it drops the opt-in and says so.
|
|
676
|
+
- Resume and followup that reuse the source run's auth profile now say the
|
|
677
|
+
profile was inherited from the source run instead of warning about an
|
|
678
|
+
`--auth-profile` flag the user never typed.
|
|
679
|
+
- The suite resolves the production compact-temp root the way runs do. On
|
|
680
|
+
macOS it named `/var/tmp` where runs record `/private/var/tmp`, so six tests
|
|
681
|
+
failed and every subprocess run's temp directory leaked.
|
|
682
|
+
- A workflow no longer cancels its own live children as stale. Two `agent()`
|
|
683
|
+
calls from plain script threads could share one positional scope, and the
|
|
684
|
+
second call cancelled the first's running child. Stale-scope cancellation now
|
|
685
|
+
targets only children an earlier supervisor lifetime started (`agent_started`
|
|
686
|
+
records the lifetime's `incarnation`).
|
|
687
|
+
- Workflow adoption relaunches instead of waiting on or failing over a key's
|
|
688
|
+
latest child run when that run is cancelled, stale, or never launched
|
|
689
|
+
(`agent_adopt_skipped`).
|
|
690
|
+
- Resume seals a workflow child that never published a pid (stuck at `running`
|
|
691
|
+
or `creating_isolation`) once it has been idle for 300 seconds and its
|
|
692
|
+
recorded launcher (`launcherPid`) is dead or its pid was reused, instead of
|
|
693
|
+
refusing with `workflow_children_unsealed` forever. The runner re-reads the
|
|
694
|
+
record under the registry lock before every process start, the first included,
|
|
695
|
+
and publishes the child's pid only over a record that is neither terminal nor
|
|
696
|
+
cancel-requested, so a sealed launch never gains a child.
|
|
697
|
+
- `workflow run --dry-run` executes the script with its working directory set to
|
|
698
|
+
the `--cwd` workspace, as a real run's supervisor does. A dry run that times
|
|
699
|
+
out leaves the workspace as the working directory, because its abandoned
|
|
700
|
+
script thread may still be resolving relative paths.
|
|
701
|
+
- Persistent-worktree change accounting lists untracked files individually
|
|
702
|
+
(`git status --untracked-files=all`), matching the per-file launch-seeded
|
|
703
|
+
digest map. A seeded untracked directory used to be reported as one collapsed
|
|
704
|
+
entry the seeded filter could never match, so a quiet `work` run claimed a
|
|
705
|
+
change the child never made and reported success, a structured child with
|
|
706
|
+
unparseable output was refused as changed instead of relaunched, and the
|
|
707
|
+
worktree was retained as dirty. `workSummary.changedFiles` and
|
|
708
|
+
`rawChangedFilesCount` count files rather than directories for untracked
|
|
709
|
+
trees.
|
|
710
|
+
- A bare `429`/`rate limit` diagnostic classifies as `usage_limit` only when
|
|
711
|
+
account-context wording (`quota`, `usage`, `billing`, `subscription`,
|
|
712
|
+
`account`, `credit`) appears on the same line within 80 characters of it, in
|
|
713
|
+
either order. An unrelated `memory usage: 82%` line or a wrapped command's
|
|
714
|
+
`usage:` help text no longer pairs with a transient 429, which had marked the
|
|
715
|
+
run a quota failure, suppressed the structured correction retry, and pointed
|
|
716
|
+
operators at credential rotation.
|
|
717
|
+
- Codex work runs recover the last substantive agent message as the completion
|
|
718
|
+
report when the stream never sealed `turn.completed`, instead of classifying a
|
|
719
|
+
complete report as `resultQuality=empty`. A progress preamble is still not
|
|
720
|
+
adopted.
|
|
721
|
+
- `run-output` no longer claims no recoverable final message exists while the
|
|
722
|
+
child wrote stdout; the diagnostic states the byte count and names the command
|
|
723
|
+
that reads it.
|
|
724
|
+
- Workflow child launches that fail before publishing a run record record the
|
|
725
|
+
exit code and a bounded, redacted stderr tail on the attempt outcome and on a
|
|
726
|
+
new `child_stdout_unparsed` event (previously only the JSON decode error of
|
|
727
|
+
empty stdout was kept). `agent_structured_retry` carries the tail too. This
|
|
728
|
+
covers a child that exits `0` after writing empty or non-JSON stdout, on both
|
|
729
|
+
the initial `agent()` launch and `followup`.
|
|
730
|
+
- An empty `runs`/`ps` listing now adds the workspace-scope warning without
|
|
731
|
+
requiring `--group`/`--harness`, so a bare `runs --running` in a directory
|
|
732
|
+
with no Registry no longer prints an empty table that reads as dead lanes.
|
|
733
|
+
`wait --group` on a group stored in another workspace reports the same hint
|
|
734
|
+
and names the searched Registry.
|
|
735
|
+
- `workflow_not_found` errors for `status`, `run --resume`, and id-based verbs
|
|
736
|
+
name the searched workflow root and the `--cwd` form, so "stored elsewhere"
|
|
737
|
+
is distinguishable from "gone". The workflow profile-drift refusal names the
|
|
738
|
+
differing identity keys with their pinned and current values.
|
|
739
|
+
- `models <engine> --summary` points at `delegate help models`, and the focused
|
|
740
|
+
help states that `--summary` takes no engine argument.
|
|
741
|
+
- The linked-worktree registry-lock guard verifies suite ancestry before
|
|
742
|
+
reporting a violation, so a concurrent unrelated Delegate launcher holding the
|
|
743
|
+
same file is no longer reported as a suite escape.
|
|
744
|
+
- Test bootstrap pins `MISE_DATA_DIR`/`MISE_CACHE_DIR` suite-wide, so a
|
|
745
|
+
subprocess-bearing test no longer makes the mise python shim download an
|
|
746
|
+
interpreter into a temporary HOME and pollute stderr.
|
|
747
|
+
- The suite removes the compact child temp directory each tracked run manifest
|
|
748
|
+
records before it tears that temporary registry down, and asserts no test
|
|
749
|
+
leaves a net directory under the production owner root. Previously any test
|
|
750
|
+
that launched a real Delegate subprocess created `/var/tmp/dlg-<uid>/<token>`
|
|
751
|
+
that only `runs prune` could ever remove, and the temporary registry those
|
|
752
|
+
paths were derived from was deleted first. The run's own recorded child is
|
|
753
|
+
settled first: a live child whose command line carries one of the paths its
|
|
754
|
+
manifest names gets a bounded grace to reach its own exit (so a fixture child
|
|
755
|
+
writing through `$TMPDIR` finishes), and only then, or after being reaped, is
|
|
756
|
+
the exact recorded directory removed.
|
|
757
|
+
- `run-output`, `runs`/`ps`, and `snapshot` reconstruct a persisted run's
|
|
758
|
+
`usage` from one allowlisted normalized shape instead of copying the stored
|
|
759
|
+
object, so a record rewritten after the run (a non-isolated child shares the
|
|
760
|
+
workspace) cannot inject unknown keys, an unknown basis, or credential-shaped
|
|
761
|
+
text into a payload or the `usage:` line. A record whose basis is not one a
|
|
762
|
+
producer emits contributes no usage at all.
|
|
763
|
+
- The served-model comparison behind the `model_substitution` warning uses the
|
|
764
|
+
same harness-aware identity rules as the pinned check: OMP's provider-qualified
|
|
765
|
+
`provider/model` observation, a Cursor display name, and a Claude family alias
|
|
766
|
+
are the requested model, not substitutions. A clean OMP lane is no longer
|
|
767
|
+
labelled as substituted.
|
|
768
|
+
- Model warnings redact their model values: `model_substitution`,
|
|
769
|
+
`pinned_continuity_unverified`, and the configured-model/alias catalog warnings
|
|
770
|
+
can carry an operator- or provider-supplied selector, and all of them reach
|
|
771
|
+
`state.json`, `delegate.doctor.v1`, or the completion report. A
|
|
772
|
+
credential-shaped selector is masked instead of persisted.
|
|
773
|
+
- Workflow child launches that fail before publishing JSON carry the same
|
|
774
|
+
evidence on the *initial* `agent()` attempt as on followups: the outcome
|
|
775
|
+
records the exit code and a bounded, redacted stderr tail, and stdout that
|
|
776
|
+
never parsed emits `child_stdout_unparsed`. An exit-zero child whose envelope
|
|
777
|
+
is unusable is now reported as `invalid_envelope` rather than `nonzero_exit`.
|
|
778
|
+
- `workflow run` detects the systemd unit from the cgroup component that owns
|
|
779
|
+
the current process instead of any ancestor `.service` in the path, so an
|
|
780
|
+
ordinary interactive session under `user@<uid>.service` no longer receives the
|
|
781
|
+
severe `KillMode` warning. A unit under the per-user manager is queried through
|
|
782
|
+
`systemctl --user` rather than read as a system-manager lookup failure.
|
|
783
|
+
- The track's child `TMPDIR`/`TMP`/`TEMP` now points at a separate per-run
|
|
784
|
+
short-path directory (`/var/tmp/dlg-<uid>/<token>`, recorded as
|
|
785
|
+
`manifest.tempPath`) instead of the run scratch. The scratch path carries the
|
|
786
|
+
registry hash and run id, so a child binding a Unix socket under it overran
|
|
787
|
+
`sun_path`; the new root is short by construction, stays owner-only, is granted
|
|
788
|
+
as a second writable root in the Codex read-only permissions profile, is bound
|
|
789
|
+
read-write inside the optional bwrap boundary, survives failure and
|
|
790
|
+
cancellation like the scratch, and is removed only by the same run-owned prune
|
|
791
|
+
path. Runs recorded without a `tempPath` prune as before.
|
|
792
|
+
- The completion report carries a `model_substitution` warning and the run's
|
|
793
|
+
normalized token usage in a labelled delegate-authored block above the child's
|
|
794
|
+
text, and `run-output` (JSON and text) projects the persisted usage. A
|
|
795
|
+
substituted lane no longer reads as the requested model's clean work in the
|
|
796
|
+
report artifact, while the child's report text and its
|
|
797
|
+
`completionReportSource` attribution are unchanged.
|
|
798
|
+
- The `dev` extra ships `setuptools` and `wheel`, making the documented
|
|
799
|
+
`python -m build --no-isolation` form work instead of failing with
|
|
800
|
+
`BackendUnavailable`. The isolated build path is unchanged.
|
|
801
|
+
|
|
802
|
+
### Changed
|
|
803
|
+
|
|
804
|
+
- The work write guard no longer protects `~/Code` or sibling checkouts by
|
|
805
|
+
default (`isolation.writeGuard.codeRoot` now defaults to `null`). Named
|
|
806
|
+
credential stores, profile homes and the installed Delegate runtime retain
|
|
807
|
+
their existing protection and re-open rules. Set `codeRoot` or `add` to opt
|
|
808
|
+
in to checkout protection. Codex's native sandbox retains its own write limits.
|
|
809
|
+
- Work-mode prompts gain two sentences saying nobody can approve during a Run
|
|
810
|
+
and the task is the approval unless the prompt asks only for a plan,
|
|
811
|
+
review, or read-only answer. Safe and call prompts are unchanged.
|
|
812
|
+
- Runs at `xhigh`, `max`, or `ultra` effort default to a 20-minute stall
|
|
813
|
+
window instead of 8, unless `--stall-minutes`, config, or
|
|
814
|
+
`DELEGATE_STALL_MINUTES` sets one; dry run, manifest, and snapshot report
|
|
815
|
+
`stallWindow` with its source.
|
|
816
|
+
- The `broker_binding_inactive` hint says one refusal while siblings launch is
|
|
817
|
+
usually launch-slot contention (relaunch once, with `--force-launch` if the
|
|
818
|
+
lane is marked) and a repeat means the binding is inactive.
|
|
819
|
+
- OMP alias objects with unknown keys name the allowed keys and point effort
|
|
820
|
+
synonyms at `thinking`; `help omp` carries a valid example. The
|
|
821
|
+
`cursor_auth_required` hint names the auth realm the Run used and the
|
|
822
|
+
`--auth-profile` alternative.
|
|
823
|
+
- **The tracked output cap is now off by default.**
|
|
824
|
+
`<engine>.trackedStreamMaxBytes` used to default to 16 MiB (64 MiB for Pi
|
|
825
|
+
and OMP) and stopped a run as `output_limit_exceeded` when a stream passed
|
|
826
|
+
it. It now defaults to `null`: a verbose run is never killed, truncated, or
|
|
827
|
+
hidden from the parser for its size, and `stdout.log` keeps the whole stream
|
|
828
|
+
and is flushed on every write, so its size and modification time follow the
|
|
829
|
+
child. Set a positive integer to opt an engine back into a cap; the
|
|
830
|
+
`output_limit_exceeded` message then names the key to raise or clear. OMP's
|
|
831
|
+
256 MiB transport and 16 MiB per-record ceilings apply only with an opted-in
|
|
832
|
+
cap, and call mode keeps its own 16 MiB caps. Runaway streams remain the
|
|
833
|
+
stall watchdog's job (`runaway_output`). The 500-line limit in the
|
|
834
|
+
`events.jsonl` mirror was never a limit on `stdout.log`; its
|
|
835
|
+
`stream.lines_truncated` marker now says the child keeps running and
|
|
836
|
+
`stdout.log` keeps the full stream.
|
|
837
|
+
- A Devin answer is held in memory as a bounded excerpt. Devin prints plain
|
|
838
|
+
lines with no message boundaries, so Delegate treats everything it prints as
|
|
839
|
+
one running answer; past 30,000 characters that answer is the first 20,000
|
|
840
|
+
and the last 10,000 with an `[N chars omitted]` marker between them.
|
|
841
|
+
`assistantText`, the completion report, and a failed run's recovered partial
|
|
842
|
+
output read the excerpt, `assistantTextChars` keeps the full length, and
|
|
843
|
+
`stdout.log` keeps the full stream. The answer used to be rebuilt as one
|
|
844
|
+
string on every line, which was quadratic and, with the cap off, held the
|
|
845
|
+
whole run in memory.
|
|
846
|
+
- **Codex and Claude `work` runs are now resumable by default.** They save
|
|
847
|
+
their native session unless the launch says `--no-resumable` (JSON
|
|
848
|
+
`resumable: false`) or the engine config sets `codex.resumable` or
|
|
849
|
+
`claude.resumable` to `false` (both default `true`), so `delegate followup`
|
|
850
|
+
works without remembering `--resumable`. `--resumable` on a launch still
|
|
851
|
+
beats the config key, and `resume` takes the same default and
|
|
852
|
+
`--no-resumable`; a resume source recorded as exactly `true` keeps it, and
|
|
853
|
+
any other source takes the launch default. The default outranks
|
|
854
|
+
`codex.ephemeral` and `claude.noSessionPersistence`, which now apply only to
|
|
855
|
+
runs that are not resumable. Safe runs, call runs, `--pass-through` runs,
|
|
856
|
+
other engines, and workflow `agent()` children (unless the call passes
|
|
857
|
+
`resumable=True`) are unchanged, and `--no-resumable` is a quiet no-op
|
|
858
|
+
there. A succeeded persistent-worktree run that is resumable keeps its
|
|
859
|
+
worktree (`worktreeRetained: "resumable_session"`) instead of retiring it
|
|
860
|
+
when clean, so `followup` and `resume` can re-enter it; a fan-out of these
|
|
861
|
+
runs leaves one retained worktree per succeeded run until `worktree prune
|
|
862
|
+
--merged`, `worktree gc`, or `worktree remove` cleans up. Launch with
|
|
863
|
+
`--no-resumable` when a run's worktree should retire itself.
|
|
864
|
+
- **`--force` no longer overrides a live run on the worktree commands.**
|
|
865
|
+
`worktree remove`, `worktree prune`, and `worktree reap` refuse a worktree
|
|
866
|
+
that a live owner still holds (`run_active`, `run_not_terminal`,
|
|
867
|
+
`process_group_alive`, `worktree_leased`) even with `--force`, and the
|
|
868
|
+
refusal names the new `--kill-live`, the only override, which removes the
|
|
869
|
+
worktree out from under the run. `--kill-live` does not discard uncommitted
|
|
870
|
+
work or force the branch by itself. Removal is also refused for a running
|
|
871
|
+
run recorded in the worktree's own `.delegate/` registry
|
|
872
|
+
(`nested_run_active`) and for a nested registry, or a run in it, that cannot
|
|
873
|
+
be read (`nested_registry_unreadable`), each overridable only by
|
|
874
|
+
`--kill-live`. A skipped entry in `prune` and `reap` JSON carries the same
|
|
875
|
+
hint.
|
|
876
|
+
- Uncommitted work has one definition across `worktree remove`, `prune`,
|
|
877
|
+
`reap`, and completion retirement: what the lane actually changed. Files the
|
|
878
|
+
launch seeded from a dirty source that still match their digest, and the
|
|
879
|
+
ledger paths in `worktrees.retirementIgnoreGlobs` (default `.beads/**` and
|
|
880
|
+
`.papercuts.jsonl`), do not count, so a worktree that `prune` plans is one
|
|
881
|
+
`remove` accepts and a refusal names only the lane's own paths. Ledger edits
|
|
882
|
+
are saved before any removal: changed ledger files are copied and
|
|
883
|
+
byte-compared into `<Registry>/salvage/<worktree>-<UTC timestamp>/`. The
|
|
884
|
+
result reports `salvagePath` and `salvagedPaths`, the text output prints a
|
|
885
|
+
`saved N changed ledger file(s)` line, and completion retirement records
|
|
886
|
+
`worktreeSalvagePath` on the run. Deleted ledger files and the old name of a
|
|
887
|
+
renamed one are recorded in a `MANIFEST.tsv` (status, path, old path) in the
|
|
888
|
+
same directory and reported as `salvageRemovedPaths`. A copy that fails, or
|
|
889
|
+
a `git status` that cannot run, refuses the removal as
|
|
890
|
+
`ledger_salvage_failed`. Delegate never deletes a salvage directory, and a
|
|
891
|
+
source-gone `reap` deletes the path without a copy.
|
|
892
|
+
- **An OMP `provider/model` id you type is now pinned by default.** A
|
|
893
|
+
`provider/model` given as `--model`, input-JSON `model`, a positional raw
|
|
894
|
+
id, or a literal workflow `agent(model=...)` behaves as `--continuity-mode
|
|
895
|
+
pinned` unless you name a mode. Delegate launches it with a private
|
|
896
|
+
`--config` overlay that sets `retry.modelFallback` and
|
|
897
|
+
`retry.usageAwareFallback` to `false`, so OMP's own retry chains cannot move
|
|
898
|
+
the run to another provider (retrying the same model is unaffected, the
|
|
899
|
+
overlay beats your `config.yml`, and a dry run shows `--config <omp config
|
|
900
|
+
overlay>`). If another provider or model is served anyway, the run fails as
|
|
901
|
+
`model_continuity_paused` with a message naming what OMP tried to serve, for
|
|
902
|
+
example `requested opencode-go/glm-5.3, but omp tried to serve
|
|
903
|
+
fireworks/glm-5p3`. A delegate alias (a key of `omp.models`) and
|
|
904
|
+
`omp.defaultModel` stay `fungible`, so multi-subscription failover keeps
|
|
905
|
+
working; the run records and warns (`model_substitution`) which provider
|
|
906
|
+
answered, and `--continuity-mode pinned` on an alias pins it.
|
|
907
|
+
`--continuity-mode fungible` or `panel` on a typed id opts out of the
|
|
908
|
+
overlay. Workflows have no continuity passthrough yet, so there a literal id
|
|
909
|
+
is pinned and an alias is fungible.
|
|
910
|
+
- An `omp` model name that is not an `omp.models` alias and not a
|
|
911
|
+
`provider/model` selector fails with `invalid_alias` once a discovered omp
|
|
912
|
+
catalog exists, unless it is exactly a catalog model id; the error lists the
|
|
913
|
+
configured aliases and the nearest catalog selectors. OMP resolves a bare
|
|
914
|
+
name by fuzzy match against its own catalog and can serve a different
|
|
915
|
+
provider (a retired alias, `kimi`, resolved to `fireworks/kimi-k3`), so
|
|
916
|
+
Delegate refuses instead of passing it through. With no catalog the run
|
|
917
|
+
proceeds with a warning; `delegate capabilities refresh` lets bare names be
|
|
918
|
+
checked.
|
|
919
|
+
- **Claude `work` runs and their followups now run with background tasks
|
|
920
|
+
disabled.** Delegate sets `CLAUDE_CODE_DISABLE_BACKGROUND_TASKS=1` and adds
|
|
921
|
+
`--disallowedTools Monitor`, because a headless child ends when the model
|
|
922
|
+
stops and a background task or Monitor dies with the session. As a long gate
|
|
923
|
+
must then run in the foreground, Delegate also sets
|
|
924
|
+
`BASH_DEFAULT_TIMEOUT_MS` and `BASH_MAX_TIMEOUT_MS` to the run's own
|
|
925
|
+
`--timeout`, never below two hours (a run with no `--timeout` gets two
|
|
926
|
+
hours), so Claude Code's 2-minute default and 10-minute maximum do not cut a
|
|
927
|
+
foreground command off. `claude.disableBackgroundTasks: false` turns all of
|
|
928
|
+
it off. An auth profile's `env` or a workspace `--env` cannot undo these
|
|
929
|
+
variables: Delegate keeps its own values and warns `profile env NAME
|
|
930
|
+
ignored` or `workspace env NAME ignored` when the value differed. Every
|
|
931
|
+
framed `work` and `safe` prompt also carries a two-sentence rule that ending
|
|
932
|
+
the turn ends the run and that long jobs must run in the foreground and
|
|
933
|
+
finish before the final message; verbatim slash pass-through prompts are not
|
|
934
|
+
rewritten.
|
|
935
|
+
- **A finished run's scratch is now reclaimed on its own age.** Scratch, its
|
|
936
|
+
sidecars (for example mail-push engine homes), and the compact child temp
|
|
937
|
+
used to be removed only when the run record was pruned, so finished runs
|
|
938
|
+
kept their disk until then. The ambient retention pass now removes them for
|
|
939
|
+
terminal runs older than `tracking.retention.scratchDays` (default `3`; `0`
|
|
940
|
+
reclaims as soon as a run is terminal), keeps the run record, and stamps
|
|
941
|
+
`scratchReclaimedAt` and `scratchReclaimedBytes` into the run state;
|
|
942
|
+
`snapshot` shows both, and prints a `scratch reclaimed:` line in text.
|
|
943
|
+
`delegate runs reclaim [--older-than DAYS] [--dry-run]` does the same on
|
|
944
|
+
demand and lists sizes. Running, stale, and already-reclaimed runs are
|
|
945
|
+
skipped, and a run that fails is reported without stopping the others.
|
|
946
|
+
Reclaiming applies the recorded-path and ownership checks that pruning uses,
|
|
947
|
+
removes entries one at a time without following symlinks, and refuses a tree
|
|
948
|
+
that holds another owner's entry or crosses onto another filesystem. The
|
|
949
|
+
ambient pass spends at most 20 seconds, counted inside the walk and the
|
|
950
|
+
removal; a run it could not finish gets no marker, is reported as
|
|
951
|
+
`budget_exhausted`, and implicit reclaim then waits ten minutes before
|
|
952
|
+
continuing. `runs reclaim` has no budget or cooldown, and still works when
|
|
953
|
+
`tracking.retention.enabled` is `false`, which stops the ambient pass.
|
|
954
|
+
- `--forbid-commit` now works in every isolation mode, including `--isolation
|
|
955
|
+
none`, which used to be refused; with isolation omitted it still implies
|
|
956
|
+
`--isolation worktree`. Delegate creates run-owned git hooks (`pre-commit`,
|
|
957
|
+
`prepare-commit-msg`, `commit-msg`, and `pre-merge-commit`) that refuse, and
|
|
958
|
+
points `core.hooksPath` at them through one variable,
|
|
959
|
+
`GIT_CONFIG_PARAMETERS`. Codex's default environment filter drops names
|
|
960
|
+
containing `KEY`, `SECRET`, or `TOKEN`, which left the indexed
|
|
961
|
+
`GIT_CONFIG_COUNT` form without its keys and broke every git command;
|
|
962
|
+
`GIT_CONFIG_PARAMETERS` survives it. The post-exit check stays as the
|
|
963
|
+
backstop: it fails the run if commits remain ahead of the creation base (the
|
|
964
|
+
HEAD recorded at launch for `--isolation none`) or if the checkout's own
|
|
965
|
+
HEAD reflog shows a commit made since launch and left behind, on a side
|
|
966
|
+
branch or reset away, for in-place and worktree runs. An unreadable reflog
|
|
967
|
+
with nothing found otherwise leaves the policy unverified, which also fails
|
|
968
|
+
the run. The hooks are a tripwire, not a wall: `git -c
|
|
969
|
+
core.hooksPath=/dev/null commit` or `git commit-tree` bypasses them, and the
|
|
970
|
+
post-exit check can be defeated on purpose too.
|
|
971
|
+
- A mail send that reaches no inbox fails with `mail_not_delivered` (exit 1)
|
|
972
|
+
instead of returning `ok` with exit 0. The message names each recipient's
|
|
973
|
+
outcome and reason and states the eligibility rule, the diagnostics carry the
|
|
974
|
+
`msgId` and ledger rows, and the sent ledger is still written.
|
|
975
|
+
- `--auth-profile` precedence (flag, then environment, then config default)
|
|
976
|
+
now selects the config file as well as the profile: `--auth-profile
|
|
977
|
+
work|personal` with an existing overlay selects `config.NAME.json`, its keys,
|
|
978
|
+
and the child's `AI_PROFILE`, and warns when that replaces an ambient
|
|
979
|
+
`DELEGATE_CONFIG` or a different `AI_PROFILE`. A flag whose overlay is
|
|
980
|
+
missing warns that the environment's config and keys stay in effect, and the
|
|
981
|
+
resolver warns when an inherited detection variable overrides the config's
|
|
982
|
+
`profiles.default`. The profile shim now finds the flag anywhere before `--`,
|
|
983
|
+
keeps the last occurrence, and strips whitespace, as the Python parser does.
|
|
984
|
+
- Errors point at the command that does what was asked. `runs`/`ps` given a
|
|
985
|
+
run id, harness alias, `--id`, or `--alias` point at `snapshot` and
|
|
986
|
+
`run-output`; unknown `show`, `status`, `info`, `inspect`, `logs`, and
|
|
987
|
+
`output` subcommands suggest the per-run commands; `mail send --body`,
|
|
988
|
+
`--message`, and `-m` explain that the body is positional, `--file`, or `-`
|
|
989
|
+
(a bare `-` now reads stdin, as help already promised); and `followup` given
|
|
990
|
+
a resume-only option says the option belongs to `resume` and shows the
|
|
991
|
+
resume form.
|
|
992
|
+
- **Exit-code semantics: a child's exit code 0 no longer means success on its
|
|
993
|
+
own.** One outcome function now decides every run's `ok`, `status`, exit code,
|
|
994
|
+
persisted record, `wait` result, and workflow `agent()` result, so these can
|
|
995
|
+
no longer disagree. A run now fails with a nonzero exit, and `ok: false`, in
|
|
996
|
+
three cases where it used to report success. The first is a tracked child
|
|
997
|
+
that exits 0 without assistant text (`failureKind: no_assistant_text`).
|
|
998
|
+
Before this change the tracked envelope said `ok: true` with exit 0 while
|
|
999
|
+
state.json said `ok: false`; call runs already failed this case and still do,
|
|
1000
|
+
now with the same `failureKind`. The second is a tracked or call child whose
|
|
1001
|
+
provider's last unrecovered error was a quota or 429 refusal
|
|
1002
|
+
(`provider_quota`). The third is a tracked run whose `--expect-file PATH`
|
|
1003
|
+
deliverable is missing (`deliverable_missing`). Scripts that treated exit 0
|
|
1004
|
+
from an empty tracked run as success will now see exit 1. Every failed or
|
|
1005
|
+
cancelled run carries `failureKind`, a closed enum listed in
|
|
1006
|
+
`docs/cli-reference.md`. A missing `--expect-file` deliverable is reported as
|
|
1007
|
+
`deliverable_missing` even when the run also produced no assistant text: the
|
|
1008
|
+
verdict the caller asked for outranks the no-output label, and both fail the
|
|
1009
|
+
run. Summaries from `runs`/`ps`/`wait` carry `failureKind` whenever the record
|
|
1010
|
+
holds the key, `null` included, so a consumer that derives its verdict from a
|
|
1011
|
+
summary agrees with the envelope instead of falling back to the older
|
|
1012
|
+
result-quality veto (a quiet work run whose changes landed succeeded).
|
|
1013
|
+
- **A work run that changed files but ended quietly still succeeds.** A
|
|
1014
|
+
tracked `work` or `followup` run that exits 0 without assistant text, while
|
|
1015
|
+
its work summary shows changed files or commits, is `succeeded` with a
|
|
1016
|
+
`no_assistant_text` warning, and the envelope carries the `workSummary` so an
|
|
1017
|
+
orchestrator can adopt the work. Without changes the same run fails as
|
|
1018
|
+
`no_assistant_text`. Safe and call runs are unaffected. Only persistent or
|
|
1019
|
+
attached worktrees record a work summary, so a quiet run in any other
|
|
1020
|
+
isolation still fails. Pass `--expect-file` for a stricter check.
|
|
1021
|
+
- Provider signals are classified from the final attempt only. On the tracked
|
|
1022
|
+
and call paths, quota, provider-error, and unrecovered-error verdicts read
|
|
1023
|
+
only the last attempt's own error events, last error message, and stderr, so
|
|
1024
|
+
an earlier attempt's 429 no longer labels a later attempt that failed for a
|
|
1025
|
+
different reason, and a final attempt's unrecovered quota error survives the
|
|
1026
|
+
retry merge. The final attempt owns the provider terminal state as well: an
|
|
1027
|
+
earlier attempt's `turn.cancelled`, max-turns stop, or refusal-coded failure
|
|
1028
|
+
no longer decides a merged run whose retry or failover answered with text and
|
|
1029
|
+
exited 0. The call path's empty-success retry now joins the thread-retry and
|
|
1030
|
+
auth-fallback merges, so a first attempt's quota line cannot classify a clean
|
|
1031
|
+
retry as `provider_quota`.
|
|
1032
|
+
- `wait` reports a running record whose runner process is gone (`staleReason`
|
|
1033
|
+
`dead_pid` or `missing_pid`) as `failureKind: runner_lost` instead of
|
|
1034
|
+
`stalled`. `stalled` is kept for the stall watchdog, which workflows retry as
|
|
1035
|
+
transient; a lost runner is not retried.
|
|
1036
|
+
- Workflows: a structured `agent()` call whose work child changed its worktree
|
|
1037
|
+
but returned no valid structured result is retried only by resuming the same
|
|
1038
|
+
session and asking for the structured result alone. When the engine cannot
|
|
1039
|
+
resume, Delegate does not launch a fresh child into that tree; the call fails
|
|
1040
|
+
as `structured_invalid` and the `agent_structured_exhausted` journal event
|
|
1041
|
+
carries the child's `workSummary`. Safe-mode children and work children
|
|
1042
|
+
without changes keep the existing correction retry.
|
|
1043
|
+
- Tracked `safe`/`work` launches accept a repeatable `--expect-file PATH`. After
|
|
1044
|
+
the child exits, Delegate checks the child's process group for members that
|
|
1045
|
+
are still alive and records an `orphanedProcesses` warning before it
|
|
1046
|
+
terminates them.
|
|
1047
|
+
- Call-mode JSON carries `assistantText`, `assistantTextChars`, and
|
|
1048
|
+
`assistantTextTruncated` on every engine, the same fields tracked envelopes
|
|
1049
|
+
use. `text`, `textChars`, and `textTruncated` stay for one release as
|
|
1050
|
+
deprecated aliases.
|
|
1051
|
+
- Workflows: `agent(..., on_failure="typed")` returns a falsy `AgentFailure`
|
|
1052
|
+
when structured retries run out. It carries the last parsed candidate, the
|
|
1053
|
+
attempt count, `failureKind`, the run id, and the served model and provider;
|
|
1054
|
+
the default is still `None`. The new globals are `agent_meta(label)`, which
|
|
1055
|
+
exposes the child's outcome and served model and provider, and
|
|
1056
|
+
`capabilities`, which lets a script detect these features. A failed child no
|
|
1057
|
+
longer erases an earlier attempt's parsed candidate. A child that exits 0
|
|
1058
|
+
without text now receives a structured correction retry. Structured parsing
|
|
1059
|
+
tolerates raw control characters, including inside a prose-wrapped or fenced
|
|
1060
|
+
answer: a candidate the scanner finds is decoded again with the stray
|
|
1061
|
+
characters stripped before a quoted fragment of the damaged document can be
|
|
1062
|
+
returned in its place.
|
|
1063
|
+
- Bundled Codex declarations move to the GPT-6 line. `gpt-6-sol` and
|
|
1064
|
+
`gpt-6-luna` replace the `gpt-5.6-sol`/`gpt-5.6-luna` rows and `gpt-5.6-terra`
|
|
1065
|
+
is dropped outright, verified against live `codex` enumeration on both the
|
|
1066
|
+
work and personal profiles (2026-09-22): Sol carries `low`-`ultra` at a
|
|
1067
|
+
`medium` default, Luna carries `low`-`max` at `medium`. The `pi` and `omp`
|
|
1068
|
+
bundled selectors follow to `openai-codex/gpt-6-sol`, and the Codex discovery
|
|
1069
|
+
fixture mirrors the live payload for the new slug. Doc prose that named
|
|
1070
|
+
`gpt-5.6-sol` as the single bundled `max`-capable Codex model was already
|
|
1071
|
+
stale and now names the bundled set (`gpt-6-astra`, `gpt-6-sol`,
|
|
1072
|
+
`gpt-6-luna`).
|
|
1073
|
+
- Bundled Claude declarations replace `claude-opus-5` with `claude-opus-5-5`,
|
|
1074
|
+
and the pinned-continuity error prose names the new id.
|
|
1075
|
+
- Bundled Grok declarations bumped to 4.7. The Grok CLI now ships `grok-4.7` as
|
|
1076
|
+
its upstream default (bundled rows: `grok-4.7`, `grok-4.6`). Cursor's 4.7
|
|
1077
|
+
ladder drops the `cursor-` prefix the 4.5/4.6 rows carried, so the bundled
|
|
1078
|
+
Cursor rows and the fixed-effort selector table now name `grok-4.7-xhigh` and
|
|
1079
|
+
`grok-4.7-xhigh-fast`; the still-served `cursor-grok-4.6-xhigh*` selectors
|
|
1080
|
+
stay in the fixed-effort table. The Grok discovery fixture mirrors the live
|
|
1081
|
+
`grok models` output, including `grok-4.7-build-fast`.
|
|
1082
|
+
- The skill-review preamble is explicitly bounded: read the served skill index
|
|
1083
|
+
once, read only the skills judged relevant, and do not run discovery CLIs to
|
|
1084
|
+
enumerate skills. The requirement stays mandatory; it no longer reads as an
|
|
1085
|
+
open-ended discovery task.
|
|
1086
|
+
- `workflow reject`'s `agent_rejected` event records `rejectedBy: "coordinator"`,
|
|
1087
|
+
and the workflow DSL `reject(key, reason, by=...)` accepts `seat`,
|
|
1088
|
+
`merge-gate`, or `coordinator`. Emitters that declare nothing keep the legacy
|
|
1089
|
+
event shape, so a missing `rejectedBy` means unknown, not seat failure.
|
|
1090
|
+
- A structured-output resume retry re-renders the target schema into the
|
|
1091
|
+
correction prompt instead of asking the model to recall the shape; both GLM
|
|
1092
|
+
review retries had rebuilt a root-array schema as an object wrapper and failed
|
|
1093
|
+
again.
|
|
1094
|
+
|
|
1095
|
+
### Security
|
|
1096
|
+
- Native Codex and Claude session files now persist for every work run,
|
|
1097
|
+
because those runs are resumable by default. Codex's rollout under
|
|
1098
|
+
`CODEX_HOME/sessions` and Claude Code's transcript under its `projects`
|
|
1099
|
+
directory hold the prompt as sent, the child's replies, and tool inputs and
|
|
1100
|
+
results (file contents the child read, command output). They live in the
|
|
1101
|
+
harness's own store, outside `.delegate/`, outside Delegate's redaction, and
|
|
1102
|
+
outside `runs prune` and the worktree cleanup commands, and Delegate does
|
|
1103
|
+
not prune them. A profile with a separate `CODEX_HOME` or Claude config
|
|
1104
|
+
directory keeps them in that account's store. Reduce the footprint with
|
|
1105
|
+
`--no-resumable`, `codex.resumable: false`, or `claude.resumable: false`.
|
|
1106
|
+
The untrusted-input boundaries are unchanged: safe runs, call runs (Claude
|
|
1107
|
+
`call --pure` still passes `--no-session-persistence`), `--pass-through`
|
|
1108
|
+
runs, other engines, and workflow children without `resumable=True` never
|
|
1109
|
+
save a session.
|
|
1110
|
+
- **Work runs now have a write guard, on by default on Linux.** Work mode runs
|
|
1111
|
+
the child with the caller's own filesystem rights, so one confused lane (`rm
|
|
1112
|
+
-rf ~`, a stray redirect into a sibling checkout) could destroy things
|
|
1113
|
+
nobody can recreate. The guard is a protect-list, not a home allowlist: a
|
|
1114
|
+
named set of irreplaceable paths becomes read-only and everything else stays
|
|
1115
|
+
writable, and reads are never restricted. By default it protects, each only
|
|
1116
|
+
when it exists, the credential stores `~/.ssh`, `~/.gnupg`, `~/.config/gh`,
|
|
1117
|
+
`~/.config/gcloud`, `~/.aws`, `~/.azure`, `~/.kube`, `~/.netrc`,
|
|
1118
|
+
`~/.git-credentials`, and `~/.password-store`; `~/.ai-profiles`; the
|
|
1119
|
+
installed Delegate runtime (`~/.local/bin/delegate` plus `~/.delegate/src`,
|
|
1120
|
+
`releases`, `bin`, and `config*.json`); and the code root (`~/Code`), so a
|
|
1121
|
+
lane in one checkout cannot write into a sibling. Inside those paths the
|
|
1122
|
+
run's own needs are re-opened: the execution root, the git common directory,
|
|
1123
|
+
the run registry, run scratch and compact temp, mail-push homes, and the
|
|
1124
|
+
selected engine's home. `/tmp`, `TMPDIR`, and home caches such as `~/.cache`
|
|
1125
|
+
are not protected. The guard never applies to safe mode, `--pass-through`
|
|
1126
|
+
runs are not guarded, and it does not stop reads, network use, or use of
|
|
1127
|
+
credentials already in the environment.
|
|
1128
|
+
- Backends and platform defaults for the write guard. On Linux the child runs
|
|
1129
|
+
under bubblewrap (`bwrap --dev-bind / /` with each protected path bound
|
|
1130
|
+
read-only), preflighted before launch; the execution root is bound as a
|
|
1131
|
+
mount of its own so a lane cannot rename its checkout away, a new hard link
|
|
1132
|
+
to a protected file cannot be made, and writes through symlinks to protected
|
|
1133
|
+
paths are refused. **On macOS the Seatbelt guard is opt-in:** with
|
|
1134
|
+
`isolation.writeGuard.macosSeatbelt` at its default `false`, the guard
|
|
1135
|
+
status is `off` with no warning and no refusal, and setting it `true` opts
|
|
1136
|
+
in to the `sandbox-exec` guard, which then wraps every engine except a Codex
|
|
1137
|
+
lane whose own `workspace-write` sandbox is on (a Codex lane with its sandbox
|
|
1138
|
+
bypassed is wrapped, and that combination is not yet live-tested). To keep the `external-sandbox` policy
|
|
1139
|
+
profile but put Codex's own sandbox back for work lanes, set
|
|
1140
|
+
`policy.harness.codex.work.bypassApprovalsAndSandbox` to `false`; Delegate
|
|
1141
|
+
then emits `--sandbox workspace-write` with `--add-dir` roots for the git
|
|
1142
|
+
common directory, registry, scratch, temp, `--writable` paths, and existing
|
|
1143
|
+
home caches.
|
|
1144
|
+
- Configure and inspect the write guard with `isolation.writeGuard`
|
|
1145
|
+
(`enabled`, `onUnavailable`, `macosSeatbelt`, `codeRoot`, `add`, `remove`,
|
|
1146
|
+
`writable`, `homeCaches`; an unknown key or wrong type fails as
|
|
1147
|
+
`invalid_isolation_config`), the `DELEGATE_WRITE_GUARD` environment variable
|
|
1148
|
+
(`off`, `0`, `false`, or `no` disables, `on`, `1`, `true`, or `yes` enables,
|
|
1149
|
+
and it overrides the config), and `--writable PATH` (repeatable, `work`
|
|
1150
|
+
mode, CLI-only), which re-opens an existing path for one run. The plan
|
|
1151
|
+
(backend, protected paths, and each re-open with its reason) is recorded in
|
|
1152
|
+
the run manifest under `writeGuard` and printed by `--dry-run`, and
|
|
1153
|
+
work-mode prompts gain a two-sentence note naming the protected paths so a
|
|
1154
|
+
lane reports a blocked write instead of working around it. When no backend
|
|
1155
|
+
is usable, `isolation.writeGuard.onUnavailable` decides: `warn` (the
|
|
1156
|
+
default) launches unguarded and records a warning, and `refuse` fails the
|
|
1157
|
+
launch with `write_guard_unavailable`. On Linux with `warn`, a bwrap
|
|
1158
|
+
preflight that fails on one unbindable path drops only that path (listed
|
|
1159
|
+
under `writeGuard.unbound`, with a warning) instead of the whole guard; when
|
|
1160
|
+
a protected path was dropped the status is `partial` rather than `enforced`
|
|
1161
|
+
and the warning starts "work write guard is PARTIAL". `refuse` never
|
|
1162
|
+
retries.
|
|
1163
|
+
- Only the selected engine's own home variable (`CODEX_HOME`,
|
|
1164
|
+
`CLAUDE_CONFIG_DIR`, `KIMI_CODE_HOME`, or that engine's default directory)
|
|
1165
|
+
can re-open a directory inside a protected path, and only when it is
|
|
1166
|
+
provably one profile. The home must itself carry an identity file
|
|
1167
|
+
(`.claude.json`, `.credentials.json`, or `auth.json` beside `config.toml`),
|
|
1168
|
+
and a bounded scan below it (three levels, no symlinks, at most 2000
|
|
1169
|
+
directories) must finish and find no other profile's home. A parent of
|
|
1170
|
+
profiles, a home that is itself protected, and a scan that hits the cap or
|
|
1171
|
+
cannot list a directory are refused: the directory stays read-only, the
|
|
1172
|
+
manifest lists it under `writeGuard.refused` with the reason, and the run
|
|
1173
|
+
gets a warning. An engine Delegate has no home variable for (droid, for one)
|
|
1174
|
+
gets no automatic re-open; name its profile directory in
|
|
1175
|
+
`isolation.writeGuard.writable` or pass `--writable`. Known limits: a second
|
|
1176
|
+
profile nested deeper than three levels is not found; a hard link that
|
|
1177
|
+
already exists between a protected file and a writable name is the same
|
|
1178
|
+
inode, so a write through it changes the protected file; and a protected
|
|
1179
|
+
path that is itself a symlink is protected at its target while the link
|
|
1180
|
+
stays replaceable.
|
|
1181
|
+
- A lane started through an estate launcher (`estate-claude`, `estate-codex`,
|
|
1182
|
+
`estate-omp`, any program named `estate-*`) gets the profiles root
|
|
1183
|
+
(`ESTATE_AI_PROFILES_ROOT`, then `AI_PROFILES_ROOT`, then `~/.ai-profiles`)
|
|
1184
|
+
re-opened, recorded as "estate launcher writes profile state". The launcher
|
|
1185
|
+
picks an account, refreshes its token, and writes session, plugin, and lock
|
|
1186
|
+
state there on every launch, so protecting it stopped `estate-claude` before
|
|
1187
|
+
Claude started. Those lanes can therefore write every profile's files; lanes
|
|
1188
|
+
started any other way keep `~/.ai-profiles` protected, and a Codex lane with
|
|
1189
|
+
its own sandbox on never gets it as a writable root.
|
|
1190
|
+
|
|
8
1191
|
## [0.31.0] - 2026-09-14
|
|
9
1192
|
|
|
10
1193
|
### Harness compatibility audit (2026-09-07)
|
|
@@ -1387,7 +2570,8 @@ Usage-audit fix wave: 82 sessions and 1,241 delegate invocations from one week o
|
|
|
1387
2570
|
|
|
1388
2571
|
- Releases before 0.1.3 predate this changelog.
|
|
1389
2572
|
|
|
1390
|
-
[Unreleased]: https://github.com/treygoff24/delegate-agent/compare/v0.
|
|
2573
|
+
[Unreleased]: https://github.com/treygoff24/delegate-agent/compare/v0.32.0...HEAD
|
|
2574
|
+
[0.32.0]: https://github.com/treygoff24/delegate-agent/compare/v0.31.0...v0.32.0
|
|
1391
2575
|
[0.31.0]: https://github.com/treygoff24/delegate-agent/compare/v0.29.1...v0.31.0
|
|
1392
2576
|
[0.30.0]: https://github.com/treygoff24/delegate-agent/compare/v0.29.1...v0.30.0
|
|
1393
2577
|
[0.29.1]: https://github.com/treygoff24/delegate-agent/compare/v0.29.0...v0.29.1
|