delegate-agent-cli 0.26.0__tar.gz → 0.27.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 (190) hide show
  1. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/CHANGELOG.md +27 -0
  2. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/PKG-INFO +1 -1
  3. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/docs/cli-reference.md +1 -1
  4. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/pyproject.toml +1 -1
  5. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/__init__.py +1 -1
  6. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/argv_builders.py +0 -18
  7. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/cli.py +63 -106
  8. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/cli_parser.py +43 -64
  9. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/harness_discovery.py +31 -0
  10. delegate_agent_cli-0.27.0/src/delegate_agent/mail.py +199 -0
  11. delegate_agent_cli-0.26.0/src/delegate_agent/mail.py → delegate_agent_cli-0.27.0/src/delegate_agent/mail_core.py +148 -593
  12. delegate_agent_cli-0.27.0/src/delegate_agent/mail_push.py +600 -0
  13. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/profiles.py +2 -5
  14. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/request_build.py +10 -17
  15. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/request_models.py +23 -1
  16. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/runner.py +56 -30
  17. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/worktree_execution.py +26 -50
  18. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent_cli.egg-info/PKG-INFO +1 -1
  19. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent_cli.egg-info/SOURCES.txt +2 -0
  20. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_harness_discovery.py +18 -0
  21. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_mail_bounds.py +66 -0
  22. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_mail_contract.py +76 -4
  23. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_mail_gating.py +64 -1
  24. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_mail_prune.py +32 -0
  25. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_mail_push_adapters.py +2 -4
  26. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_resume_attachment.py +3 -0
  27. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/CONTRIBUTING.md +0 -0
  28. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/LICENSE +0 -0
  29. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/MANIFEST.in +0 -0
  30. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/README.md +0 -0
  31. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/SECURITY.md +0 -0
  32. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/bin/delegate-profile-shim +0 -0
  33. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/bin/delegate.py +0 -0
  34. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/config.example.json +0 -0
  35. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/docs/agent-setup.md +0 -0
  36. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/docs/assets/delegate-agent-header.png +0 -0
  37. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/docs/configuration.md +0 -0
  38. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/docs/delegate-workflows.md +0 -0
  39. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/docs/development.md +0 -0
  40. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/docs/live-runtime.md +0 -0
  41. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/docs/publishing-checklist.md +0 -0
  42. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/docs/security-model.md +0 -0
  43. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/docs/troubleshooting.md +0 -0
  44. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/docs/worktrees.md +0 -0
  45. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/examples/task.claude.json +0 -0
  46. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/examples/task.codex.json +0 -0
  47. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/examples/task.cursor.json +0 -0
  48. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/examples/task.droid.json +0 -0
  49. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/examples/task.grok.json +0 -0
  50. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/examples/task.judge.json +0 -0
  51. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/examples/task.kimi.json +0 -0
  52. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/setup.cfg +0 -0
  53. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/archived_logs.py +0 -0
  54. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/argv_utils.py +0 -0
  55. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/bundled_models.py +0 -0
  56. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/capability_commands.py +0 -0
  57. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/child_failures.py +0 -0
  58. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/command_errors.py +0 -0
  59. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/command_help.py +0 -0
  60. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/config.py +0 -0
  61. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/config_commands.py +0 -0
  62. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/constants.py +0 -0
  63. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/describe_payload.py +0 -0
  64. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/errors.py +0 -0
  65. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/git_utils.py +0 -0
  66. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/harness_events.py +0 -0
  67. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/inspection_commands.py +0 -0
  68. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/isolation.py +0 -0
  69. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/json_types.py +0 -0
  70. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/log_output.py +0 -0
  71. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/model_discovery.py +0 -0
  72. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/personas.py +0 -0
  73. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/private_io.py +0 -0
  74. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/profile_commands.py +0 -0
  75. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/profile_guard.py +0 -0
  76. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/prompt_instructions.py +0 -0
  77. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/prompt_transport.py +0 -0
  78. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/reasoning.py +0 -0
  79. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/redaction.py +0 -0
  80. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/rendering.py +0 -0
  81. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/resume_command.py +0 -0
  82. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/retention.py +0 -0
  83. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/run_metadata.py +0 -0
  84. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/run_output_commands.py +0 -0
  85. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/run_registry.py +0 -0
  86. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/run_status.py +0 -0
  87. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/safe_workspace.py +0 -0
  88. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/seatbelt.py +0 -0
  89. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/setup_commands.py +0 -0
  90. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/snapshot_view.py +0 -0
  91. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/structured_output.py +0 -0
  92. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/wait_cancel_commands.py +0 -0
  93. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/workflows/__init__.py +0 -0
  94. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/workflows/commands.py +0 -0
  95. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/workflows/registry.py +0 -0
  96. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/workflows/runtime.py +0 -0
  97. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/workflows/schema.py +0 -0
  98. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/workflows/script.py +0 -0
  99. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/worktree_commands.py +0 -0
  100. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/worktree_gc.py +0 -0
  101. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/worktree_mgmt.py +0 -0
  102. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/worktree_records.py +0 -0
  103. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/worktree_remove.py +0 -0
  104. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/worktree_summary.py +0 -0
  105. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent/wsl.py +0 -0
  106. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent_cli.egg-info/dependency_links.txt +0 -0
  107. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent_cli.egg-info/entry_points.txt +0 -0
  108. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent_cli.egg-info/requires.txt +0 -0
  109. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/src/delegate_agent_cli.egg-info/top_level.txt +0 -0
  110. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_capability_commands.py +0 -0
  111. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_child_failures.py +0 -0
  112. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_codex_pure_sandbox.py +0 -0
  113. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_codex_thread_resilience.py +0 -0
  114. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_command_help.py +0 -0
  115. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_config_commands.py +0 -0
  116. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_delegate_help_cli.py +0 -0
  117. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_delegate_isolation.py +0 -0
  118. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_delegate_parser.py +0 -0
  119. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_delegate_validation.py +0 -0
  120. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_end_to_end_tracking.py +0 -0
  121. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_engine_argv.py +0 -0
  122. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_execution_argv_and_prompt.py +0 -0
  123. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_execution_dry_run.py +0 -0
  124. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_execution_worktree_failure_cleanup.py +0 -0
  125. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_execution_worktree_preflight.py +0 -0
  126. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_execution_worktree_run.py +0 -0
  127. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_harness_events.py +0 -0
  128. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_inspection_commands.py +0 -0
  129. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_kimi_commands.py +0 -0
  130. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_mail_delivery.py +0 -0
  131. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_mail_eligibility.py +0 -0
  132. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_mail_framing.py +0 -0
  133. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_mail_identity.py +0 -0
  134. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_mail_io.py +0 -0
  135. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_mail_parser.py +0 -0
  136. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_mail_push.py +0 -0
  137. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_mail_push_batch.py +0 -0
  138. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_mail_push_cursor.py +0 -0
  139. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_mail_push_gating.py +0 -0
  140. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_mail_push_seams.py +0 -0
  141. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_mail_reply.py +0 -0
  142. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_mail_resolution.py +0 -0
  143. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_mail_rules.py +0 -0
  144. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_model_discovery.py +0 -0
  145. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_model_selection_wave1a.py +0 -0
  146. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_model_selection_wave1b.py +0 -0
  147. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_omp_read_only_behavior.py +0 -0
  148. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_packaging.py +0 -0
  149. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_persona_capability.py +0 -0
  150. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_persona_claude.py +0 -0
  151. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_persona_contract.py +0 -0
  152. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_persona_framer.py +0 -0
  153. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_persona_opencode.py +0 -0
  154. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_persona_parser.py +0 -0
  155. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_persona_records.py +0 -0
  156. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_persona_resolution.py +0 -0
  157. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_persona_size_guard.py +0 -0
  158. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_persona_validation.py +0 -0
  159. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_persona_workflow.py +0 -0
  160. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_profiles.py +0 -0
  161. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_pure_call.py +0 -0
  162. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_reasoning_capabilities.py +0 -0
  163. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_resume_capture.py +0 -0
  164. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_resume_inheritance.py +0 -0
  165. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_resume_parser.py +0 -0
  166. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_resume_projections.py +0 -0
  167. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_resume_refusals.py +0 -0
  168. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_resume_size_guard.py +0 -0
  169. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_resume_stale.py +0 -0
  170. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_resume_trust.py +0 -0
  171. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_retention.py +0 -0
  172. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_run_registry.py +0 -0
  173. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_runner_capture.py +0 -0
  174. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_safe_workspace_isolation.py +0 -0
  175. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_setup_commands.py +0 -0
  176. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_slash_passthrough.py +0 -0
  177. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_snapshot_redaction.py +0 -0
  178. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_snapshot_rendering.py +0 -0
  179. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_snapshot_run_output.py +0 -0
  180. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_snapshot_view.py +0 -0
  181. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_structured_output.py +0 -0
  182. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_tracked_timeout.py +0 -0
  183. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_utility_modules.py +0 -0
  184. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_wait_cancel_commands.py +0 -0
  185. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_wave4_launch_features.py +0 -0
  186. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_workflow_commands.py +0 -0
  187. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_worktree_list_show.py +0 -0
  188. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_worktree_prune_gc.py +0 -0
  189. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_worktree_remove.py +0 -0
  190. {delegate_agent_cli-0.26.0 → delegate_agent_cli-0.27.0}/tests/test_wsl_guardrails.py +0 -0
