delegate-agent-cli 0.20.0__tar.gz → 0.21.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.
Files changed (147) hide show
  1. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/CHANGELOG.md +118 -0
  2. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/PKG-INFO +1 -1
  3. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/docs/cli-reference.md +28 -2
  4. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/docs/worktrees.md +22 -0
  5. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/pyproject.toml +1 -1
  6. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/__init__.py +1 -1
  7. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/capability_commands.py +20 -2
  8. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/cli.py +2 -0
  9. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/cli_parser.py +4 -0
  10. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/command_help.py +19 -2
  11. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/harness_discovery.py +268 -33
  12. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/isolation.py +128 -0
  13. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/private_io.py +23 -5
  14. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/redaction.py +33 -1
  15. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/rendering.py +39 -0
  16. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/request_build.py +243 -43
  17. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/setup_commands.py +26 -4
  18. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/worktree_commands.py +22 -4
  19. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/worktree_execution.py +7 -15
  20. delegate_agent_cli-0.21.0/src/delegate_agent/worktree_gc.py +1129 -0
  21. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/worktree_mgmt.py +8 -0
  22. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent_cli.egg-info/PKG-INFO +1 -1
  23. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_capability_commands.py +70 -2
  24. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_engine_argv.py +461 -1
  25. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_execution_worktree_run.py +21 -2
  26. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_harness_discovery.py +474 -0
  27. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_setup_commands.py +27 -0
  28. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_snapshot_redaction.py +14 -0
  29. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_worktree_prune_gc.py +1032 -1
  30. delegate_agent_cli-0.20.0/src/delegate_agent/worktree_gc.py +0 -577
  31. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/CONTRIBUTING.md +0 -0
  32. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/LICENSE +0 -0
  33. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/MANIFEST.in +0 -0
  34. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/README.md +0 -0
  35. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/SECURITY.md +0 -0
  36. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/bin/delegate-profile-shim +0 -0
  37. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/bin/delegate.py +0 -0
  38. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/config.example.json +0 -0
  39. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/docs/agent-setup.md +0 -0
  40. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/docs/assets/delegate-agent-header.png +0 -0
  41. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/docs/configuration.md +0 -0
  42. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/docs/delegate-workflows.md +0 -0
  43. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/docs/development.md +0 -0
  44. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/docs/live-runtime.md +0 -0
  45. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/docs/publishing-checklist.md +0 -0
  46. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/docs/security-model.md +0 -0
  47. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/docs/troubleshooting.md +0 -0
  48. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/examples/task.claude.json +0 -0
  49. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/examples/task.codex.json +0 -0
  50. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/examples/task.cursor.json +0 -0
  51. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/examples/task.droid.json +0 -0
  52. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/examples/task.grok.json +0 -0
  53. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/examples/task.judge.json +0 -0
  54. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/examples/task.kimi.json +0 -0
  55. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/setup.cfg +0 -0
  56. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/archived_logs.py +0 -0
  57. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/argv_builders.py +0 -0
  58. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/argv_utils.py +0 -0
  59. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/bundled_models.py +0 -0
  60. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/child_failures.py +0 -0
  61. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/command_errors.py +0 -0
  62. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/config.py +0 -0
  63. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/config_commands.py +0 -0
  64. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/constants.py +0 -0
  65. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/describe_payload.py +0 -0
  66. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/errors.py +0 -0
  67. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/git_utils.py +0 -0
  68. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/harness_events.py +0 -0
  69. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/inspection_commands.py +0 -0
  70. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/json_types.py +0 -0
  71. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/log_output.py +0 -0
  72. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/model_discovery.py +0 -0
  73. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/profile_commands.py +0 -0
  74. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/profile_guard.py +0 -0
  75. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/profiles.py +0 -0
  76. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/prompt_instructions.py +0 -0
  77. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/prompt_transport.py +0 -0
  78. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/reasoning.py +0 -0
  79. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/request_models.py +0 -0
  80. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/retention.py +0 -0
  81. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/run_metadata.py +0 -0
  82. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/run_output_commands.py +0 -0
  83. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/run_registry.py +0 -0
  84. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/run_status.py +0 -0
  85. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/runner.py +0 -0
  86. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/safe_workspace.py +0 -0
  87. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/seatbelt.py +0 -0
  88. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/snapshot_view.py +0 -0
  89. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/structured_output.py +0 -0
  90. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/wait_cancel_commands.py +0 -0
  91. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/workflows/__init__.py +0 -0
  92. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/workflows/commands.py +0 -0
  93. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/workflows/registry.py +0 -0
  94. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/workflows/runtime.py +0 -0
  95. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/workflows/schema.py +0 -0
  96. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/workflows/script.py +0 -0
  97. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/worktree_records.py +0 -0
  98. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/worktree_remove.py +0 -0
  99. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/worktree_summary.py +0 -0
  100. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent/wsl.py +0 -0
  101. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent_cli.egg-info/SOURCES.txt +0 -0
  102. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent_cli.egg-info/dependency_links.txt +0 -0
  103. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent_cli.egg-info/entry_points.txt +0 -0
  104. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent_cli.egg-info/requires.txt +0 -0
  105. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/src/delegate_agent_cli.egg-info/top_level.txt +0 -0
  106. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_child_failures.py +0 -0
  107. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_codex_pure_sandbox.py +0 -0
  108. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_codex_thread_resilience.py +0 -0
  109. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_command_help.py +0 -0
  110. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_config_commands.py +0 -0
  111. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_delegate_help_cli.py +0 -0
  112. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_delegate_isolation.py +0 -0
  113. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_delegate_parser.py +0 -0
  114. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_delegate_validation.py +0 -0
  115. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_end_to_end_tracking.py +0 -0
  116. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_execution_argv_and_prompt.py +0 -0
  117. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_execution_dry_run.py +0 -0
  118. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_execution_worktree_failure_cleanup.py +0 -0
  119. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_execution_worktree_preflight.py +0 -0
  120. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_harness_events.py +0 -0
  121. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_inspection_commands.py +0 -0
  122. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_kimi_commands.py +0 -0
  123. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_model_discovery.py +0 -0
  124. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_model_selection_wave1a.py +0 -0
  125. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_model_selection_wave1b.py +0 -0
  126. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_omp_read_only_behavior.py +0 -0
  127. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_packaging.py +0 -0
  128. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_profiles.py +0 -0
  129. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_pure_call.py +0 -0
  130. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_reasoning_capabilities.py +0 -0
  131. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_retention.py +0 -0
  132. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_run_registry.py +0 -0
  133. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_runner_capture.py +0 -0
  134. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_safe_workspace_isolation.py +0 -0
  135. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_slash_passthrough.py +0 -0
  136. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_snapshot_rendering.py +0 -0
  137. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_snapshot_run_output.py +0 -0
  138. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_snapshot_view.py +0 -0
  139. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_structured_output.py +0 -0
  140. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_tracked_timeout.py +0 -0
  141. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_utility_modules.py +0 -0
  142. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_wait_cancel_commands.py +0 -0
  143. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_wave4_launch_features.py +0 -0
  144. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_workflow_commands.py +0 -0
  145. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_worktree_list_show.py +0 -0
  146. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_worktree_remove.py +0 -0
  147. {delegate_agent_cli-0.20.0 → delegate_agent_cli-0.21.0}/tests/test_wsl_guardrails.py +0 -0
