delegate-agent-cli 0.21.0__tar.gz → 0.22.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 (146) hide show
  1. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/CHANGELOG.md +12 -0
  2. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/PKG-INFO +1 -1
  3. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/pyproject.toml +1 -1
  4. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/__init__.py +1 -1
  5. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/capability_commands.py +1 -2
  6. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/cli.py +3 -15
  7. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/cli_parser.py +8 -24
  8. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/command_errors.py +2 -0
  9. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/config.py +3 -5
  10. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/config_commands.py +7 -3
  11. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/constants.py +24 -0
  12. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/describe_payload.py +5 -14
  13. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/harness_discovery.py +8 -4
  14. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/profiles.py +1 -6
  15. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/prompt_transport.py +18 -0
  16. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/reasoning.py +2 -31
  17. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/redaction.py +5 -5
  18. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/request_build.py +33 -82
  19. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/run_registry.py +0 -2
  20. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/run_status.py +6 -10
  21. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/runner.py +42 -111
  22. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/safe_workspace.py +0 -4
  23. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/setup_commands.py +3 -12
  24. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/snapshot_view.py +2 -2
  25. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/structured_output.py +8 -7
  26. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/workflows/commands.py +12 -12
  27. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/workflows/registry.py +3 -4
  28. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/workflows/runtime.py +53 -45
  29. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/workflows/schema.py +5 -4
  30. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/workflows/script.py +8 -4
  31. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/worktree_records.py +1 -3
  32. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent_cli.egg-info/PKG-INFO +1 -1
  33. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_execution_worktree_failure_cleanup.py +0 -3
  34. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_retention.py +0 -3
  35. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/CONTRIBUTING.md +0 -0
  36. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/LICENSE +0 -0
  37. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/MANIFEST.in +0 -0
  38. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/README.md +0 -0
  39. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/SECURITY.md +0 -0
  40. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/bin/delegate-profile-shim +0 -0
  41. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/bin/delegate.py +0 -0
  42. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/config.example.json +0 -0
  43. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/docs/agent-setup.md +0 -0
  44. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/docs/assets/delegate-agent-header.png +0 -0
  45. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/docs/cli-reference.md +0 -0
  46. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/docs/configuration.md +0 -0
  47. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/docs/delegate-workflows.md +0 -0
  48. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/docs/development.md +0 -0
  49. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/docs/live-runtime.md +0 -0
  50. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/docs/publishing-checklist.md +0 -0
  51. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/docs/security-model.md +0 -0
  52. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/docs/troubleshooting.md +0 -0
  53. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/docs/worktrees.md +0 -0
  54. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/examples/task.claude.json +0 -0
  55. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/examples/task.codex.json +0 -0
  56. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/examples/task.cursor.json +0 -0
  57. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/examples/task.droid.json +0 -0
  58. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/examples/task.grok.json +0 -0
  59. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/examples/task.judge.json +0 -0
  60. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/examples/task.kimi.json +0 -0
  61. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/setup.cfg +0 -0
  62. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/archived_logs.py +0 -0
  63. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/argv_builders.py +0 -0
  64. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/argv_utils.py +0 -0
  65. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/bundled_models.py +0 -0
  66. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/child_failures.py +0 -0
  67. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/command_help.py +0 -0
  68. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/errors.py +0 -0
  69. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/git_utils.py +0 -0
  70. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/harness_events.py +0 -0
  71. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/inspection_commands.py +0 -0
  72. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/isolation.py +0 -0
  73. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/json_types.py +0 -0
  74. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/log_output.py +0 -0
  75. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/model_discovery.py +0 -0
  76. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/private_io.py +0 -0
  77. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/profile_commands.py +0 -0
  78. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/profile_guard.py +0 -0
  79. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/prompt_instructions.py +0 -0
  80. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/rendering.py +0 -0
  81. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/request_models.py +0 -0
  82. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/retention.py +0 -0
  83. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/run_metadata.py +0 -0
  84. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/run_output_commands.py +0 -0
  85. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/seatbelt.py +0 -0
  86. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/wait_cancel_commands.py +0 -0
  87. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/workflows/__init__.py +0 -0
  88. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/worktree_commands.py +0 -0
  89. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/worktree_execution.py +0 -0
  90. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/worktree_gc.py +0 -0
  91. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/worktree_mgmt.py +0 -0
  92. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/worktree_remove.py +0 -0
  93. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/worktree_summary.py +0 -0
  94. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent/wsl.py +0 -0
  95. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent_cli.egg-info/SOURCES.txt +0 -0
  96. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent_cli.egg-info/dependency_links.txt +0 -0
  97. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent_cli.egg-info/entry_points.txt +0 -0
  98. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent_cli.egg-info/requires.txt +0 -0
  99. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/src/delegate_agent_cli.egg-info/top_level.txt +0 -0
  100. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_capability_commands.py +0 -0
  101. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_child_failures.py +0 -0
  102. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_codex_pure_sandbox.py +0 -0
  103. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_codex_thread_resilience.py +0 -0
  104. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_command_help.py +0 -0
  105. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_config_commands.py +0 -0
  106. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_delegate_help_cli.py +0 -0
  107. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_delegate_isolation.py +0 -0
  108. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_delegate_parser.py +0 -0
  109. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_delegate_validation.py +0 -0
  110. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_end_to_end_tracking.py +0 -0
  111. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_engine_argv.py +0 -0
  112. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_execution_argv_and_prompt.py +0 -0
  113. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_execution_dry_run.py +0 -0
  114. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_execution_worktree_preflight.py +0 -0
  115. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_execution_worktree_run.py +0 -0
  116. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_harness_discovery.py +0 -0
  117. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_harness_events.py +0 -0
  118. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_inspection_commands.py +0 -0
  119. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_kimi_commands.py +0 -0
  120. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_model_discovery.py +0 -0
  121. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_model_selection_wave1a.py +0 -0
  122. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_model_selection_wave1b.py +0 -0
  123. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_omp_read_only_behavior.py +0 -0
  124. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_packaging.py +0 -0
  125. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_profiles.py +0 -0
  126. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_pure_call.py +0 -0
  127. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_reasoning_capabilities.py +0 -0
  128. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_run_registry.py +0 -0
  129. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_runner_capture.py +0 -0
  130. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_safe_workspace_isolation.py +0 -0
  131. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_setup_commands.py +0 -0
  132. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_slash_passthrough.py +0 -0
  133. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_snapshot_redaction.py +0 -0
  134. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_snapshot_rendering.py +0 -0
  135. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_snapshot_run_output.py +0 -0
  136. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_snapshot_view.py +0 -0
  137. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_structured_output.py +0 -0
  138. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_tracked_timeout.py +0 -0
  139. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_utility_modules.py +0 -0
  140. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_wait_cancel_commands.py +0 -0
  141. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_wave4_launch_features.py +0 -0
  142. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_workflow_commands.py +0 -0
  143. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_worktree_list_show.py +0 -0
  144. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_worktree_prune_gc.py +0 -0
  145. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_worktree_remove.py +0 -0
  146. {delegate_agent_cli-0.21.0 → delegate_agent_cli-0.22.0}/tests/test_wsl_guardrails.py +0 -0
