delegate-agent-cli 0.22.0__tar.gz → 0.24.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 (156) hide show
  1. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/CHANGELOG.md +26 -0
  2. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/PKG-INFO +9 -1
  3. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/README.md +8 -0
  4. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/docs/cli-reference.md +40 -0
  5. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/docs/delegate-workflows.md +2 -0
  6. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/docs/security-model.md +13 -0
  7. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/docs/worktrees.md +19 -0
  8. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/pyproject.toml +1 -1
  9. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/__init__.py +1 -1
  10. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/cli.py +234 -7
  11. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/cli_parser.py +208 -3
  12. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/command_help.py +91 -1
  13. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/inspection_commands.py +31 -0
  14. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/isolation.py +3 -0
  15. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/private_io.py +97 -6
  16. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/prompt_transport.py +6 -0
  17. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/rendering.py +18 -0
  18. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/request_build.py +83 -13
  19. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/request_models.py +35 -0
  20. delegate_agent_cli-0.24.0/src/delegate_agent/resume_command.py +861 -0
  21. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/run_metadata.py +3 -0
  22. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/run_registry.py +303 -30
  23. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/run_status.py +9 -4
  24. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/runner.py +63 -7
  25. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/snapshot_view.py +2 -0
  26. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/wait_cancel_commands.py +1 -1
  27. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/workflows/runtime.py +6 -2
  28. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/worktree_execution.py +18 -2
  29. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/worktree_gc.py +19 -0
  30. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/worktree_records.py +78 -7
  31. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/worktree_remove.py +31 -1
  32. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent_cli.egg-info/PKG-INFO +9 -1
  33. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent_cli.egg-info/SOURCES.txt +10 -0
  34. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_command_help.py +1 -0
  35. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_delegate_help_cli.py +1 -0
  36. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_delegate_parser.py +24 -0
  37. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_delegate_validation.py +17 -0
  38. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_inspection_commands.py +170 -1
  39. delegate_agent_cli-0.24.0/tests/test_resume_attachment.py +604 -0
  40. delegate_agent_cli-0.24.0/tests/test_resume_capture.py +186 -0
  41. delegate_agent_cli-0.24.0/tests/test_resume_inheritance.py +385 -0
  42. delegate_agent_cli-0.24.0/tests/test_resume_parser.py +54 -0
  43. delegate_agent_cli-0.24.0/tests/test_resume_projections.py +87 -0
  44. delegate_agent_cli-0.24.0/tests/test_resume_refusals.py +140 -0
  45. delegate_agent_cli-0.24.0/tests/test_resume_size_guard.py +151 -0
  46. delegate_agent_cli-0.24.0/tests/test_resume_stale.py +172 -0
  47. delegate_agent_cli-0.24.0/tests/test_resume_trust.py +301 -0
  48. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_retention.py +33 -0
  49. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_run_registry.py +164 -0
  50. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_runner_capture.py +80 -0
  51. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/CONTRIBUTING.md +0 -0
  52. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/LICENSE +0 -0
  53. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/MANIFEST.in +0 -0
  54. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/SECURITY.md +0 -0
  55. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/bin/delegate-profile-shim +0 -0
  56. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/bin/delegate.py +0 -0
  57. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/config.example.json +0 -0
  58. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/docs/agent-setup.md +0 -0
  59. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/docs/assets/delegate-agent-header.png +0 -0
  60. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/docs/configuration.md +0 -0
  61. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/docs/development.md +0 -0
  62. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/docs/live-runtime.md +0 -0
  63. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/docs/publishing-checklist.md +0 -0
  64. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/docs/troubleshooting.md +0 -0
  65. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/examples/task.claude.json +0 -0
  66. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/examples/task.codex.json +0 -0
  67. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/examples/task.cursor.json +0 -0
  68. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/examples/task.droid.json +0 -0
  69. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/examples/task.grok.json +0 -0
  70. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/examples/task.judge.json +0 -0
  71. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/examples/task.kimi.json +0 -0
  72. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/setup.cfg +0 -0
  73. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/archived_logs.py +0 -0
  74. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/argv_builders.py +0 -0
  75. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/argv_utils.py +0 -0
  76. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/bundled_models.py +0 -0
  77. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/capability_commands.py +0 -0
  78. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/child_failures.py +0 -0
  79. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/command_errors.py +0 -0
  80. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/config.py +0 -0
  81. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/config_commands.py +0 -0
  82. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/constants.py +0 -0
  83. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/describe_payload.py +0 -0
  84. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/errors.py +0 -0
  85. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/git_utils.py +0 -0
  86. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/harness_discovery.py +0 -0
  87. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/harness_events.py +0 -0
  88. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/json_types.py +0 -0
  89. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/log_output.py +0 -0
  90. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/model_discovery.py +0 -0
  91. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/profile_commands.py +0 -0
  92. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/profile_guard.py +0 -0
  93. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/profiles.py +0 -0
  94. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/prompt_instructions.py +0 -0
  95. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/reasoning.py +0 -0
  96. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/redaction.py +0 -0
  97. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/retention.py +0 -0
  98. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/run_output_commands.py +0 -0
  99. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/safe_workspace.py +0 -0
  100. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/seatbelt.py +0 -0
  101. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/setup_commands.py +0 -0
  102. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/structured_output.py +0 -0
  103. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/workflows/__init__.py +0 -0
  104. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/workflows/commands.py +0 -0
  105. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/workflows/registry.py +0 -0
  106. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/workflows/schema.py +0 -0
  107. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/workflows/script.py +0 -0
  108. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/worktree_commands.py +0 -0
  109. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/worktree_mgmt.py +0 -0
  110. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/worktree_summary.py +0 -0
  111. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent/wsl.py +0 -0
  112. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent_cli.egg-info/dependency_links.txt +0 -0
  113. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent_cli.egg-info/entry_points.txt +0 -0
  114. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent_cli.egg-info/requires.txt +0 -0
  115. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/src/delegate_agent_cli.egg-info/top_level.txt +0 -0
  116. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_capability_commands.py +0 -0
  117. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_child_failures.py +0 -0
  118. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_codex_pure_sandbox.py +0 -0
  119. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_codex_thread_resilience.py +0 -0
  120. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_config_commands.py +0 -0
  121. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_delegate_isolation.py +0 -0
  122. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_end_to_end_tracking.py +0 -0
  123. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_engine_argv.py +0 -0
  124. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_execution_argv_and_prompt.py +0 -0
  125. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_execution_dry_run.py +0 -0
  126. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_execution_worktree_failure_cleanup.py +0 -0
  127. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_execution_worktree_preflight.py +0 -0
  128. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_execution_worktree_run.py +0 -0
  129. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_harness_discovery.py +0 -0
  130. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_harness_events.py +0 -0
  131. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_kimi_commands.py +0 -0
  132. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_model_discovery.py +0 -0
  133. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_model_selection_wave1a.py +0 -0
  134. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_model_selection_wave1b.py +0 -0
  135. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_omp_read_only_behavior.py +0 -0
  136. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_packaging.py +0 -0
  137. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_profiles.py +0 -0
  138. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_pure_call.py +0 -0
  139. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_reasoning_capabilities.py +0 -0
  140. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_safe_workspace_isolation.py +0 -0
  141. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_setup_commands.py +0 -0
  142. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_slash_passthrough.py +0 -0
  143. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_snapshot_redaction.py +0 -0
  144. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_snapshot_rendering.py +0 -0
  145. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_snapshot_run_output.py +0 -0
  146. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_snapshot_view.py +0 -0
  147. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_structured_output.py +0 -0
  148. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_tracked_timeout.py +0 -0
  149. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_utility_modules.py +0 -0
  150. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_wait_cancel_commands.py +0 -0
  151. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_wave4_launch_features.py +0 -0
  152. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_workflow_commands.py +0 -0
  153. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_worktree_list_show.py +0 -0
  154. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_worktree_prune_gc.py +0 -0
  155. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_worktree_remove.py +0 -0
  156. {delegate_agent_cli-0.22.0 → delegate_agent_cli-0.24.0}/tests/test_wsl_guardrails.py +0 -0