@@ -5,6 +5,123 @@ 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
+ ## [0.21.0] - 2026-07-26
9
+
10
+ ### Added
11
+
12
+ - Added `delegate worktree gc --all`, which scans the machine-wide worktree
13
+ pool for worktrees whose source repository no longer exists. Such worktrees
14
+ were previously unreachable by any `gc` invocation, because reaping is driven
15
+ by the run registry inside each source repository and that registry dies with
16
+ the repository. The scan adds no catalog or persistent state: it reads each
17
+ worktree's Git backlink directly, and treats a backlink whose target is gone
18
+ as the sole orphan signal. The scan removes nothing at all, at any depth: a
19
+ missing source repository makes a worktree's uncommitted state impossible to
20
+ inspect, so orphans and empty pool directories alike are reported for
21
+ deliberate manual cleanup. Because that output invites manual deletion, every
22
+ uncertainty resolves away from an orphan verdict: a pointer file that cannot
23
+ be read, a path comparison the filesystem cannot answer, an entry too recently
24
+ modified to have settled, and a directory whose name does not match the pool's
25
+ own fingerprint contract are all reported as warnings rather than orphans.
26
+ That last rule is what keeps a scan of an arbitrary root from labelling
27
+ somebody else's directories. `--pool PATH` scans a different pool root, which
28
+ reaches pools stranded by an earlier `worktrees.dataHome`.
29
+ - `capabilities refresh` and `setup` now name each harness on stderr as it is
30
+ probed, so a harness that hangs is identifiable rather than anonymous
31
+ silence. Progress output is suppressed under `--json`.
32
+
33
+ ### Changed
34
+
35
+ - A launch now fails instead of proceeding when the configured binary for a
36
+ harness identifies itself as a *different* known harness. Delegate re-probes
37
+ `--version` when a cached capability record has refused a run, and during
38
+ `capabilities refresh` and `setup` (see Fixed); when that banner positively
39
+ names another harness — a Codex path replaced in place by Grok, say — the run
40
+ stops with `harness_identity_mismatch` and exit code 2, naming the configured
41
+ harness, the harness that answered, and the selector path.
42
+ Previously the launch continued against that same binary with the same Codex
43
+ argv, sandbox flags, and approval policy, and only the cached capability
44
+ metadata was discarded. This fires solely on a banner that NAMES another known
45
+ harness, never on uncertainty: a probe that errors, times out, exceeds its
46
+ output bound, or prints an unbranded banner still fails open and launches on
47
+ the cached record. An unbranded shape — a bare date-and-hash build ID is the
48
+ clearest case — is deliberately not identification, because wrapper prefixes
49
+ are a supported configuration and refusing a legitimate launch costs more than
50
+ carrying a stale record until the next refresh. The selector is scrubbed of
51
+ credentials before it reaches the message or the diagnostics, since a wrapper
52
+ prefix can carry a token inline or after a flag. Dry runs do not probe and are
53
+ unaffected. The refusal also names the discovery cache file for the active
54
+ auth profile, in its `cachePath` diagnostic and in a next action, because
55
+ `capabilities refresh` cannot clear this state: a refresh keeps the
56
+ last-known-good record whenever a probe fails, which is correct for a flaky
57
+ probe and wrong here, where the record itself is the problem. Without the
58
+ path, a wrong identification would refuse every launch with no documented way
59
+ out short of editing configuration.
60
+ - A run whose cached capability record is discarded for version drift now says
61
+ so, naming `delegate capabilities refresh <engine>`. The record is dropped for
62
+ that run only and the superseded copy stays on disk, so every later launch
63
+ repeats the drop and loses the same discovery-sourced models and reasoning
64
+ levels — previously with no indication that a stale cache was the cause.
65
+ Selector drift stays silent, since `capabilities` already reports it under
66
+ `driftedHarnesses`.
67
+
68
+ ### Fixed
69
+
70
+ - Discovery now detects a harness that was upgraded in place. Cached capability
71
+ records were invalidated only by comparing the configured selector, so an
72
+ upgrade behind an unchanged selector left stale capabilities cached
73
+ indefinitely and a newly supported reasoning level was rejected until a manual
74
+ `capabilities refresh`. Delegate now re-probes `--version` for the single
75
+ harness it is about to use and compares it against the version already stored
76
+ in the record, then rebuilds the run against the refreshed picture. The probe
77
+ runs only once the cached record has already cost the run something, never on
78
+ the ordinary path, which therefore spawns no extra process at all — and that
79
+ matters because the probe costs 26-668ms, worst on the Node and Bun harnesses
80
+ that are already slow to start. Two things trigger it: an explicit
81
+ `--reasoning-effort` the record refuses, and a `defaultReasoningEffort` the
82
+ record silently drops. The second is the one most likely to go unnoticed,
83
+ since a configured default degrades to a warning rather than failing the
84
+ launch, and it is set once and then never typed again. A probe that cannot
85
+ repair the default leaves the run exactly as it was: a launch that succeeded
86
+ is never turned into a failure. The check fails open — a probe that errors,
87
+ times out, or prints a banner nothing recognizes keeps the cached record, so a
88
+ failing probe can never prevent a launch. A banner that positively identifies
89
+ a *different* known harness is the exception, and it aborts the launch rather
90
+ than merely invalidating the record — see Changed. Each banner is matched
91
+ against the expected harness's own pattern before anyone else's, so a banner
92
+ mentioning more than one tool is read as the harness that was asked for
93
+ instead of whichever pattern happens to be declared earlier. Banners are read
94
+ with escape sequences stripped, so a colorized version line no longer freezes
95
+ a record permanently. A cached selector whose binary no longer exists is also
96
+ treated as drift.
97
+ - Not covered, deliberately: a harness that *removes* a capability leaves its
98
+ cached record listing more than the harness now supports, which produces no
99
+ refusal and so no probe. The child rejects the effort it was handed, which is
100
+ loud; the one quiet case is Cursor's route table aiming a run at a superseded
101
+ model id. Catching either would cost a probe on every launch, which is the
102
+ trade this path exists to refuse. `capabilities refresh` remains the cure.
103
+ - A dry run never probes the harness for its version. Dry run documents that it
104
+ neither launches a child runtime nor requires the real child binary, and the
105
+ drift probe is a child runtime; it now runs only on a real launch, leaving dry
106
+ run with the two filesystem-local checks.
107
+ - A discovery cache carrying a newer schema than the running build is no longer
108
+ discarded and overwritten. It is left intact while the run degrades to
109
+ probing without persisting, so an older Delegate can no longer destroy a newer
110
+ cache. Losing that race mid-probe degrades the same way rather than failing the
111
+ command: `capabilities refresh` and `setup` both report `cacheWriteSkipped`,
112
+ `setup` additionally leaving `cacheState` at `unchanged`, and each exits
113
+ successfully, since only persistence was lost. The
114
+ schema is re-read immediately before the atomic replacement rather than before
115
+ the snapshot is serialized, which narrows the window in which a newer cache
116
+ could be published and then overwritten down to the `os.replace` call itself.
117
+ POSIX offers no compare-and-swap for a rename, so that last window remains.
118
+ - The test suite no longer writes into the developer's real
119
+ `~/.delegate/worktrees`. Tests exercising persistent-worktree runs created
120
+ real pooled worktrees over temporary source repositories and orphaned them
121
+ permanently, because `worktrees_data_home` falls through to the user's home
122
+ directory with no seam a test could redirect. `HOME` is now redirected at test
123
+ collection time, which reaches every such resolver at once.
124
+
8
125
  ## [0.20.0] - 2026-07-25