@@ -5,6 +5,17 @@ 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.22.0] - 2026-07-26
9
+
10
+ ### Changed
11
+
12
+ - Consolidated pure-call validation in `constants.validate_pure_call`, collapsed repeated `attempt`/`call` invocations in `runner.py` into local closures, eliminated `typing.Any` from `src`, and removed defensive `getattr`/`isinstance` hedges where the contract guarantees the type.
13
+
14
+ ### Removed
15
+
16
+ - Removed the write-only `ReasoningCapability.supported_efforts` field and unused `STATUS_FILTER_ACTIVE`/`STATUS_FILTER_RECENT` constants.
17
+ - Removed dead back-compat re-exports from `delegate_agent.cli`: `build_kimi_argv`, `build_opencode_argv`, `build_pi_argv`, `prefix_droid_safe_prompt`, `redacted_prompt_argv`, `KIMI_PROMPT_REDACTION`, `_grok_harness_bypass_enabled`, `_resolve_default_model`, and `block_external_symlinks`. These had no known consumers; the supported import surface is the CLI and `delegate --json` contracts.
18
+
8
19
  ## [0.21.0] - 2026-07-26
9
20
 
10
21
  ### Added
@@ -748,6 +759,7 @@ Usage-audit fix wave: 82 sessions and 1,241 delegate invocations from one week o
748
759
 
749
760
  - Releases before 0.1.3 predate this changelog.
750
761
 
762
+ [0.22.0]: https://github.com/treygoff24/delegate-agent/compare/v0.21.0...v0.22.0
751
763
  [0.21.0]: https://github.com/treygoff24/delegate-agent/compare/v0.20.0...v0.21.0
752
764
  [0.20.0]: https://github.com/treygoff24/delegate-agent/compare/v0.19.0...v0.20.0
753
765
  [0.19.0]: https://github.com/treygoff24/delegate-agent/compare/v0.18.0...v0.19.0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: delegate-agent-cli