@@ -5,6 +5,30 @@ 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.24.0] - 2026-07-31
9
+
10
+ ### Added
11
+
12
+ - Added `delegate resume` for relaunching terminal Runs with a bounded,
13
+ untrusted continuation built from the original prompt and prior result.
14
+ Resume preserves compatible launch settings, supports explicit cross-engine
15
+ resumes, and attaches to an existing persistent worktree with a live cleanup
16
+ lease rather than creating a second worktree.
17
+ - Added private `prompt.txt` capture for new tracked Runs so their original
18
+ prompts can be resumed without placing prompt text in the Manifest or
19
+ Snapshot. Legacy records without that capture fail closed with a clear
20
+ `resume_prompt_unavailable` error.
21
+
22
+ ## [0.23.0] - 2026-07-30
23
+
24
+ ### Added
25
+
26
+ - Added `delegate runs prune` with a 30-day default age threshold, an explicit `--older-than` override, and `--dry-run` preview output. It prunes terminal Run records and their associated Registry artifacts (per-Run directory, logs, Completion Report, retained raw-log archive) without touching genuinely running Runs. Persistent-worktree Runs are skipped unless their worktree is recorded as removed or missing — unknown or unrecorded worktree status is treated as possibly present, mirroring `worktree gc` — so live worktrees never lose their Registry records. The ambient retention pass is bypassed on the prune command path so a dry run never mutates state.
27
+
28
+ ### Fixed
29
+
30
+ - Focused unittest invocations (`python3 -m unittest tests.<module>`) no longer fail on the src layout import: `tests/__init__.py` now puts `src` on `sys.path` before any test module import runs.
31
+
8
32
  ## [0.22.0] - 2026-07-26
9
33
 
10
34
  ### Changed
@@ -759,6 +783,8 @@ Usage-audit fix wave: 82 sessions and 1,241 delegate invocations from one week o
759
783
 
760
784
  - Releases before 0.1.3 predate this changelog.
761
785
 