@@ -7,6 +7,32 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.27.0] - 2026-08-01
11
+
12
+ ### Changed
13
+
14
+ - Split the mail module into `mail_core` (mailbox) and `mail_push` (hook
15
+ protocol) behind a stable `mail` facade; extracted the runner's mail
16
+ sentinel capture and push finalization; unified the three launch seams'
17
+ mail preparation in one helper with ownership transfer kept at the seams;
18
+ `parse_prompt_tail` returns a named, tuple-compatible `PromptTail`.
19
+ - Mail-disabled launches no longer touch the filesystem for mail (lanes keep
20
+ their identity and storage materializes lazily on first send); workspace
21
+ resolution runs once per launch; the discovery cache is memoized per
22
+ process; the directory retry baseline is captured only when a codex
23
+ fallback profile is configured; watch sleeps are bounded by the remaining
24
+ deadline; non-once watches dedupe emitted messages and skip unchanged
25
+ inboxes; mail bodies stream with a bounded read.
26
+ - All mail subcommands render prose in human mode (watch keeps its
27
+ documented NDJSON contract); JSON output is byte-unchanged.
28
+
29
+ ### Removed
30
+
31
+ - Dead code from the framer migration and mail rounds: unused safe-prefix
32
+ helpers, the write-only `prompt_framed` field, and unreferenced mail
33
+ helpers; development-history comments replaced with durable behavior
34
+ statements.
35
+
10
36
  ## [0.26.0] - 2026-08-01