9
126
 
10
127
  ### Added
@@ -631,6 +748,7 @@ Usage-audit fix wave: 82 sessions and 1,241 delegate invocations from one week o
631
748
 
632
749
  - Releases before 0.1.3 predate this changelog.
633
750
 
751
+ [0.21.0]: https://github.com/treygoff24/delegate-agent/compare/v0.20.0...v0.21.0
634
752
  [0.20.0]: https://github.com/treygoff24/delegate-agent/compare/v0.19.0...v0.20.0
635
753
  [0.19.0]: https://github.com/treygoff24/delegate-agent/compare/v0.18.0...v0.19.0
636
754
  [0.18.0]: https://github.com/treygoff24/delegate-agent/compare/v0.17.0...v0.18.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: delegate-agent-cli
3
- Version: 0.20.0
3
+ Version: 0.21.0
4
4
  Summary: A tiny CLI for delegating bounded agent tasks across coding-agent runtimes.
5
5
  Author: Trey Goff
6
6
  License-Expression: MIT
@@ -803,7 +803,31 @@ records independently. If one current probe fails, its last-known-good record
803
803
  is retained and the refresh response lists that harness in `staleHarnesses`.
804
804
  If the configured executable selector changes, cached reads mark that harness
805
805
  stale and ordinary launches ignore only that harness's record until refresh.