786
+ [0.24.0]: https://github.com/treygoff24/delegate-agent/compare/v0.23.0...v0.24.0
787
+ [0.23.0]: https://github.com/treygoff24/delegate-agent/compare/v0.22.0...v0.23.0
762
788
  [0.22.0]: https://github.com/treygoff24/delegate-agent/compare/v0.21.0...v0.22.0
763
789
  [0.21.0]: https://github.com/treygoff24/delegate-agent/compare/v0.20.0...v0.21.0
764
790
  [0.20.0]: https://github.com/treygoff24/delegate-agent/compare/v0.19.0...v0.20.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: delegate-agent-cli
3
- Version: 0.22.0
3
+ Version: 0.24.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
@@ -332,6 +332,8 @@ delegate runs --recent
332
332
  delegate ps # active and stale tracked runs
333
333
  delegate snapshot <alias-or-runId>
334
334
  delegate run-output <alias-or-runId>
335
+ delegate resume <alias-or-runId> "Continue from the previous result and run the tests."
336
+ delegate runs prune --dry-run # preview GC of terminal run records older than 30 days
335
337
  ```
336
338
 
337
339
  Aliases are always numbered (`codex-1`, `cursor-2`); a **bare harness name
@@ -340,6 +342,12 @@ newest codex run and `delegate run-output codex:glm` the newest matching
340
342
  model. Envelopes echo `requestedHandle`/`resolvedHandle`/`resolutionKind` so you
341
343
  can confirm which run you addressed.
342
344
 
345
+ `resume` creates a new Run from a terminal source Run. It carries forward the
346
+ source mode and compatible launch settings, places the prior prompt and bounded
347
+ result before your new instructions, and attaches to an existing persistent
348
+ worktree instead of creating another one. Use `--dry-run` to inspect the
349
+ resolved launch without creating a Run.
350
+
343
351
  `run-output` defaults to the best available parent-facing output: a completion
344
352
  report when present, a recovered final assistant message when possible, or
345
353
  bounded stdout/stderr diagnostics. Use `--completion-report` when you want that
@@ -299,6 +299,8 @@ delegate runs --recent
299
299
  delegate ps # active and stale tracked runs
300
300
  delegate snapshot <alias-or-runId>
301
301
  delegate run-output <alias-or-runId>
302
+ delegate resume <alias-or-runId> "Continue from the previous result and run the tests."
303
+ delegate runs prune --dry-run # preview GC of terminal run records older than 30 days
302
304
  ```
303
305
 
304
306
  Aliases are always numbered (`codex-1`, `cursor-2`); a **bare harness name
@@ -307,6 +309,12 @@ newest codex run and `delegate run-output codex:glm` the newest matching
307
309
  model. Envelopes echo `requestedHandle`/`resolvedHandle`/`resolutionKind` so you
308
310
  can confirm which run you addressed.
309
311
 
312
+ `resume` creates a new Run from a terminal source Run. It carries forward the
313
+ source mode and compatible launch settings, places the prior prompt and bounded
314
+ result before your new instructions, and attaches to an existing persistent
315
+ worktree instead of creating another one. Use `--dry-run` to inspect the
316
+ resolved launch without creating a Run.
317
+
310
318
  `run-output` defaults to the best available parent-facing output: a completion
311
319
  report when present, a recovered final assistant message when possible, or
312
320
  bounded stdout/stderr diagnostics. Use `--completion-report` when you want that
@@ -931,17 +931,57 @@ Tracked runs return bounded parent-facing output and store local metadata under
931
931
 
932
932
  ```bash
933
933
  delegate runs [--active|--running|--stale|--recent] [--harness HARNESS] [--group NAME] [--limit N]
934
+ delegate runs prune [--older-than DAYS] [--dry-run]
934
935
  delegate ps [--harness HARNESS] [--group NAME] [--limit N]
935
936
  delegate snapshot [--latest HARNESS] [--no-redact] <handle>
936
937
  delegate run-output [--latest HARNESS] <handle> [--completion-report] [--stdout] [--stderr] [--tail N] [--max-chars N] [--raw] [--no-redact]
938
+ delegate resume [--engine ENGINE] [--model MODEL] [--reasoning-effort LEVEL] [--timeout SEC] [--dry-run] <handle> [extra instructions...]
937
939
  delegate wait <handle>... [--latest HARNESS] [--group NAME] [--timeout SEC] [--interval SEC] [--completion-report]
938
940
  delegate cancel <handle>...
939
941
  ```
940
942
 