11
37
 
12
38
  ### Added
@@ -862,6 +888,7 @@ Usage-audit fix wave: 82 sessions and 1,241 delegate invocations from one week o
862
888
 
863
889
  - Releases before 0.1.3 predate this changelog.
864
890
 
891
+ [0.27.0]: https://github.com/treygoff24/delegate-agent/compare/v0.26.0...v0.27.0
865
892
  [0.26.0]: https://github.com/treygoff24/delegate-agent/compare/v0.25.0...v0.26.0
866
893
  [0.25.0]: https://github.com/treygoff24/delegate-agent/compare/v0.24.0...v0.25.0
867
894
  [0.24.0]: https://github.com/treygoff24/delegate-agent/compare/v0.23.0...v0.24.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: delegate-agent-cli
3
- Version: 0.26.0
3
+ Version: 0.27.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
@@ -1013,7 +1013,7 @@ delegate runs prune [--older-than DAYS] [--dry-run]
1013
1013
  delegate ps [--harness HARNESS] [--group NAME] [--limit N]
1014
1014
  delegate snapshot [--latest HARNESS] [--no-redact] <handle>
1015
1015
  delegate run-output [--latest HARNESS] <handle> [--completion-report] [--stdout] [--stderr] [--tail N] [--max-chars N] [--raw] [--no-redact]
1016
- delegate resume [--engine ENGINE] [--model MODEL] [--reasoning-effort LEVEL] [--timeout SEC] [--dry-run] <handle> [extra instructions...]
1016
+ delegate resume [--engine ENGINE] [--model MODEL] [--reasoning-effort LEVEL] [--fast|--no-fast] [--progress|--no-progress] [--timeout SEC] [--output-schema PATH|--no-output-schema] [--include-dirty] [--persona NAME|--no-persona] [--allow-repo-persona] [--mail-push] [--dry-run] <handle> [extra instructions...]
1017
1017
  delegate wait <handle>... [--latest HARNESS] [--group NAME] [--timeout SEC] [--interval SEC] [--completion-report]
1018
1018
  delegate cancel <handle>...