806
- Cached reads never run `--version` merely to test staleness.
806
+ Cached reads never run `--version` merely to test staleness, and neither does an
807
+ ordinary launch: a cached record can only be short of what the harness now
808
+ supports, never claim more, so one that satisfies the run is used as-is.
809
+
810
+ A launch re-probes `--version` for its own harness only when the cached record
811
+ has already cost the run something: it refused an explicit `--reasoning-effort`,
812
+ or it silently dropped a configured `defaultReasoningEffort` (a configured
813
+ default degrades to a warning instead of failing the launch, so nothing else
814
+ would signal it). An in-place upgrade behind an unchanged selector is the reason
815
+ either can be wrong, so the probe compares the live banner against the version
816
+ stored in the record, and a mismatch drops the record and rebuilds the run
817
+ against config and bundled capabilities. If the refreshed picture cannot satisfy
818
+ a configured default either, the run proceeds as it already had — a probe never
819
+ turns a launch that succeeded into one that fails.
820
+
821
+ The reverse case is not covered: a harness that *removes* a capability leaves
822
+ its record listing more than the harness now supports, which produces no refusal
823
+ and therefore no probe. Run `capabilities refresh` after an upgrade that narrows
824
+ what a harness accepts. The result carries a warning naming
825
+ `capabilities refresh <engine>`, because the superseded record stays on disk
826
+ until a refresh rewrites it. The probe fails open — an error, a timeout, or an
827
+ unrecognized banner leaves the original refusal standing — except when the
828
+ banner positively names a *different* known harness, which fails the run with
829
+ `harness_identity_mismatch` rather than handing one harness's argv and sandbox
830
+ policy to another. Dry runs never probe at all.
807
831
 