943
+ `delegate resume` relaunches a terminal Run as a new Run. It accepts
944
+ `succeeded`, `failed`, `cancelled`, and stale Runs; an effectively running Run
945
+ is refused. A successful source requires extra instructions. The continuation
946
+ contains the original prompt, then a bounded prior-run Completion Report (or a
947
+ Snapshot digest), then the new instructions last. Prior-run content is framed
948
+ as untrusted data and is disclosed to the target Harness, including on a
949
+ cross-engine resume. Resume options must appear before the handle; trailing
950
+ tokens are continuation instructions.
951
+
952
+ The new Run inherits source settings according to this table. `--engine` and
953
+ the listed resume flags must precede the handle; `--group` and
954
+ `--auth-profile` are global flags and therefore precede `resume`. A persistent
955
+ worktree source is resumed by attaching to its existing worktree rather than
956
+ creating a second one. The attachment is a live lease: `worktree remove`,
957
+ `worktree prune`, and `worktree gc` refuse or skip the worktree while the
958
+ attached Run may still be active. `--dry-run` resolves the continuation without
959
+ creating a Run or writing a prompt record.
960
+
961
+ | Field | Source manifest key | Override flag | Absent or legacy behavior | Cross-engine rule |
962
+ | --- | --- | --- | --- | --- |
963
+ | Engine | `engine`, falling back to `harness` | `--engine` | A missing or unknown source engine refuses resume. | The selected engine becomes the target; engine-scoped fields below may drop. |
964
+ | Mode | `mode` | None; v1 does not override mode. | A missing or invalid mode refuses resume. | Retained unchanged. |
965
+ | Model selection | `modelAlias`, then `modelRequested`, `modelResolved`, then `model` | `--model` | No usable key uses the target engine configuration default and emits a note. | Source model selection drops; pass `--model` to pin a target model. |
966
+ | Reasoning effort | `requestedReasoningEffort`, then `resolvedReasoningEffort`, together with `reasoningEffortSource` | `--reasoning-effort` | Missing effort uses the target default and emits a note. A source value from configuration is re-resolved through target capability/configuration; only `cli` and `input-json` source intent is inherited directly. | Source effort drops with a note; an explicit override remains valid. |
967
+ | Fast tier | `requestedFast` | `--fast` or `--no-fast` | Omitted leaves fast unspecified. | Inherited only for a same-engine Codex resume. Other targets drop it; non-Codex targets emit a drop note when a source value is present. |
968
+ | Progress intent | `progressRequested` (`on`, `off`, or omitted) | `--progress` or `--no-progress` | Omitted uses target progress configuration and emits a note; only `on` and `off` are inherited. | Retained; progress intent is not engine-specific. |
969
+ | Timeout | `timeoutSeconds` | `--timeout` | Omitted uses the target timeout default and emits a note. A present value must be a positive integer (an integral JSON float is accepted). | Retained. |
970
+ | Output schema | Inline `outputSchema` text | `--output-schema PATH` or `--no-output-schema` | Omitted means no schema. Stored text is re-materialized privately only during normal launch. | Only Codex inherits inline schema text. Other engines soft-drop it with a note before output-schema validation. |
971
+ | OpenCode agent | `agent` | None | Omitted means no agent selection. | Inherited only for a same-engine OpenCode resume; otherwise dropped with a note. |
972
+ | Workspace cwd / execution cwd | `cwd`; persistent records also derive `executionCwd`, branch, and source Git root | None (`--cwd` selects the registry workspace and must match recorded `cwd`) | Missing `cwd` falls back to the selected registry workspace; missing persistent-worktree metadata causes attachment validation to refuse. | Retained as workspace context; an attach uses the validated existing worktree rather than copying `executionCwd` into a new worktree. |
973
+ | Isolation / lifecycle | `isolationMode`; persistent detection also reads `isolationLifecycle`, `preservedWorkspace`, `worktreeStatus`, and `worktreeAttachment` | None; `--isolation` is rejected for resume. | Missing ordinary `isolationMode` uses target isolation configuration and emits a note. | Retained. Persistent or attached sources become an `attached` execution lifecycle. |
974
+ | Group | `group` | Global `--group` | Omitted uses the ungrouped target default and emits a note. | Retained. |
975
+ | Auth profile | `authProfile` | Global `--auth-profile` | Omitted uses target profile detection/default and emits a note. | Retained. |
976
+ | Forbid commit | `commitPolicy.forbidCommit` when exactly `true` | None | Omitted or any other value leaves commit prohibition off. | Retained. |
977
+ | Include dirty | `includeDirty` | `--include-dirty` | Source and explicit values are creation-only and are dropped for ordinary resume with a note. | Retained only as a drop decision; on a persistent/attached source, explicit `--include-dirty` is rejected because attachment does not create or sync a worktree. |
978
+
941
979
  `delegate runs` defaults to recent runs. `--active` preserves the legacy active view and includes both live `running` runs and `stale` runs. Use `--running` for only live tracked processes and `--stale` for runs recorded as running whose PID is missing or dead. `--active`, `--running`, `--stale`, and `--recent` are mutually exclusive. `--group NAME` filters by launch group and the runs table shows a `group` column when any visible run has one.
942
980
  `delegate ps` is the shorter first-class form of `delegate runs --active` and
943
981
  accepts the same harness, group, and limit selectors.
944
982
 
983
+ `delegate runs prune` removes old terminal Run records from the workspace Registry so they stop accumulating forever. Only runs whose effective status is terminal (`succeeded`, `failed`, `cancelled`, or `stale` from a dead child) and whose last Registry activity is older than the threshold (default 30 days; override with `--older-than DAYS`) are eligible. Effectively running runs are always skipped, and persistent-worktree runs are skipped unless their worktree is recorded as removed or missing — pruning the record of a live worktree would orphan it from `worktree list`/`show`/`remove`. Pruning deletes the per-Run directory (Snapshot, Manifest, logs, events, Completion Report) and the retained raw-log archive; worktree paths on disk are never touched. `--dry-run` reports what would be removed without changing anything. JSON output uses schema `delegate.runs-prune.v1` with `planned`, `removed`, `skipped` (each entry carries a `reason`), and `errors` sections.
984
+
945
985
  Run-scoped handles (`snapshot`, `run-output`, `wait`, and `cancel`)