3
- Version: 0.21.0
3
+ Version: 0.22.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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "delegate-agent-cli"
7
- version = "0.21.0"
7
+ version = "0.22.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.21.0"
3
+ VERSION = "0.22.0"
4
4
 
5
5
  __all__ = ["VERSION"]
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- from collections.abc import Callable
4
3
  from dataclasses import dataclass
5
4
  from pathlib import Path
6
5
  from typing import TextIO
@@ -139,7 +138,7 @@ def _refresh_payload(
139
138
  *,
140
139
  profile: profiles.ProfileResolution,
141
140
  engines: tuple[str, ...] | None = None,
142
- progress: Callable[[str], None] | None = None,
141
+ progress: harness_discovery.ProgressCallback | None = None,
143
142
  ) -> JsonObject:
144
143
  try:
145
144
  result = harness_discovery.refresh_discovery(
@@ -40,20 +40,14 @@ from delegate_agent import runner as delegate_runner
40
40
  from delegate_agent.argv_builders import ( # noqa: F401 # re-exported for tests / back-compat
41
41
  SAFE_REVIEW_PREFIX_BY_ENGINE,
42
42
  _claude_harness_bypass_enabled,
43
- _grok_harness_bypass_enabled,
44
43
  build_claude_argv,
45
44
  build_codex_argv,
46
45
  build_cursor_argv,
47
46
  build_devin_argv,
48
47
  build_droid_argv,
49
48
  build_grok_argv,
50
- build_kimi_argv,
51
49
  build_omp_argv,
52
- build_opencode_argv,
53
- build_pi_argv,
54
50
  prefix_cursor_safe_prompt,
55
- prefix_droid_safe_prompt,
56
- redacted_prompt_argv,
57
51
  )
58
52
  from delegate_agent.argv_utils import public_argv
59
53
  from delegate_agent.cli_parser import ( # noqa: F401 # re-exported for tests / back-compat
@@ -98,7 +92,6 @@ from delegate_agent.prompt_transport import ( # noqa: F401 # CURSOR_PROMPT_RED
98
92
  DEVIN_AGENT_CONFIG_DISPLAY,
99
93
  DROID_PROMPT_FILE_ARG_PLACEHOLDER,
100
94
  DROID_PROMPT_FILE_DISPLAY,
101
- KIMI_PROMPT_REDACTION,
102
95
  PROMPT_FILE_ARG_PLACEHOLDER,
103
96
  PROMPT_FILE_DISPLAY,
104
97
  PROMPT_TRANSPORT_ARGV,
@@ -109,7 +102,6 @@ from delegate_agent.request_build import ( # noqa: F401 # re-exported for test
109
102
  CALL_TEMP_CWD_PLACEHOLDER,
110
103
  RUN_INPUT_KEYS,
111
104
  _load_input_json_object,
112
- _resolve_default_model,
113
105
  build_request,
114
106
  effective_prompt,
115
107
  load_config,
@@ -136,7 +128,6 @@ from delegate_agent.safe_workspace import ( # noqa: F401 # re-exported for tes
136
128
  SAFE_BLOCKED_SYMLINK_PLACEHOLDER,
137
129
  SAFE_EXTERNAL_SYMLINK_WARNING_PREFIX,
138
130
  SAFE_UNBORN_GIT_WARNING,
139
- block_external_symlinks,
140
131
  cleanup_safe_isolated_workspace,
141
132
  create_directory_safe_workspace,
142
133
  create_git_safe_workspace,
@@ -332,7 +323,6 @@ def dry_run_payload(request: Request) -> JsonObject:
332
323
  if request.profile_resolution.name is not None:
333
324
  payload["profileEnv"] = redaction.redact_env_map(request.profile_resolution.env)
334
325
 
335
- # Structured isolation fields from the isolation context.
336
326
  if request.isolation_context is not None:
337
327
  ctx = request.isolation_context
338
328
 
@@ -515,7 +505,6 @@ def make_run_context(
515
505
  if request.isolation_context is not None
516
506
  else False
517
507
  )
518
- # Extract isolation metadata from the isolation context.
519
508
  iso_ctx = request.isolation_context
520
509
  if iso_ctx is not None:
521
510
  isolation_mode = iso_ctx.isolation_mode
@@ -860,7 +849,7 @@ def execute_request(
860
849
  profiles.preflight_codex_request(request, config.get("codex", {}))
861
850
  ctx = request.isolation_context
862
851
 
863
- # --- Persistent worktree path (work + worktree) ---
852
+ # Persistent worktree runs (work + worktree) hand off to worktree_execution.
864
853
  if ctx is not None and ctx.isolation_lifecycle == "persistent":
865
854
  try:
866
855
  return worktree_execution.execute_persistent_worktree(
@@ -1022,7 +1011,6 @@ def pre_read_run_json_for_config(
1022
1011
  if json_cwd is not None and not isinstance(json_cwd, str):
1023
1012
  raise DelegateError("invalid_cwd", "cwd must be a string.")
1024
1013
 
1025
- # Reject explicit null isolation in the JSON pre-read.
1026
1014
  if "isolation" in raw and raw["isolation"] is None:
1027
1015
  raise DelegateError(
1028
1016
  "invalid_isolation",
@@ -1280,8 +1268,8 @@ def main(
1280
1268
  DelegateError(
1281
1269
  exc.error,
1282
1270
  exc.message,
1283
- diagnostics=getattr(exc, "diagnostics", None),
1284
- next_actions=getattr(exc, "next_actions", None),
1271
+ diagnostics=exc.diagnostics,
1272
+ next_actions=exc.next_actions,
1285
1273
  ),
1286
1274
  json_mode,
1287
1275
  stdout,
@@ -32,10 +32,9 @@ from delegate_agent.constants import (
32
32
  ENGINES_PROSE,
33
33
  KNOWN_ENGINES,
34
34
  MODELESS_ENGINES,
35
- PURE_CALL_ENGINES,
36
35
  VALID_MODES,
37
- pure_call_supported,
38
36
  validate_mode,
37
+ validate_pure_call,
39
38
  )
40
39
  from delegate_agent.errors import DelegateError
41
40
  from delegate_agent.request_models import (
@@ -647,10 +646,6 @@ def corrected_global_argv(argv: list[str]) -> list[str]:
647
646
  return [*globals_out, *rest]
648
647
 
649
648
 
650
- def corrected_global_command(argv: list[str]) -> str:
651
- return _shell_command(corrected_global_argv(argv))
652
-
653
-
654
649
  def raise_misplaced_global_option(message: str, argv: list[str] | None = None) -> NoReturn:
655
650
  guidance = (
656
651
  "Move global options before the subcommand "
@@ -1320,22 +1315,7 @@ def _validate_pure_options(
1320
1315
  return
1321
1316
  if mode != "call":
1322
1317
  raise DelegateError("unsupported_pure_call", "--pure only applies to call mode.")
1323
- if group is not None:
1324
- raise DelegateError(
1325
- "pure_conflicts_group",
1326
- "--pure cannot be combined with --group; pure call is a stateless one-hop completion.",
1327
- )
1328
- if read_only:
1329
- raise DelegateError(
1330
- "pure_conflicts_read_only", "--pure cannot be combined with --read-only."
1331
- )
1332
- if not pure_call_supported(engine):
1333
- supported = ", ".join(PURE_CALL_ENGINES)
1334
- raise DelegateError(
1335
- "unsupported_pure_call",
1336
- f"{engine} does not support pure call mode.",
1337
- next_actions=[f"Use --pure with one of: {supported}."],
1338
- )
1318
+ validate_pure_call(engine, pure=pure, read_only=read_only, group=group)
1339
1319
 
1340
1320
 
1341
1321
  def parse_snapshot(rest: list[str], json_mode: bool, cwd: str | None) -> ParsedCommand:
@@ -1982,6 +1962,10 @@ def _require_option_value(rest: list[str], index: int, option: str) -> str:
1982
1962
 
1983
1963
  WorktreeOptionSpec = tuple[str, str]
1984
1964
 
1965
+ # Parsed worktree option values: flags store True, "str"/"group"/"status" kinds
1966
+ # store the validated string, and the int kinds store a parsed int.
1967
+ WorktreeOptionValue = str | int | bool
1968
+
1985
1969
  WORKTREE_OPTION_SPECS: dict[str, dict[str, WorktreeOptionSpec]] = {
1986
1970
  "list": {
1987
1971
  "--harness": ("str", "harness"),
@@ -2020,7 +2004,7 @@ WORKTREE_OPTION_SPECS: dict[str, dict[str, WorktreeOptionSpec]] = {
2020
2004
 
2021
2005
 
2022
2006
  def _apply_worktree_option(
2023
- options: dict[str, object],
2007
+ options: dict[str, WorktreeOptionValue],
2024
2008
  args: list[str],
2025
2009
  index: int,
2026
2010
  option: str,
@@ -2069,7 +2053,7 @@ def parse_worktree(rest: list[str], json_mode: bool, cwd: str | None) -> ParsedC
2069
2053
  return help_command(json_mode, topic)
2070
2054
  if action not in WORKTREE_OPTION_SPECS:
2071
2055
  raise DelegateError("unknown_worktree_action", f"Unknown worktree action: {action}")
2072
- options: dict[str, object] = {}
2056
+ options: dict[str, WorktreeOptionValue] = {}
2073
2057
  positional: list[str] = []
2074
2058
  i = 0
2075
2059
  action_specs = WORKTREE_OPTION_SPECS[action]
@@ -10,6 +10,8 @@ class CommandError(Exception):
10
10
  super().__init__(message)
11
11
  self.error = error
12
12
  self.message = message
13
+ self.diagnostics: dict | None = None
14
+ self.next_actions: list[str] | None = None
13
15
 
14
16
 
15
17
  def resolve_run_target(
@@ -821,11 +821,9 @@ def _reject_opencode_flag_like_value(
821
821
  def _reject_pi_family_flag_like_value(value: JsonValue, *, path: str, error: str) -> None:
822
822
  if value is None:
823
823
  return
824
- if not isinstance(value, str) or not value.strip() or value.startswith("-"):
825
- raise ConfigError(
826
- error,
827
- f"{path} must be a non-empty string that does not start with '-'.",
828
- )
824
+ _reject_opencode_flag_like_value(value, path=path, error=error)
825
+ if not isinstance(value, str):
826
+ return
829
827
  if ":" in value:
830
828
  raise ConfigError(
831
829
  error,
@@ -23,10 +23,14 @@ class ConfigCommand:
23
23
  PROFILE_CONFIG_NAMES = delegate_config.PROFILE_CONFIG_NAMES
24
24
 
25
25
 
26
- def _target_path() -> Path:
26
+ def target_config_path() -> Path:
27
+ """Resolve the config path, rejecting Windows-style paths under WSL."""
27
28
  raw = delegate_config.config_path()
28
29
  if wsl.should_reject_windows_path(str(raw)):
29
- raise DelegateError("windows_path", wsl.windows_path_message("DELEGATE_CONFIG", str(raw)))
30
+ raise DelegateError(
31
+ "windows_path",
32
+ wsl.windows_path_message(delegate_config.CONFIG_ENV, str(raw)),
33
+ )
30
34
  return raw
31
35
 
32
36
 
@@ -94,7 +98,7 @@ def _validated_effective_config(config: JsonObject) -> JsonObject:
94
98
  def emit(command: ConfigCommand, stdout: TextIO) -> int:
95
99
  if command.action not in {"init", "sync-profiles"}:
96
100
  raise DelegateError("invalid_config_command", f"Unknown config action: {command.action}")
97
- path = _target_path()
101
+ path = target_config_path()
98
102
  if command.action == "sync-profiles":
99
103
  if not path.exists():
100
104
  raise DelegateError(
@@ -53,6 +53,30 @@ def pure_call_supported(engine: str) -> bool:
53
53
  return engine == "claude"
54
54
 
55
55
 
56
+ def validate_pure_call(
57
+ engine: str, *, pure: bool, read_only: bool, group: str | None = None
58
+ ) -> None:
59
+ """Reject --pure combinations that conflict with its stateless one-hop contract."""
60
+ if not pure:
61
+ return
62
+ if group is not None:
63
+ raise DelegateError(
64
+ "pure_conflicts_group",
65
+ "--pure cannot be combined with --group; pure call is a stateless one-hop completion.",
66
+ )
67
+ if read_only:
68
+ raise DelegateError(
69
+ "pure_conflicts_read_only", "--pure cannot be combined with --read-only."
70
+ )
71
+ if not pure_call_supported(engine):
72
+ supported = ", ".join(PURE_CALL_ENGINES)
73
+ raise DelegateError(
74
+ "unsupported_pure_call",
75
+ f"{engine} does not support pure call mode.",
76
+ next_actions=[f"Use --pure with one of: {supported}."],
77
+ )
78
+
79
+
56
80
  # Public harness capability contract. Request validation and describe output both
57
81
  # derive from this map so enabling a capability cannot drift between the two.
58
82
  ENGINE_CAPABILITIES = {
@@ -48,14 +48,12 @@ from delegate_agent.constants import (
48
48
  from delegate_agent.errors import EXIT_OK, DelegateError
49
49
  from delegate_agent.json_types import JsonObject
50
50
  from delegate_agent.prompt_transport import (
51
- DEVIN_AGENT_CONFIG_ARG_PLACEHOLDER,
52
- DEVIN_AGENT_CONFIG_DISPLAY,
53
51
  DROID_PROMPT_FILE_DISPLAY,
54
- PROMPT_FILE_ARG_PLACEHOLDER,
55
- PROMPT_FILE_DISPLAY,
56
52
  PROMPT_TRANSPORT_ARGV,
57
53
  PROMPT_TRANSPORT_FILE,
58
54
  PROMPT_TRANSPORT_STDIN,
55
+ devin_display_argv,
56
+ prompt_file_display_argv,
59
57
  )
60
58
  from delegate_agent.request_build import OPENCODE_SAFE_AGENT, _resolve_default_model
61
59
 
@@ -718,7 +716,7 @@ def _grok_describe_argv(
718
716
  policy,
719
717
  allow_bypass_permissions=_grok_harness_bypass_enabled(config, mode),
720
718
  )
721
- return [PROMPT_FILE_DISPLAY if item == PROMPT_FILE_ARG_PLACEHOLDER else item for item in argv]
719
+ return prompt_file_display_argv(argv)
722
720
 
723
721
 
724
722
  def _devin_describe_argv(
@@ -730,14 +728,7 @@ def _devin_describe_argv(
730
728
  _resolve_default_model(devin),
731
729
  call_read_only=call_read_only,
732
730
  )
733
- return [
734
- DEVIN_AGENT_CONFIG_DISPLAY
735
- if item == DEVIN_AGENT_CONFIG_ARG_PLACEHOLDER
736
- else PROMPT_FILE_DISPLAY
737
- if item == PROMPT_FILE_ARG_PLACEHOLDER
738
- else item
739
- for item in argv
740
- ]
731
+ return devin_display_argv(argv)
741
732
 
742
733
 
743
734
  def _kimi_describe_argv(kimi: JsonObject, *, mode: str) -> list[str]:
@@ -774,7 +765,7 @@ def _opencode_describe_argv(
774
765
  if isinstance(opencode.get("defaultReasoningEffort"), str)
775
766
  else None,
776
767
  )
777
- return [PROMPT_FILE_DISPLAY if item == PROMPT_FILE_ARG_PLACEHOLDER else item for item in argv]
768
+ return prompt_file_display_argv(argv)
778
769
 
779
770
 
780
771
  def _pi_family_describe_argv(section: JsonObject, engine: str, *, mode: str) -> list[str]:
@@ -19,13 +19,17 @@ from collections.abc import Callable, Collection, Mapping
19
19
  from dataclasses import dataclass
20
20
  from datetime import UTC, datetime
21
21
  from pathlib import Path
22
- from typing import BinaryIO, TextIO
22
+ from typing import BinaryIO, TextIO, TypeAlias
23
23
 
24
24
  from delegate_agent import config as delegate_config
25
25
  from delegate_agent import private_io, profiles, redaction
26
26
  from delegate_agent.constants import KNOWN_ENGINES
27
27
  from delegate_agent.json_types import JsonObject
28
28
 
29
+ # A refresh-progress callback: called with each harness name before its probe
30
+ # spawns. Shared by every caller that threads a progress hook into discovery.
31
+ ProgressCallback: TypeAlias = Callable[[str], None]
32
+
29
33
  DISCOVERY_SCHEMA = 1
30
34
  PROBE_STATUSES = frozenset({"ok", "partial", "missing", "error"})
31
35
  MODEL_SCOPES = frozenset({"account", "configured", "catalog", "unknown"})
@@ -1364,7 +1368,7 @@ def probe_harness(
1364
1368
  )
1365
1369
 
1366
1370
 
1367
- def stderr_probe_progress(stream: TextIO | None = None) -> Callable[[str], None]:
1371
+ def stderr_probe_progress(stream: TextIO | None = None) -> ProgressCallback:
1368
1372
  """Return a progress callback that names each harness as probing starts.
1369
1373
 
1370
1374
  Every probe is bounded by ``METADATA_PROBE_TIMEOUT_SEC``, so a wedged
@@ -1384,7 +1388,7 @@ def probe_all_harnesses(
1384
1388
  *,
1385
1389
  env: Mapping[str, str],
1386
1390
  factory_settings_path: Path | None = None,
1387
- progress: Callable[[str], None] | None = None,
1391
+ progress: ProgressCallback | None = None,
1388
1392
  ) -> JsonObject:
1389
1393
  records: JsonObject = {}
1390
1394
  for harness in KNOWN_ENGINES:
@@ -1603,7 +1607,7 @@ def refresh_discovery(
1603
1607
  home: Path | None = None,
1604
1608
  factory_settings_path: Path | None = None,
1605
1609
  persist: bool = True,
1606
- progress: Callable[[str], None] | None = None,
1610
+ progress: ProgressCallback | None = None,
1607
1611
  ) -> JsonObject:
1608
1612
  """Probe harnesses, optionally persisting successful last-known-good records."""
1609
1613
  if isinstance(engines, str):
@@ -275,13 +275,8 @@ def preflight_codex_home(codex_home: str, *, codex: JsonObject) -> None:
275
275
  )
276
276
 
277
277
 
278
- def _request_resolution(request: object) -> ProfileResolution:
279
- resolution = getattr(request, "profile_resolution", None)
280
- return resolution if resolution is not None else empty_profile_resolution()
281
-
282
-
283
278
  def preflight_codex_request(request: object, codex_config: JsonObject) -> None:
284
- resolution = _request_resolution(request)
279
+ resolution: ProfileResolution = request.profile_resolution # type: ignore[attr-defined]
285
280
  if resolution.name is not None and resolution.codex_home is None:
286
281
  raise DelegateError(
287
282
  "profile_missing_codex_home",
@@ -10,6 +10,24 @@ DROID_PROMPT_FILE_DISPLAY = PROMPT_FILE_DISPLAY
10
10
  DEVIN_AGENT_CONFIG_ARG_PLACEHOLDER = "<delegate-devin-agent-config>"
11
11
  DEVIN_AGENT_CONFIG_DISPLAY = "<devin agent config>"
12
12
 
13
+
14
+ def prompt_file_display_argv(argv: list[str]) -> list[str]:
15
+ """Map the prompt-file placeholder to its parent-facing display token."""
16
+ return [PROMPT_FILE_DISPLAY if item == PROMPT_FILE_ARG_PLACEHOLDER else item for item in argv]
17
+
18
+
19
+ def devin_display_argv(argv: list[str]) -> list[str]:
20
+ """Map devin's agent-config and prompt-file placeholders to display tokens."""
21
+ return [
22
+ DEVIN_AGENT_CONFIG_DISPLAY
23
+ if item == DEVIN_AGENT_CONFIG_ARG_PLACEHOLDER
24
+ else PROMPT_FILE_DISPLAY
25
+ if item == PROMPT_FILE_ARG_PLACEHOLDER
26
+ else item
27
+ for item in argv
28
+ ]
29
+
30
+
13
31
  PROMPT_TRANSPORT_ARGV = "argv"
14
32
  PROMPT_TRANSPORT_FILE = "file"
15
33
  PROMPT_TRANSPORT_STDIN = "stdin"
@@ -178,7 +178,6 @@ class ReasoningCapability:
178
178
  harness: str
179
179
  model: str
180
180
  effort: str
181
- supported_efforts: tuple[str, ...]
182
181
  default_effort: str | None
183
182
  transport: str
184
183
  source: str
@@ -486,7 +485,6 @@ def resolve_reasoning_capability(
486
485
  harness=harness,
487
486
  model=model,
488
487
  effort=effort,
489
- supported_efforts=supported,
490
488
  default_effort=_default_effort(declaration, supported),
491
489
  transport=TRANSPORT_BY_HARNESS[harness],
492
490
  source=source,
@@ -553,7 +551,6 @@ def _capability_from_declaration(
553
551
  harness=harness,
554
552
  model=model,
555
553
  effort=effort,
556
- supported_efforts=supported,
557
554
  default_effort=_default_effort(declaration, supported),
558
555
  transport=transport,
559
556
  source=source,
@@ -682,7 +679,6 @@ def resolve_discovered_model_capability(
682
679
  harness=harness,
683
680
  model=model or "",
684
681
  effort=effort,
685
- supported_efforts=(effort,),
686
682
  default_effort=None,
687
683
  transport=TRANSPORT_OPENCODE_VARIANT_FLAG,
688
684
  source="pass-through",
@@ -759,7 +755,7 @@ def add_reasoning_payload_fields(payload: JsonObject, carrier: ReasoningPayloadC
759
755
  unsatisfied config-sourced default with a warning.
760
756
  """
761
757
  effort = carrier.reasoning_effort
762
- requested = getattr(carrier, "requested_reasoning_effort", None)
758
+ requested = carrier.requested_reasoning_effort
763
759
  if requested is not None:
764
760
  payload["requestedReasoningEffort"] = requested
765
761
  elif effort is not None:
@@ -770,7 +766,7 @@ def add_reasoning_payload_fields(payload: JsonObject, carrier: ReasoningPayloadC
770
766
  payload["reasoningEffortSource"] = carrier.reasoning_effort_source
771
767
  if carrier.reasoning_capability_source is not None:
772
768
  payload["reasoningCapabilitySource"] = carrier.reasoning_capability_source
773
- evidence = getattr(carrier, "reasoning_capability_evidence", None)
769
+ evidence = carrier.reasoning_capability_evidence
774
770
  if evidence is not None:
775
771
  payload["reasoningCapabilityEvidence"] = evidence
776
772
  if carrier.reasoning_transport is not None:
@@ -1499,31 +1495,6 @@ def parse_codex_models_payload(raw: JsonObject) -> JsonObject:
1499
1495
  return parsed
1500
1496
 
1501
1497
 
1502
- def merge_reasoning_capability_cache(
1503
- existing: JsonObject | None,
1504
- refreshed: JsonObject,
1505
- ) -> JsonObject:
1506
- """Overlay refreshed harness declarations on the existing cache.
1507
-
1508
- Inputs are already validated at their boundaries (parse_codex_models_payload
1509
- for refreshed data, load_reasoning_capability_cache for the existing file),
1510
- and write_reasoning_capability_cache validates the merged result.
1511
- """
1512
- harnesses: JsonObject = {}
1513
- if existing is not None:
1514
- existing_harnesses = existing.get("harnesses")
1515
- if isinstance(existing_harnesses, dict):
1516
- harnesses.update(existing_harnesses)
1517
- refreshed_harnesses = refreshed["harnesses"]
1518
- if not isinstance(refreshed_harnesses, dict):
1519
- raise ReasoningCapabilityError(
1520
- "capability_refresh_failed",
1521
- "refreshed reasoning cache must contain a harnesses object.",
1522
- )
1523
- harnesses.update(refreshed_harnesses)
1524
- return {"schema": 1, "harnesses": harnesses}
1525
-
1526
-
1527
1498
  def write_reasoning_capability_cache(workspace: str | Path, cache: JsonObject) -> Path:
1528
1499
  validate_cache_payload(cache)
1529
1500
  path = reasoning_capability_cache_path(workspace)
@@ -3,7 +3,7 @@ from __future__ import annotations
3
3
  import re
4
4
  from collections.abc import Mapping, Sequence
5
5
 
6
- from delegate_agent.json_types import JsonValue
6
+ from delegate_agent.json_types import JsonObject, JsonValue
7
7
 
8
8
  SLACK_WEBHOOK_SCHEME = "https"
9
9
  URL_SCHEME_SEPARATOR = "://"
@@ -197,7 +197,7 @@ def redact_mapping_value(key: str, value: JsonValue) -> JsonValue:
197
197
  return redact_value(value)
198
198
 
199
199
 
200
- def redact_env_map(mapping: Mapping[str, str]) -> dict[str, JsonValue]:
200
+ def redact_env_map(mapping: Mapping[str, str]) -> JsonObject:
201
201
  return {key: redact_mapping_value(key, value) for key, value in mapping.items()}
202
202
 
203
203
 
@@ -243,7 +243,7 @@ def redact_value(value: JsonValue) -> JsonValue:
243
243
  return value
244
244
 
245
245
 
246
- def scrub_public_projection(value: dict[str, JsonValue]) -> dict[str, JsonValue]:
246
+ def scrub_public_projection(value: JsonObject) -> JsonObject:
247
247
  """Redact untrusted JSON keys and values without losing key collisions."""
248
248
  scrubbed = _scrub_public_value(value, preserve_paths=False)
249
249
  return scrubbed if isinstance(scrubbed, dict) else {}
@@ -251,7 +251,7 @@ def scrub_public_projection(value: dict[str, JsonValue]) -> dict[str, JsonValue]
251
251
 
252
252
  def _scrub_public_value(value: object, *, preserve_paths: bool) -> JsonValue:
253
253
  if isinstance(value, dict):
254
- scrubbed: dict[str, JsonValue] = {}
254
+ scrubbed: JsonObject = {}
255
255
  for key, child in value.items():
256
256
  if not isinstance(key, str):
257
257
  continue
@@ -276,7 +276,7 @@ def _scrub_public_value(value: object, *, preserve_paths: bool) -> JsonValue:
276
276
  return None
277
277
 
278
278
 
279
- def _collision_safe_key(projected: dict[str, JsonValue], key: str) -> str:
279
+ def _collision_safe_key(projected: JsonObject, key: str) -> str:
280
280
  if key not in projected:
281
281
  return key
282
282
  collision = 2