808
832
  All automatic probes run with `shell=False`, stdin closed, a neutral temporary
809
833
  working directory, bounded output, and a timeout. They pass the active profile
@@ -1079,11 +1103,13 @@ delegate worktree show <handle>
1079
1103
  delegate worktree show --latest HARNESS
1080
1104
  delegate worktree remove <handle|--group NAME> [--discard-uncommitted] [--force-branch] [--force] [--keep-branch]
1081
1105
  delegate worktree prune [--merged] [--older-than DAYS] [--harness HARNESS] [--group NAME] [--include-detached] [--dry-run] [--discard-uncommitted] [--force-branch] [--force]
1082
- delegate worktree gc [--dry-run]
1106
+ delegate worktree gc [--dry-run] [--all] [--pool PATH]
1083
1107
  ```
1084
1108
 
1085
1109
  `worktree show --latest HARNESS` selects the latest persistent worktree for the harness, not merely the latest run overall. `worktree list` JSON includes a `summary` with status counts, registry drift counts, warning counts, `autoPruneMode`, and whether the returned operation was read-only; `summary.totalPersistentWorktrees` is always registry-wide, while `allStatusCounts` is scoped to the `--harness` / `--group` filters (pre-status-filter) and `statusCounts` to the visible entries. `worktree remove --group NAME` removes all matching persistent worktrees with the same safety checks as single-handle removal. `worktree prune --group NAME` limits prune candidates to the group. `worktree gc --dry-run` reports `wouldPruneSourceRoots` and structured orphan reasons; `worktree gc` JSON also includes `mode`, `effects`, per-entry `action`, and orphan `safeAction` fields. `gc` never deletes worktree directories.
1086
1110
 
1111
+ `worktree gc --all` additionally scans the machine-wide worktree pool (`worktrees.dataHome`, default `~/.delegate/worktrees`) and adds a `pool` object with `dataHome`, `scannedWorktrees`, `orphans`, `emptyFingerprintDirs`, and `warnings`. A pool orphan is a pooled worktree whose Git admin directory no longer serves it, classified `source_root_missing` or `worktree_metadata_missing` with the same `safeAction` vocabulary as registry orphans, plus `worktreePath`, `fingerprint`, `sourceGitRoot`, and `gitdir` (null when the backlink is absent). This is the only way to see worktrees whose source repository was deleted along with its `.delegate/` registry, so `--all` is accepted outside a Delegate registry. The pool scan removes nothing at all: orphans are reported because uncommitted work in them is undetectable once the source repository is gone, and empty fingerprint directories are reported for the same hand-clearing rather than reclaimed. Only a settled absence produces an orphan — metadata that could not be read (a symlinked, non-regular, oversized, or unreadable `.git` entry, or an admin directory that could not be inspected) is treated as live and reported in `pool.warnings`, and so is any pool entry modified within the last 15 minutes, since worktree creation writes the directory before its metadata and a concurrent scan would otherwise call a worktree being born an orphan (`worktree_unsettled`, `fingerprint_dir_unsettled`). `pool.warnings` also covers first-level directories whose names are not repository fingerprints, fingerprint directories that hold loose files rather than being empty, and unlistable fingerprint directories. `--pool PATH` scans a specific pool root instead of the configured one, which finds pools left behind by an earlier `worktrees.dataHome`; an explicit `--pool` path that is missing, not a directory, or unreadable is an `invalid_pool_root` error.
1112
+
1087
1113
  List/show entry fields include `branchMergedIntoSource` (branch graph only), `mergedIntoSource` (backward-compatible branch graph state), `fullyIntegrated` (branch merged and worktree clean), `hasUncommittedChanges`, `integrationStatus`, and `uncommittedChangesIntegrated`. `workSummary` is included on `worktree show` and run completion payloads when Delegate can inspect the worktree; `worktree list` omits the deep summary for responsiveness. Consumers that need safe retirement should require `fullyIntegrated: true` or inspect `integrationStatus`. When `integrationStatus` is `branch-merged-worktree-dirty`, merge/cherry-pick suggestions are suppressed because commit integration is already complete and only uncommitted edits remain.
1088
1114
 
1089
1115
  Unknown persistent-worktree handles return suggestions scoped to persistent
@@ -216,6 +216,28 @@ delegate worktree gc
216
216
 
217
217
  In dry-run mode, `gc` reports `wouldPruneSourceRoots` and classifies un-prunable worktrees with reasons such as `source_root_missing`, `worktree_metadata_missing`, `branch_missing`, and `detached_backlink`. Without `--dry-run`, it may update Delegate registry status (for example, marking missing paths as `missing` or inconsistent metadata as `unknown`) and may run `git worktree prune` to clean Git administrative metadata for already-missing paths, but it does not delete worktree directories. JSON output includes an `effects` object that makes those mutation boundaries explicit.
218
218
 
219
+ ## Find pooled worktrees whose repository is gone
220
+
221
+ ```bash
222
+ delegate worktree gc --all
223
+ delegate worktree gc --all --dry-run
224
+ delegate worktree gc --pool ~/.delegate/worktrees
225
+ ```
226
+
227
+ Persistent worktrees live in a machine-global pool under `worktrees.dataHome` (default `~/.delegate/worktrees`), but they are tracked in a run registry inside the source repository. Delete the source repository and its registry goes with it, leaving a pooled worktree that no per-repository `gc` can reach. `--all` finds these by walking the pool directly and reading each worktree's `.git` backlink file as text — the ordinary Git-based checks cannot classify them, because every `git` command fails inside a worktree whose repository is gone. Because a live worktree always has a live backlink, the walk is safe to run across every repository on the machine at once. `--all` works outside a Delegate registry for the same reason.
228
+
229
+ The scan adds a `pool` object to the JSON with `dataHome`, `scannedWorktrees`, `orphans`, `emptyFingerprintDirs`, and `warnings`. Each orphan carries `worktreePath`, `fingerprint`, the recovered `sourceGitRoot` (null when the layout does not reveal it), `gitdir` (null when the worktree has no `.git` backlink), a `reason`, and a `safeAction`.
230
+
231
+ A worktree counts as an orphan only when two things are settled. Its `.git` backlink must be definitely gone or definitely unusable — absent, or readable and holding no usable `gitdir:` line. And its Git admin directory must no longer serve it: the admin directory must exist and hold a `gitdir` backfile naming this worktree. Anything that merely *could not be checked* — a `.git` entry that is a symlink, a directory, a FIFO, oversized, or unreadable; an admin directory that could not be inspected — is reported as live and surfaced in `warnings` with reason `worktree_metadata_unverifiable`. A false orphan invites someone to delete real work by hand, while a false live only under-reports, so every unresolved case resolves toward live.
232
+
233
+ Absence also has to have lasted. Creating a persistent worktree is several steps — Delegate makes the fingerprint directory, then `git worktree add` creates the worktree directory, its `.git` pointer, and the admin directory's backfile — and a scan that lands inside that window sees exactly what a deleted repository leaves behind. Since the scan runs in a different process from the one creating the worktree, and often for a different repository, it has nothing to wait on and judges by age instead: a pool entry modified within the last 15 minutes is reported in `warnings` (`worktree_unsettled`, or `fingerprint_dir_unsettled` for an empty directory) and classified on a later run. An entry whose age cannot be read at all is treated the same way. This delays reporting a genuine orphan by one run at most, since a real orphan's metadata never comes back.
234
+
235
+ Only first-level directories named like a repository fingerprint (12 lowercase hex characters, as written by `plan_worktree_path`) are treated as Delegate's. The root is whatever you passed, so anything else is skipped without being descended, and is summarized in `warnings` with reason `not_a_pool_fingerprint_dir` — pointing `--pool` at an ordinary directory yields warnings, never orphans or cleanup candidates.
236
+
237
+ The pool scan removes nothing. With the source repository gone there is no way to tell whether an orphan holds uncommitted work, so removal is a deliberate manual decision — inspect the reported path, rescue anything you need, then delete it yourself. Empty fingerprint directories, left behind when a fingerprint's last worktree was removed, are reported in `emptyFingerprintDirs` for the same hand-clearing; `gc` does not remove those either. "Empty" means the directory holds nothing at all: a fingerprint directory with loose files in it is reported in `warnings` (`fingerprint_dir_not_empty`) instead. Fingerprint directories that could not be listed also appear in `warnings` rather than being reported as empty.
238
+
239
+ Use `--pool PATH` to scan a pool root that is no longer the configured `dataHome`. An explicitly named `--pool` path must exist and be a readable directory; anything else is an `invalid_pool_root` error rather than a successful scan of nothing, including a root that becomes unreadable between validation and the walk. A configured `dataHome` that does not exist yet is not an error — it just means no pool has been created — and is reported in `pool.warnings`.
240
+
219
241
  ## Security boundary
220
242
 
221
243
  Worktree isolation is source-checkout isolation, not a full sandbox. The child process may still use credentials, network access, external tools, and absolute paths according to its runtime and host permissions. See [Security model](security-model.md).
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "delegate-agent-cli"
7
- version = "0.20.0"
7
+ version = "0.21.0"
8
8
  description = "A tiny CLI for delegating bounded agent tasks across coding-agent runtimes."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -1,5 +1,5 @@
1
1
  """Delegate Agent CLI package."""
2
2
 
3
- VERSION = "0.20.0"
3
+ VERSION = "0.21.0"
4
4
 
5
5
  __all__ = ["VERSION"]
@@ -1,5 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
+ from collections.abc import Callable
3
4
  from dataclasses import dataclass
4
5
  from pathlib import Path
5
6
  from typing import TextIO
@@ -138,9 +139,12 @@ def _refresh_payload(
138
139
  *,
139
140
  profile: profiles.ProfileResolution,
140
141
  engines: tuple[str, ...] | None = None,
142
+ progress: Callable[[str], None] | None = None,
141
143
  ) -> JsonObject:
142
144
  try:
143
- result = harness_discovery.refresh_discovery(config, profile=profile, engines=engines)
145
+ result = harness_discovery.refresh_discovery(
146
+ config, profile=profile, engines=engines, progress=progress
147
+ )
144
148
  except OSError as exc:
145
149
  raise CapabilitiesError(
146
150
  "capability_refresh_failed",
@@ -215,6 +219,8 @@ def _refresh_payload(
215
219
  }
216
220
  if drifted_harnesses:
217
221
  payload["driftedHarnesses"] = drifted_harnesses
222
+ if result.get("futureSchemaCache") is True:
223
+ payload["cacheWriteSkipped"] = harness_discovery.FUTURE_SCHEMA_CACHE_WARNING
218
224
  _add_legacy_cache_fields(payload, legacy_path, reasoning_payload)
219
225
  return redaction.scrub_public_projection(payload)
220
226
 
@@ -229,6 +235,7 @@ def emit(
229
235
  profile: profiles.ProfileResolution | None = None,
230
236
  discovery: JsonObject | None = None,
231
237
  stdout: TextIO,
238
+ stderr: TextIO | None = None,
232
239
  ) -> int:
233
240
  active_profile = profile or profiles.resolve_active_profile(
234
241
  config,
@@ -236,8 +243,16 @@ def emit(
236
243
  cli_override=auth_profile_override,
237
244
  )
238
245
  if command.refresh:
246
+ # JSON callers get a clean stdout document and a quiet stderr; the
247
+ # per-harness line exists for a human watching a cold refresh.
239
248
  payload = _refresh_payload(
240
- config, workspace, profile=active_profile, engines=command.engines
249
+ config,
250
+ workspace,
251
+ profile=active_profile,
252
+ engines=command.engines,
253
+ progress=(
254
+ None if command.json_mode else harness_discovery.stderr_probe_progress(stderr)
255
+ ),
241
256
  )
242
257
  else:
243
258
  payload = capabilities_payload(
@@ -252,6 +267,9 @@ def emit(
252
267
  delegate_rendering.print_json(payload, stdout)
253
268
  else:
254
269
  print(f"reasoning capabilities: {payload['cachePath']}", file=stdout)
270
+ skipped = payload.get("cacheWriteSkipped")
271
+ if isinstance(skipped, str):
272
+ print(skipped, file=stdout)
255
273
  drifted = payload.get("driftedHarnesses")
256
274
  if isinstance(drifted, list) and drifted:
257
275
  print(
@@ -1087,6 +1087,7 @@ def main(
1087
1087
  json_mode=global_options.json_mode,
1088
1088
  auth_profile_override=global_options.auth_profile,
1089
1089
  stdout=stdout,
1090
+ stderr=stderr,
1090
1091
  )
1091
1092
 
1092
1093
  # For run --input-json, pre-read the JSON to discover config from the
@@ -1177,6 +1178,7 @@ def main(
1177
1178
  profile=discovery_profile,
1178
1179
  discovery=discovery_snapshot,
1179
1180
  stdout=stdout,
1181
+ stderr=stderr,
1180
1182
  )
1181
1183
 
1182
1184
  if parsed.subcommand in {
@@ -2013,6 +2013,8 @@ WORKTREE_OPTION_SPECS: dict[str, dict[str, WorktreeOptionSpec]] = {
2013
2013
  },
2014
2014
  "gc": {
2015
2015
  "--dry-run": ("flag", "dry_run"),
2016
+ "--all": ("flag", "all_pools"),
2017
+ "--pool": ("str", "pool"),
2016
2018
  },
2017
2019
  }
2018
2020
 
@@ -2090,6 +2092,8 @@ def parse_worktree(rest: list[str], json_mode: bool, cwd: str | None) -> ParsedC
2090
2092
  raise DelegateError(
2091
2093
  "unexpected_argument", f"worktree {action} does not accept positional arguments."
2092
2094
  )
2095
+ if action == "gc" and options.get("pool") == "":
2096
+ raise DelegateError("invalid_option_value", "worktree gc --pool requires a path.")
2093
2097
  if action == "show":
2094
2098
  if options.get("latest_harness") is not None:
2095
2099
  if positional:
@@ -1408,19 +1408,36 @@ COMMAND_SPECS: dict[str, CommandSpec] = {
1408
1408
  "worktree gc": CommandSpec(
1409
1409
  name="worktree gc",
1410
1410
  summary="Garbage-collect orphaned worktree metadata and stale registry entries.",
1411
- usage=("delegate [--cwd PATH] [--json] worktree gc [--dry-run]",),
1411
+ usage=("delegate [--cwd PATH] [--json] worktree gc [--dry-run] [--all] [--pool PATH]",),
1412
1412
  options=(
1413
1413
  OptionSpec(
1414
1414
  "--dry-run",
1415
1415
  None,
1416
1416
  "Report source roots that would be pruned and un-prunable worktree reasons without changing anything.",
1417
1417
  ),
1418
+ OptionSpec(
1419
+ "--all",
1420
+ None,
1421
+ "Also scan the machine-wide worktree pool for worktrees whose source repository is gone. Reports them; never removes them.",
1422
+ ),
1423
+ OptionSpec(
1424
+ "--pool",
1425
+ "PATH",
1426
+ "Scan this worktree pool root instead of the configured worktrees.dataHome (finds pools left behind by an earlier dataHome). The path must exist and be a readable directory.",
1427
+ ),
1418
1428
  ),
1419
1429
  examples=(
1420
1430
  "delegate worktree gc",
1421
1431
  "delegate worktree gc --dry-run",
1432
+ "delegate worktree gc --all",
1433
+ "delegate worktree gc --all --dry-run",
1434
+ ),
1435
+ notes=(
1436
+ "GC reconciles registry and Git metadata; it never removes worktree paths.",
1437
+ "--all works outside a Delegate registry, since pooled worktrees outlive the repositories that created them.",
1438
+ "Pool orphans are reported, never removed: their uncommitted state cannot be inspected once the source repository is gone.",
1439
+ "The pool scan removes nothing at all, including empty pool directories — it reports them so you can clear them yourself.",
1422
1440
  ),
1423
- notes=("GC reconciles registry and Git metadata; it never removes worktree paths.",),
1424
1441
  see_also=("worktree list", "worktree prune", "worktree remove"),
1425
1442
  unsupported_global_options=("--isolation", "--auth-profile"),
1426
1443
  ),