946
986
  resolve exact run IDs and numbered aliases first. A bare harness name such as
947
987
  `codex` resolves to that harness's latest run, and `harness:modelAlias` (for
@@ -175,6 +175,7 @@ engines = args.get("engines", ["codex", "claude", "cursor"])
175
175
  models = args.get("models", {})
176
176
  prompt = args["prompt"]
177
177
 
178
+
178
179
  def reviewer(engine):
179
180
  return lambda: agent(
180
181
  prompt,
@@ -184,6 +185,7 @@ def reviewer(engine):
184
185
  phase=f"Review ({engine})",
185
186
  )
186
187
 
188
+
187
189
  reviews = parallel([reviewer(engine) for engine in engines])
188
190
  return {"reviews": dict(zip(engines, reviews))}
189
191
  ```
@@ -200,6 +200,19 @@ Persistent worktree isolation is not a security sandbox. It does not prevent:
200
200
  - Writes to absolute paths outside the worktree.
201
201
  - Actions taken through authenticated tools, MCP servers, browser sessions, or external CLIs.
202
202
 
203
+ `delegate resume` reads the initial manifest and `prompt.txt` outside the Registry
204
+ lock with bounded, no-follow, single-link readers. It re-checks status and reads
205
+ the report or snapshot history inside the lock using those same readers.
206
+ Completion Reports are used only when the source Run is terminal under the lock;
207
+ otherwise resume falls back to an atomic Snapshot digest. Prompt and report text are untrusted data, not
208
+ Delegate instructions, and are disclosed to the target Harness as part of the
209
+ continuation. `prompt.txt` is verbatim, unredacted, child-tamperable, retained
210
+ until `delegate runs prune`, and an `--engine` override can disclose it to a
211
+ different provider. A persistent-worktree resume attaches to the existing path
212
+ by deriving and validating its Registry record; it does not weaken
213
+ safe/worktree path checks or create a replacement worktree when the original
214
+ has moved.
215
+
203
216
  ## Config and secret hygiene
204
217
 
205
218
  - Keep real config in `~/.delegate/config.json` or a private `DELEGATE_CONFIG` path. Repository-local `.delegate/config.json` is not loaded implicitly.
@@ -83,6 +83,25 @@ Delegate also creates a local branch named like:
83
83
  delegate/<label>-<short-run-id>
84
84
  ```
85
85
 
86
+ ### Resume attachment
87
+
88
+ To continue a terminal Run that used a persistent worktree, use the Run handle
89
+ rather than launching a second persistent worktree:
90
+
91
+ ```bash
92
+ delegate resume <alias-or-runId> "Continue the implementation and run the tests."
93
+ ```
94
+
95
+ The resumed Run attaches to the existing execution path and records an
96
+ attachment lease. It does not create a new branch, copy dirty files, or derive
97
+ a second persistent-worktree record. The lease is released when the attached
98
+ Run reaches a terminal state. Worktree removal, pruning, and registry repair
99
+ refuse or skip a path with a live attached Run; inspect or wait for that Run
100
+ before cleanup. A missing, moved, symlinked, unregistered, or branch-mismatched
101
+ source worktree is refused rather than recreated implicitly. `--include-dirty`
102
+ is rejected on attached resume because it only controls creation-time
103
+ synchronization; ordinary resume drops the flag with a note.
104
+
86
105
  ## Preflight requirements
87
106
 
88
107
  Persistent worktree work-mode runs require:
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "delegate-agent-cli"
7
- version = "0.22.0"
7
+ version = "0.24.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.22.0"
3
+ VERSION = "0.24.0"
4
4
 
5
5
  __all__ = ["VERSION"]