1019
1019
  ```
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "delegate-agent-cli"
7
- version = "0.26.0"
7
+ version = "0.27.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.26.0"
3
+ VERSION = "0.27.0"
4
4
 
5
5
  __all__ = ["VERSION"]
@@ -15,7 +15,6 @@ from delegate_agent import reasoning
15
15
  from delegate_agent.constants import MODE_CALL, MODE_SAFE, MODE_WORK, validate_mode
16
16
  from delegate_agent.errors import DelegateError
17
17
  from delegate_agent.json_types import JsonObject
18
- from delegate_agent.prompt_instructions import SKILL_REVIEW_PREFIX
19
18
  from delegate_agent.prompt_transport import (
20
19
  CURSOR_PROMPT_REDACTION,
21
20
  DEVIN_AGENT_CONFIG_ARG_PLACEHOLDER,
@@ -108,23 +107,6 @@ def prefix_cursor_safe_prompt(prompt: str) -> str:
108
107
  return _prefix_safe_prompt(prompt, "cursor")
109
108
 
110
109
 
111
- def prefix_kimi_safe_prompt(prompt: str) -> str:
112
- return _prefix_safe_prompt(prompt, "kimi")
113
-
114
-
115
- def prefix_droid_safe_prompt(prompt: str) -> str:
116
- safe_prefix = SAFE_REVIEW_PREFIX_BY_ENGINE["droid"]
117
- if prompt.startswith(safe_prefix):
118
- return prompt
119
- skill_prefix = SKILL_REVIEW_PREFIX
120
- if prompt.startswith(skill_prefix):
121
- insert_at = len(skill_prefix)
122
- if prompt[insert_at:].startswith(safe_prefix):
123
- return prompt
124
- return prompt[:insert_at] + safe_prefix + prompt[insert_at:]
125
- return f"{safe_prefix}{prompt}"
126
-
127
-
128
110
  def _harness_bypass_enabled(config: JsonObject, mode: str, engine: str) -> bool:
129
111
  """Return true only for explicit harness-scoped bypass policy.
130
112
 
@@ -783,7 +783,7 @@ def _execute_attached_worktree(
783
783
  workspace_kind=source_workspace.kind,
784
784
  )
785
785
  maybe_run_retention_pass(registry_root, config)
786
- if request.mode == MODE_WORK:
786
+ if request.mode == MODE_WORK and delegate_config.mail_enabled(config):
787
787
  mail.prepare_mail_storage(registry_root)
788
788
  mail.sanitize_inherited_mail_identity(request.env_overrides)
789
789
  run_id, alias = run_registry.register_run(
@@ -807,39 +807,27 @@ def _execute_attached_worktree(
807
807
  if request.mode == MODE_WORK:
808
808
  mail.bind_mail_identity(child_env, run_id, alias)
809
809
  request.env_overrides = child_env
810
- provision: mail.MailPushProvision | None = None
811
- if request.mode == MODE_WORK:
812
- wired_argv, wired_display_argv = mail.wire_work_mail_launch(
813
- request.engine,
814
- execution_request.argv,
815
- execution_request.display_argv,
816
- registry_root,
817
- prompt=request.prompt,
818
- prompt_transport=request.prompt_transport,
819
- stderr=stderr,
820
- isolated_workspace=True,
821
- )
822
- execution_request = dc_replace(
823
- execution_request, argv=wired_argv, display_argv=wired_display_argv
824
- )
825
- if request.mail_push:
826
- provision = mail.provision_mail_push(
827
- request.engine,
828
- execution_request.argv,
829
- execution_request.display_argv,
830
- registry_root,
831
- run_id,
832
- request.env_overrides or {},
833
- profiles.codex_fallback_child_env_overrides(
834
- request.profile_resolution, request.env_overrides or {}
835
- ),
836
- )
837
- if provision.warning is not None:
838
- request.warnings = (*request.warnings, provision.warning)
839
- print(f"delegate mail: WARNING: {provision.warning}", file=stderr)
840
- execution_request = dc_replace(
841
- execution_request, argv=provision.argv, display_argv=provision.display_argv
842
- )
810
+ mail_launch = mail.prepare_work_mail_launch(
811
+ enabled=request.mode == MODE_WORK and delegate_config.mail_enabled(config),
812
+ mail_push=request.mail_push,
813
+ engine=request.engine,
814
+ argv=execution_request.argv,
815
+ display_argv=execution_request.display_argv,
816
+ registry_root=registry_root,
817
+ run_id=run_id,
818
+ env_overrides=request.env_overrides,
819
+ profile_resolution=request.profile_resolution,
820
+ prompt=request.prompt,
821
+ prompt_transport=request.prompt_transport,
822
+ stderr=stderr,
823
+ isolated_workspace=True,
824
+ warnings=request.warnings,
825
+ )
826
+ execution_request = dc_replace(
827
+ execution_request, argv=mail_launch.argv, display_argv=mail_launch.display_argv
828
+ )
829
+ request.warnings = mail_launch.request_warnings
830
+ provision = mail_launch.provision
843
831
  ctx_runner = make_run_context(
844
832
  registry_root,
845
833
  request,
@@ -850,22 +838,10 @@ def _execute_attached_worktree(
850
838
  if provision is not None:
851
839
  ctx_runner = dc_replace(
852
840
  ctx_runner,
853
- env_overrides=(
854
- dict(provision.env)
855
- if provision.warning is not None and provision.env is not None
856
- else ctx_runner.env_overrides
857
- ),
858
- fallback_env_overrides=mail.mail_push_fallback_env_overrides(
859
- provision, ctx_runner.fallback_env_overrides, registry_root, run_id
860
- ),
861
- warnings=tuple(
862
- dict.fromkeys(
863
- (*ctx_runner.warnings, *((provision.warning,) if provision.warning else ()))
864
- )
841
+ **mail_launch.context_updates(
842
+ ctx_runner.env_overrides, ctx_runner.fallback_env_overrides, ctx_runner.warnings
865
843
  ),
866
844
  )
867
- if provision.warning is not None and provision.env is not None:
868
- ctx_runner = dc_replace(ctx_runner, env_overrides=dict(provision.env))
869
845
  attachment = {
870
846
  **(iso.attachment or {}),
871
847
  "startHeadOid": start_head_oid,
@@ -1303,7 +1279,7 @@ def execute_request(
1303
1279
  workspace_kind=source_workspace.kind,
1304
1280
  )
1305
1281
  maybe_run_retention_pass(registry_root, config)
1306
- if isolated_request.mode == MODE_WORK:
1282
+ if isolated_request.mode == MODE_WORK and delegate_config.mail_enabled(config):
1307
1283
  mail.prepare_mail_storage(registry_root)
1308
1284
  mail.sanitize_inherited_mail_identity(isolated_request.env_overrides)
1309
1285
  run_id, alias = run_registry.register_run(
@@ -1330,41 +1306,32 @@ def execute_request(
1330
1306
  provision: mail.MailPushProvision | None = None
1331
1307
  mail_push_cleanup_transferred = False
1332
1308
  try:
1333
- if isolated_request.mode == MODE_WORK:
1334
- isolated_request.argv, isolated_request.display_argv = mail.wire_work_mail_launch(
1335
- isolated_request.engine,
1336
- isolated_request.argv,
1337
- isolated_request.display_argv,
1338
- registry_root,
1339
- prompt=isolated_request.prompt,
1340
- prompt_transport=isolated_request.prompt_transport,
1341
- stderr=stderr,
1342
- isolated_workspace=bool(
1343
- isolated_request.isolation_context
1344
- and isolated_request.isolation_context.isolation_lifecycle
1345
- in ("temporary", "persistent", "attached")
1346
- ),
1347
- )
1348
- if isolated_request.mail_push:
1349
- provision = mail.provision_mail_push(
1350
- isolated_request.engine,
1351
- isolated_request.argv,
1352
- isolated_request.display_argv,
1353
- registry_root,
1354
- run_id,
1355
- isolated_request.env_overrides or {},
1356
- profiles.codex_fallback_child_env_overrides(
1357
- isolated_request.profile_resolution,
1358
- isolated_request.env_overrides or {},
1359
- ),
1360
- )
1361
- if provision.warning is not None:
1362
- isolated_request.warnings = (*isolated_request.warnings, provision.warning)
1363
- print(f"delegate mail: WARNING: {provision.warning}", file=stderr)
1364
- isolated_request.argv, isolated_request.display_argv = (
1365
- provision.argv,
1366
- provision.display_argv,
1367
- )
1309
+ mail_launch = mail.prepare_work_mail_launch(
1310
+ enabled=isolated_request.mode == MODE_WORK and delegate_config.mail_enabled(config),
1311
+ mail_push=isolated_request.mail_push,
1312
+ engine=isolated_request.engine,
1313
+ argv=isolated_request.argv,
1314
+ display_argv=isolated_request.display_argv,
1315
+ registry_root=registry_root,
1316
+ run_id=run_id,
1317
+ env_overrides=isolated_request.env_overrides,
1318
+ profile_resolution=isolated_request.profile_resolution,
1319
+ prompt=isolated_request.prompt,
1320
+ prompt_transport=isolated_request.prompt_transport,
1321
+ stderr=stderr,
1322
+ isolated_workspace=bool(
1323
+ isolated_request.isolation_context
1324
+ and isolated_request.isolation_context.isolation_lifecycle
1325
+ in ("temporary", "persistent", "attached")
1326
+ ),
1327
+ warnings=isolated_request.warnings,
1328
+ )
1329
+ isolated_request.argv, isolated_request.display_argv = (
1330
+ mail_launch.argv,
1331
+ mail_launch.display_argv,
1332
+ )
1333
+ isolated_request.warnings = mail_launch.request_warnings
1334
+ provision = mail_launch.provision
1368
1335
  ctx_runner = make_run_context(
1369
1336
  registry_root,
1370
1337
  isolated_request,
@@ -1375,25 +1342,12 @@ def execute_request(
1375
1342
  if provision is not None:
1376
1343
  ctx_runner = dc_replace(
1377
1344
  ctx_runner,
1378
- env_overrides=(
1379
- dict(provision.env)
1380
- if provision.warning is not None and provision.env is not None
1381
- else ctx_runner.env_overrides
1382
- ),
1383
- fallback_env_overrides=mail.mail_push_fallback_env_overrides(
1384
- provision, ctx_runner.fallback_env_overrides, registry_root, run_id
1385
- ),
1386
- warnings=tuple(
1387
- dict.fromkeys(
1388
- (
1389
- *ctx_runner.warnings,
1390
- *((provision.warning,) if provision.warning else ()),
1391
- )
1392
- )
1345
+ **mail_launch.context_updates(
1346
+ ctx_runner.env_overrides,
1347
+ ctx_runner.fallback_env_overrides,
1348
+ ctx_runner.warnings,
1393
1349
  ),
1394
1350
  )
1395
- if provision.warning is not None and provision.env is not None:
1396
- ctx_runner = dc_replace(ctx_runner, env_overrides=dict(provision.env))
1397
1351
  # Evaluate every argument expression BEFORE marking ownership
1398
1352
  # transferred: a raise inside an argument (public_argv below) would
1399
1353
  # otherwise skip both the CLI cleanup and the runner's guard.
@@ -1543,6 +1497,7 @@ def main(
1543
1497
  try:
1544
1498
  parsed = parse_cli(argv)
1545
1499
  global_options = parsed.global_options
1500
+ workspace: ResolvedWorkspace | None = None
1546
1501
  json_mode = global_options.json_mode
1547
1502
  profile_guard.enforce_profile_guard(parsed, stderr=stderr)
1548
1503
  if parsed.subcommand == "help":
@@ -1581,16 +1536,18 @@ def main(
1581
1536
  "call mode does not use --cwd.",
1582
1537
  )
1583
1538
  if global_options.group is not None:
1584
- config_workspace = workspace_path_for_config(global_options.cwd)
1539
+ workspace = resolve_workspace(global_options.cwd)
1540
+ config_workspace = Path(workspace.path)
1585
1541
  else:
1586
1542
  config_workspace = None
1587
1543
  else:
1588
- config_workspace = workspace_path_for_config(global_options.cwd)
1544
+ workspace = resolve_workspace(global_options.cwd)
1545
+ config_workspace = Path(workspace.path)
1589
1546
  config, source = load_config(workspace=config_workspace)
1590
1547
  validate_config(config)
1591
1548
 
1592
1549
  if parsed.subcommand == "personas":
1593
- workspace = resolve_workspace(global_options.cwd)
1550
+ workspace = workspace or resolve_workspace(global_options.cwd)
1594
1551
  return emit_personas(workspace, json_mode=global_options.json_mode, stdout=stdout)
1595
1552
 
1596
1553
  discovery_profile: profiles.ProfileResolution | None = None
@@ -1641,7 +1598,7 @@ def main(
1641
1598
  mail_workspace = mail.resolve_mail_workspace(global_options.cwd)
1642
1599
  workspace = resolve_workspace(str(mail_workspace))
1643
1600
  else:
1644
- workspace = resolve_workspace(global_options.cwd)
1601
+ workspace = workspace or resolve_workspace(global_options.cwd)
1645
1602
 
1646
1603
  if parsed.subcommand == "capabilities":
1647
1604
  command = parsed.capabilities
@@ -1714,7 +1671,7 @@ def main(
1714
1671
  # through the normal request build and execution path.
1715
1672
  parsed = resume_plan.parsed
1716
1673
  global_options = parsed.global_options
1717
- request = request_from_parsed(parsed, config, stdin, stderr)
1674
+ request = request_from_parsed(parsed, config, stdin, stderr, workspace=workspace)
1718
1675
  if resume_plan is not None:
1719
1676
  request = resume_command.apply_resume_to_request(request, resume_plan)
1720
1677
  if request.dry_run:
@@ -43,6 +43,7 @@ from delegate_agent.request_models import (
43
43
  InspectionOptions,
44
44
  LaunchOptions,
45
45
  ParsedCommand,
46
+ PromptTail,
46
47
  ResumeOptions,
47
48
  RunJsonOptions,
48
49
  )
@@ -985,27 +986,26 @@ def parse_modeless_engine(
985
986
  # prompt text (`cursor work explain --help`).
986
987
  if len(rest) >= 2 and command_help.is_help_token(rest[1]):
987
988
  return help_command(json_mode, f"{topic} call" if mode == "call" else topic)
988
- (
989
- prompt_file,
990
- output_schema,
991
- reasoning_effort,
992
- fast,
993
- progress_intent,
994
- forbid_commit,
995
- prompt_parts,
996
- json_mode,
997
- isolation,
998
- read_only,
999
- pure,
1000
- timeout,
1001
- include_dirty,
1002
- mail_push,
1003
- model,
1004
- agent,
1005
- persona,
1006
- no_persona,
1007
- allow_repo_persona,
1008
- ) = parse_prompt_tail(rest[1:], json_mode, isolation, command_prefix=[engine, mode])
989
+ tail = parse_prompt_tail(rest[1:], json_mode, isolation, command_prefix=[engine, mode])
990
+ prompt_file = tail.prompt_file
991
+ output_schema = tail.output_schema
992
+ reasoning_effort = tail.reasoning_effort
993
+ fast = tail.fast
994
+ progress_intent = tail.progress_intent
995
+ forbid_commit = tail.forbid_commit
996
+ prompt_parts = tail.prompt_parts
997
+ json_mode = tail.json_mode
998
+ isolation = tail.isolation
999
+ read_only = tail.read_only
1000
+ pure = tail.pure
1001
+ timeout = tail.timeout
1002
+ include_dirty = tail.include_dirty
1003
+ mail_push = tail.mail_push
1004
+ model = tail.model
1005
+ agent = tail.agent
1006
+ persona = tail.persona
1007
+ no_persona = tail.no_persona
1008
+ allow_repo_persona = tail.allow_repo_persona
1009
1009
  if agent is not None and engine != "opencode":
1010
1010
  raise DelegateError("unsupported_agent", "--agent is only supported by opencode.")
1011
1011
  if fast is not None and engine != "codex":
@@ -1120,27 +1120,26 @@ def parse_droid(
1120
1120
  # Help wins after the mode, before prompt capture: `droid [alias] safe --help`.
1121
1121
  if tail and command_help.is_help_token(tail[0]):
1122
1122
  return help_command(json_mode, f"{topic} call" if mode == "call" else topic)
1123
- (
1124
- prompt_file,
1125
- output_schema,
1126
- reasoning_effort,
1127
- fast,
1128
- progress_intent,
1129
- forbid_commit,
1130
- prompt_parts,
1131
- json_mode,
1132
- isolation,
1133
- read_only,
1134
- pure,
1135
- timeout,
1136
- include_dirty,
1137
- mail_push,
1138
- model,
1139
- agent,
1140
- persona,
1141
- no_persona,
1142
- allow_repo_persona,
1143
- ) = parse_prompt_tail(tail, json_mode, isolation, command_prefix=command_prefix)
1123
+ tail_result = parse_prompt_tail(tail, json_mode, isolation, command_prefix=command_prefix)
1124
+ prompt_file = tail_result.prompt_file
1125
+ output_schema = tail_result.output_schema
1126
+ reasoning_effort = tail_result.reasoning_effort
1127
+ fast = tail_result.fast
1128
+ progress_intent = tail_result.progress_intent
1129
+ forbid_commit = tail_result.forbid_commit
1130
+ prompt_parts = tail_result.prompt_parts
1131
+ json_mode = tail_result.json_mode
1132
+ isolation = tail_result.isolation
1133
+ read_only = tail_result.read_only
1134
+ pure = tail_result.pure
1135
+ timeout = tail_result.timeout
1136
+ include_dirty = tail_result.include_dirty
1137
+ mail_push = tail_result.mail_push
1138
+ model = tail_result.model
1139
+ agent = tail_result.agent
1140
+ persona = tail_result.persona
1141
+ no_persona = tail_result.no_persona
1142
+ allow_repo_persona = tail_result.allow_repo_persona
1144
1143
  if agent is not None:
1145
1144
  raise DelegateError("unsupported_agent", "--agent is only supported by opencode.")
1146
1145
  if fast is not None:
@@ -1467,27 +1466,7 @@ def parse_prompt_tail(
1467
1466
  isolation: str | None,
1468
1467
  *,
1469
1468
  command_prefix: list[str] | None = None,
1470
- ) -> tuple[
1471
- str | None,
1472
- str | None,
1473
- str | None,
1474
- bool | None,
1475
- str | None,
1476
- bool,
1477
- list[str],
1478
- bool,
1479
- str | None,
1480
- bool,
1481
- bool,
1482
- int | None,
1483
- bool,
1484
- str | None,
1485
- str | None,
1486
- str | None,
1487
- bool,
1488
- bool,
1489
- bool,
1490
- ]:
1469
+ ) -> PromptTail:
1491
1470
  prompt_file: str | None = None
1492
1471
  output_schema: str | None = None
1493
1472
  reasoning_effort: str | None = None
@@ -1793,7 +1772,7 @@ def parse_prompt_tail(
1793
1772
  raise_misplaced_global_option(
1794
1773
  "Global options must appear before the subcommand; use --prompt-file for literal flag text.",
1795
1774
  )
1796
- return (
1775
+ return PromptTail(
1797
1776
  prompt_file,
1798
1777
  output_schema,
1799
1778
  reasoning_effort,
@@ -7,6 +7,7 @@ are deliberately layered on later.
7
7
 
8
8
  from __future__ import annotations
9
9
 
10
+ import copy
10
11
  import hashlib
11
12
  import json
12
13
  import os
@@ -43,6 +44,7 @@ CONFIGURED_SELECTOR_MISSING = "configured_selector_missing"
43
44
  FUTURE_SCHEMA_CACHE_WARNING = (
44
45
  "discovery cache was written by a newer delegate; probing without reading or replacing it"
45
46
  )
47
+ _DISCOVERY_CACHE_MEMO: dict[tuple[Path, int, int], JsonObject | None] = {}
46
48
 
47
49
 
48
50
  class FutureCacheSchemaError(ValueError):
@@ -1445,6 +1447,25 @@ def discovery_cache_path(profile_name: str | None, *, home: Path | None = None)
1445
1447
  return root / f"profile-{digest}.json"
1446
1448
 
1447
1449
 
1450
+ def _clear_discovery_cache_memo(path: Path) -> None:
1451
+ for key in tuple(_DISCOVERY_CACHE_MEMO):
1452
+ if key[0] == path:
1453
+ del _DISCOVERY_CACHE_MEMO[key]
1454
+
1455
+
1456
+ def _discovery_cache_memo_key(path: Path) -> tuple[Path, int, int] | None:
1457
+ try:
1458
+ stat_result = path.stat()
1459
+ except OSError:
1460
+ _clear_discovery_cache_memo(path)
1461
+ return None
1462
+ key = (path, stat_result.st_mtime_ns, stat_result.st_size)
1463
+ for cached_key in tuple(_DISCOVERY_CACHE_MEMO):
1464
+ if cached_key[0] == path and cached_key != key:
1465
+ del _DISCOVERY_CACHE_MEMO[cached_key]
1466
+ return key
1467
+
1468
+
1448
1469
  def cache_schema_is_future(profile_name: str | None, *, home: Path | None = None) -> bool:
1449
1470
  """Report whether the on-disk cache was written by a newer delegate.
1450
1471
 
@@ -1471,9 +1492,15 @@ def load_discovery_cache(
1471
1492
  ) -> JsonObject | None:
1472
1493
  """Load a valid profile snapshot; malformed data is treated as absent."""
1473
1494
  path = discovery_cache_path(profile_name, home=home)
1495
+ memo_key = _discovery_cache_memo_key(path)
1496
+ if memo_key is None:
1497
+ return None
1498
+ if memo_key in _DISCOVERY_CACHE_MEMO:
1499
+ return copy.deepcopy(_DISCOVERY_CACHE_MEMO[memo_key])
1474
1500
  try:
1475
1501
  snapshot = private_io.read_json_object(path)
1476
1502
  if snapshot is None:
1503
+ _DISCOVERY_CACHE_MEMO[memo_key] = None
1477
1504
  return None
1478
1505
  validate_snapshot(
1479
1506
  snapshot,
@@ -1481,16 +1508,19 @@ def load_discovery_cache(
1481
1508
  allow_unknown_harnesses=True,
1482
1509
  )
1483
1510
  except (private_io.RegistryJsonError, ValueError):
1511
+ _DISCOVERY_CACHE_MEMO[memo_key] = None
1484
1512
  return None
1485
1513
 
1486
1514
  harnesses = snapshot["harnesses"]
1487
1515
  if not isinstance(harnesses, dict): # validate_snapshot already enforces this.
1516
+ _DISCOVERY_CACHE_MEMO[memo_key] = None
1488
1517
  return None
1489
1518
  if any(harness not in KNOWN_ENGINES for harness in harnesses):
1490
1519
  snapshot = dict(snapshot)
1491
1520
  snapshot["harnesses"] = {
1492
1521
  harness: record for harness, record in harnesses.items() if harness in KNOWN_ENGINES
1493
1522
  }
1523
+ _DISCOVERY_CACHE_MEMO[memo_key] = copy.deepcopy(snapshot)
1494
1524
  return snapshot
1495
1525
 
1496
1526
 
@@ -1523,6 +1553,7 @@ def write_discovery_cache(
1523
1553
  raise FutureCacheSchemaError(FUTURE_SCHEMA_CACHE_WARNING)
1524
1554
 
1525
1555
  private_io.write_json_atomic(path, snapshot, before_replace=refuse_to_replace_a_newer_cache)
1556
+ _clear_discovery_cache_memo(path)
1526
1557
  return path
1527
1558
 
1528
1559