@@ -23,6 +23,7 @@ from delegate_agent import (
23
23
  profiles,
24
24
  reasoning,
25
25
  redaction,
26
+ resume_command,
26
27
  run_metadata,
27
28
  run_output_commands,
28
29
  run_registry,
@@ -79,7 +80,7 @@ from delegate_agent.errors import (
79
80
  EXIT_USAGE,
80
81
  DelegateError,
81
82
  )
82
- from delegate_agent.git_utils import capture_git_metadata # noqa: F401 # re-exported for tests
83
+ from delegate_agent.git_utils import capture_git_metadata # re-exported for tests
83
84
  from delegate_agent.isolation import ( # noqa: F401 # re-exported for tests
84
85
  IsolationContext,
85
86
  build_isolation_context,
@@ -100,6 +101,7 @@ from delegate_agent.prompt_transport import ( # noqa: F401 # CURSOR_PROMPT_RED
100
101
  )
101
102
  from delegate_agent.request_build import ( # noqa: F401 # re-exported for tests / back-compat
102
103
  CALL_TEMP_CWD_PLACEHOLDER,
104
+ INLINE_OUTPUT_SCHEMA_PLACEHOLDER,
103
105
  RUN_INPUT_KEYS,
104
106
  _load_input_json_object,
105
107
  build_request,
@@ -306,16 +308,28 @@ def dry_run_payload(request: Request) -> JsonObject:
306
308
  run_metadata.add_model_payload_fields(payload, request)
307
309
  reasoning.add_reasoning_payload_fields(payload, request)
308
310
  run_metadata.add_speed_payload_fields(payload, request)
311
+ if request.output_schema_text is not None:
312
+ payload["outputSchemaInline"] = True
313
+ payload["argv"] = [
314
+ "<inline output schema>" if value == INLINE_OUTPUT_SCHEMA_PLACEHOLDER else value
315
+ for value in payload["argv"]
316
+ ]
309
317
  if request.warnings:
310
318
  payload["warnings"] = list(request.warnings)
311
319
  if request.progress:
312
320
  payload["progressRequested"] = True
321
+ if request.timeout is not None:
322
+ payload["timeoutSeconds"] = request.timeout
313
323
  if request.forbid_commit:
314
324
  payload["commitPolicy"] = {"forbidCommit": True}
315
325
  if request.include_dirty:
316
326
  payload["includeDirty"] = True
317
327
  if request.group is not None:
318
328
  payload["group"] = request.group
329
+ if request.agent is not None:
330
+ payload["agent"] = request.agent
331
+ if request.resumed_from is not None:
332
+ payload["resumedFrom"] = request.resumed_from
319
333
  if request.auth_profile is not None:
320
334
  payload["authProfile"] = request.auth_profile
321
335
  if request.fallback_auth_profile is not None:
@@ -329,6 +343,8 @@ def dry_run_payload(request: Request) -> JsonObject:
329
343
  # Keep the existing human-readable `isolation` note for legacy use.
330
344
  if ctx.effective_isolation == "worktree" and ctx.isolation_lifecycle == "persistent":
331
345
  payload["isolation"] = "worktree persistent"
346
+ elif ctx.effective_isolation == "worktree" and ctx.isolation_lifecycle == "attached":
347
+ payload["isolation"] = "worktree attached (resume)"
332
348
  elif ctx.effective_isolation == "worktree":
333
349
  payload["isolation"] = "worktree temporary"
334
350
  elif ctx.effective_isolation == "none":
@@ -344,7 +360,7 @@ def dry_run_payload(request: Request) -> JsonObject:
344
360
  # Only persistent worktree isolation has a planned Delegate-managed
345
361
  # branch/path. Temporary safe-mode isolation is created ephemerally at
346
362
  # execution time and must not claim a persistent worktree plan.
347
- if ctx.isolation_lifecycle == "persistent":
363
+ if ctx.isolation_lifecycle in ("persistent", "attached"):
348
364
  planned_cwd = ctx.planned_execution_cwd or "<planned-worktree-path>"
349
365
  planned_branch = ctx.planned_branch or "<planned-branch>"
350
366
  payload["plannedExecutionCwd"] = planned_cwd
@@ -356,7 +372,11 @@ def dry_run_payload(request: Request) -> JsonObject:
356
372
  payload["plannedBranch"] = None
357
373
 
358
374
  # Always emit isolatedWorkspace as explicit boolean (mirrors preservedWorkspace).
359
- payload["isolatedWorkspace"] = ctx.isolation_lifecycle in ("temporary", "persistent")
375
+ payload["isolatedWorkspace"] = ctx.isolation_lifecycle in (
376
+ "temporary",
377
+ "persistent",
378
+ "attached",
379
+ )
360
380
  else:
361
381
  # Fallback when no isolation context is provided (e.g. direct build_request calls in tests).
362
382
  # Use embedded-default logic: safe local harnesses -> worktree temporary, others -> none.
@@ -498,10 +518,11 @@ def make_run_context(
498
518
  )
499
519
  execution_cwd = request.workspace
500
520
  # isolated_workspace must reflect the EFFECTIVE behavior, not the
501
- # mere presence of an isolation_context object. Only "temporary" or
502
- # "persistent" lifecycle means a physically separate execution workspace.
521
+ # mere presence of an isolation_context object. Only "temporary",
522
+ # "persistent", or "attached" lifecycle means a physically separate
523
+ # execution workspace.
503
524
  isolated_workspace = (
504
- request.isolation_context.isolation_lifecycle in ("temporary", "persistent")
525
+ request.isolation_context.isolation_lifecycle in ("temporary", "persistent", "attached")
505
526
  if request.isolation_context is not None
506
527
  else False
507
528
  )
@@ -575,6 +596,12 @@ def make_run_context(
575
596
  call_read_only=request.call_read_only or request.pure,
576
597
  pure=request.pure,
577
598
  prompt_instruction_mode=request.prompt_instruction_mode,
599
+ source_prompt=request.source_prompt,
600
+ progress_requested=request.progress_requested,
601
+ timeout_seconds=request.timeout,
602
+ output_schema_text=request.output_schema_record_text,
603
+ agent=request.agent,
604
+ resumed_from=request.resumed_from,
578
605
  )
579
606
 
580
607
 
@@ -626,6 +653,173 @@ def _preserve_call_artifacts(
626
653
  )
627
654
 
628
655
 
656
+ def _execute_attached_worktree(
657
+ request: Request,
658
+ json_mode: bool,
659
+ *,
660
+ config: JsonObject,
661
+ config_source: str | None,
662
+ completion_report_mode: str,
663
+ source_workspace: ResolvedWorkspace,
664
+ stdout: TextIO,
665
+ stderr: TextIO,
666
+ ) -> tuple[int, JsonObject | None]:
667
+ """Launch a resumed child inside the source run's existing worktree.
668
+
669
+ Lease-by-derivation: the attachment is recorded in this run's manifest
670
+ (``worktreeAttachment``) and removal paths refuse while the run is
671
+ effectively running. No worktree record is derived for attached runs.
672
+ """
673
+ from dataclasses import replace as dc_replace
674
+
675
+ iso = request.isolation_context
676
+ assert iso is not None and iso.isolation_lifecycle == "attached"
677
+ worktree_path = iso.planned_execution_cwd
678
+ if not worktree_path:
679
+ raise DelegateError("worktree_missing", "Attached execution has no worktree path.")
680
+ wt = Path(worktree_path)
681
+ if wt.is_symlink() or not wt.is_dir():
682
+ raise DelegateError(
683
+ "worktree_missing",
684
+ f"The source run's worktree path is no longer a directory: {worktree_path}",
685
+ )
686
+
687
+ head_probe = capture_git_metadata(worktree_path)
688
+ start_head_oid = head_probe[2]
689
+ if not start_head_oid:
690
+ raise DelegateError(
691
+ "worktree_missing",
692
+ f"Could not resolve HEAD in the attached worktree: {worktree_path}",
693
+ )
694
+
695
+ execution_request = worktree_execution._request_for_execution_workspace(request, worktree_path)
696
+ # Re-check the actual argv payload now that the worktree note (and any
697
+ # forbid-commit note) has been prepended.
698
+ resume_command.enforce_resume_prompt_size(
699
+ request.engine,
700
+ execution_request.argv[-1],
701
+ )
702
+ ensure_binary(
703
+ execution_request.argv,
704
+ engine=request.engine,
705
+ config_source=config_source,
706
+ env_overrides=request.env_overrides,
707
+ )
708
+ if request.engine == "codex":
709
+ profiles.preflight_codex_request(request, config.get("codex", {}))
710
+
711
+ registry_root = run_registry.ensure_registry(
712
+ Path(source_workspace.path),
713
+ workspace_kind=source_workspace.kind,
714
+ )
715
+ maybe_run_retention_pass(registry_root, config)
716
+ run_id, alias = run_registry.register_run(
717
+ registry_root,
718
+ harness=request.engine,
719
+ metadata={
720
+ "mode": request.mode,
721
+ "model": request.model,
722
+ "modelAlias": request.model_alias,
723
+ "modelResolved": request.model,
724
+ "group": request.group,
725
+ "workflowAgentKey": request.workflow_agent_key,
726
+ "cwd": source_workspace.path,
727
+ "worktreeAttachment": {
728
+ **(iso.attachment or {}),
729
+ "startHeadOid": start_head_oid,
730
+ },
731
+ },
732
+ )
733
+ ctx_runner = make_run_context(
734
+ registry_root,
735
+ request,
736
+ run_id=run_id,
737
+ alias=alias,
738
+ source_workspace=source_workspace,
739
+ )
740
+ attachment = {
741
+ **(iso.attachment or {}),
742
+ "startHeadOid": start_head_oid,
743
+ }
744
+ ctx_runner = dc_replace(
745
+ ctx_runner,
746
+ execution_cwd=worktree_path,
747
+ isolated_workspace=True,
748
+ branch=iso.planned_branch,
749
+ source_git_root=iso.source_git_root,
750
+ creation_context={"sourceHeadOid": start_head_oid},
751
+ worktree_attachment=attachment,
752
+ )
753
+ try:
754
+ resume_command.revalidate_attached_target(
755
+ registry_root, attachment, expected_branch=iso.planned_branch
756
+ )
757
+ except DelegateError as exc:
758
+ run_path = run_registry.run_directory(registry_root, run_id)
759
+ if ctx_runner.source_prompt is not None:
760
+ run_registry.write_private_text(
761
+ run_path / run_registry.PROMPT_TXT_FILE, ctx_runner.source_prompt
762
+ )
763
+ delegate_runner.write_manifest(
764
+ run_path,
765
+ delegate_runner.build_manifest(ctx_runner, execution_request.display_argv),
766
+ )
767
+ delegate_runner.write_state(
768
+ run_path,
769
+ delegate_runner.build_state(
770
+ ctx_runner,
771
+ status=run_registry.STATUS_FAILED,
772
+ exit_code=1,
773
+ extra={
774
+ "error": "worktree_missing",
775
+ "message": exc.message,
776
+ "failureReason": "worktree_missing",
777
+ },
778
+ ),
779
+ )
780
+ raise DelegateError("worktree_missing", exc.message) from exc
781
+ execution_root = str(Path(worktree_path).resolve(strict=False))
782
+ source_root = str(Path(source_workspace.path).resolve(strict=False))
783
+ child_env = {
784
+ **(request.env_overrides or {}),
785
+ "DELEGATE_SOURCE_ROOT": source_root,
786
+ "DELEGATE_EXECUTION_ROOT": execution_root,
787
+ "WORKSPACE_ROOT": execution_root,
788
+ }
789
+ ctx_runner = dc_replace(
790
+ ctx_runner,
791
+ env_overrides=child_env,
792
+ fallback_env_overrides=profiles.codex_fallback_child_env_overrides(
793
+ request.profile_resolution,
794
+ child_env,
795
+ ),
796
+ )
797
+ try:
798
+ return delegate_runner.execute_tracked(
799
+ execution_request.argv,
800
+ worktree_path,
801
+ ctx_runner,
802
+ json_mode=json_mode,
803
+ stdout=stdout,
804
+ stderr=stderr,
805
+ completion_report_mode=completion_report_mode,
806
+ stdin_text=execution_request.stdin_text,
807
+ prompt_file_text=execution_request.prompt_file_text,
808
+ prompt_file_placeholder=PROMPT_FILE_ARG_PLACEHOLDER,
809
+ agent_config_text=execution_request.agent_config_text,
810
+ agent_config_placeholder=DEVIN_AGENT_CONFIG_ARG_PLACEHOLDER,
811
+ output_schema_text=request.output_schema_text,
812
+ output_schema_path=request.output_schema,
813
+ manifest_argv=execution_request.display_argv,
814
+ progress=request.progress,
815
+ progress_initial_delay_sec=request.progress_initial_delay_sec,
816
+ progress_interval_sec=request.progress_interval_sec,
817
+ timeout=request.timeout,
818
+ )
819
+ except delegate_runner.RunnerLaunchError as exc:
820
+ raise DelegateError(exc.error, exc.message, exc.exit_code) from exc
821
+
822
+
629
823
  def execute_request(
630
824
  request: Request,
631
825
  json_mode: bool,
@@ -849,6 +1043,21 @@ def execute_request(
849
1043
  profiles.preflight_codex_request(request, config.get("codex", {}))
850
1044
  ctx = request.isolation_context
851
1045
 
1046
+ # Attached resume runs execute inside an existing persistent worktree:
1047
+ # never plan, create, or sync a worktree — short-circuit before the
1048
+ # persistent branch.
1049
+ if ctx is not None and ctx.isolation_lifecycle == "attached":
1050
+ return _execute_attached_worktree(
1051
+ request,
1052
+ json_mode,
1053
+ config=config,
1054
+ config_source=config_source,
1055
+ completion_report_mode=completion_report_mode,
1056
+ source_workspace=source_workspace,
1057
+ stdout=stdout,
1058
+ stderr=stderr,
1059
+ )
1060
+
852
1061
  # Persistent worktree runs (work + worktree) hand off to worktree_execution.
853
1062
  if ctx is not None and ctx.isolation_lifecycle == "persistent":
854
1063
  try:
@@ -875,6 +1084,10 @@ def execute_request(
875
1084
 
876
1085
  with safe_isolated_request(request) as isolated_request:
877
1086
  _set_child_root_env(isolated_request, source_workspace)
1087
+ if isolated_request.resumed_from is not None and isolated_request.argv:
1088
+ resume_command.enforce_resume_prompt_size(
1089
+ isolated_request.engine, isolated_request.argv[-1]
1090
+ )
878
1091
  ensure_binary(
879
1092
  isolated_request.argv,
880
1093
  engine=isolated_request.engine,
@@ -1180,7 +1393,12 @@ def main(
1180
1393
  "workflow",
1181
1394
  }:
1182
1395
  existing_registry = run_registry.registry_root_if_exists(Path(workspace.path))
1183
- if existing_registry is not None:
1396
+ pruning_runs = (
1397
+ parsed.subcommand == "runs"
1398
+ and parsed.runs is not None
1399
+ and parsed.runs.action == "prune"
1400
+ )
1401
+ if existing_registry is not None and not pruning_runs:
1184
1402
  maybe_run_retention_pass(existing_registry, config)
1185
1403
  if parsed.subcommand == "snapshot":
1186
1404
  return emit_snapshot(parsed, workspace, stdout)
@@ -1200,7 +1418,16 @@ def main(
1200
1418
  if parsed.subcommand == "profiles":
1201
1419
  return emit_profiles_command(parsed, config, source, stdout)
1202
1420
 
1421
+ resume_plan: resume_command.ResumePlan | None = None
1422
+ if parsed.subcommand == "resume":
1423
+ resume_plan = resume_command.build_resume_plan(parsed, workspace, config, stderr=stderr)
1424
+ # The plan is a fully ordinary launch; from here on the resume flows
1425
+ # through the normal request build and execution path.
1426
+ parsed = resume_plan.parsed
1427
+ global_options = parsed.global_options
1203
1428
  request = request_from_parsed(parsed, config, stdin)
1429
+ if resume_plan is not None:
1430
+ request = resume_command.apply_resume_to_request(request, resume_plan)
1204
1431
  if request.dry_run:
1205
1432
  payload = dry_run_payload(request)
1206
1433
  if global_options.json_mode: