drydock-cli 3.0.83__tar.gz → 3.0.85__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 (103) hide show
  1. {drydock_cli-3.0.83/drydock_cli.egg-info → drydock_cli-3.0.85}/PKG-INFO +8 -4
  2. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/README.md +7 -3
  3. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/advisor.py +9 -1
  4. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/tui/app.py +30 -8
  5. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/tui/widgets.py +2 -2
  6. {drydock_cli-3.0.83 → drydock_cli-3.0.85/drydock_cli.egg-info}/PKG-INFO +8 -4
  7. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/pyproject.toml +1 -1
  8. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_advisor.py +7 -0
  9. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_tui.py +35 -0
  10. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/LICENSE +0 -0
  11. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/NOTICE +0 -0
  12. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/__init__.py +0 -0
  13. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/__main__.py +0 -0
  14. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/agent.py +0 -0
  15. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/bash_safety.py +0 -0
  16. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/builtin_skills/__init__.py +0 -0
  17. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/builtin_skills/rmf-categorize.md +0 -0
  18. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/builtin_skills/rmf-control.md +0 -0
  19. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/builtin_skills/rmf-poam.md +0 -0
  20. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/builtin_skills/rmf-review.md +0 -0
  21. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/builtin_skills/stig-assess.md +0 -0
  22. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/builtin_skills/stig-remediate.md +0 -0
  23. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/cci.py +0 -0
  24. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/cli.py +0 -0
  25. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/compaction.py +0 -0
  26. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/config.py +0 -0
  27. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/detect.py +0 -0
  28. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/extract.py +0 -0
  29. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/gittools.py +0 -0
  30. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/graphrag.py +0 -0
  31. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/guards.py +0 -0
  32. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/loop_detect.py +0 -0
  33. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/mcp.py +0 -0
  34. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/poam.py +0 -0
  35. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/providers.py +0 -0
  36. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/rmf.py +0 -0
  37. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/rmf_graph.py +0 -0
  38. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/skills.py +0 -0
  39. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/stig.py +0 -0
  40. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/tool_registry.py +0 -0
  41. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/tools/__init__.py +0 -0
  42. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/tui/__init__.py +0 -0
  43. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/tui/approval.py +0 -0
  44. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/tui/messages.py +0 -0
  45. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/tuning.py +0 -0
  46. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock/web.py +0 -0
  47. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock_cli.egg-info/SOURCES.txt +0 -0
  48. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock_cli.egg-info/dependency_links.txt +0 -0
  49. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock_cli.egg-info/entry_points.txt +0 -0
  50. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock_cli.egg-info/requires.txt +0 -0
  51. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/drydock_cli.egg-info/top_level.txt +0 -0
  52. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/setup.cfg +0 -0
  53. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_approval.py +0 -0
  54. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_back_command.py +0 -0
  55. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_bash_output_bounding.py +0 -0
  56. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_bash_process_group.py +0 -0
  57. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_bash_safety.py +0 -0
  58. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_bash_timeout_network.py +0 -0
  59. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_cci.py +0 -0
  60. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_cli_agents.py +0 -0
  61. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_compact_command.py +0 -0
  62. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_compaction.py +0 -0
  63. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_config.py +0 -0
  64. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_config_migration.py +0 -0
  65. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_context_limit_config.py +0 -0
  66. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_detect.py +0 -0
  67. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_dispatch.py +0 -0
  68. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_e2e_connected.py +0 -0
  69. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_empty_response.py +0 -0
  70. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_extract.py +0 -0
  71. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_failure_loop.py +0 -0
  72. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_first_run_setup.py +0 -0
  73. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_gittools.py +0 -0
  74. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_graphrag.py +0 -0
  75. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_guards_and_tools.py +0 -0
  76. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_hallucinated_tools.py +0 -0
  77. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_leaked_tool_call.py +0 -0
  78. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_loop_detect.py +0 -0
  79. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_mcp.py +0 -0
  80. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_oneshot_unreachable.py +0 -0
  81. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_plan_autocontinue.py +0 -0
  82. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_poam.py +0 -0
  83. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_providers_unreachable.py +0 -0
  84. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_read_index.py +0 -0
  85. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_rmf.py +0 -0
  86. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_rmf_graph.py +0 -0
  87. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_rmf_stig_graph.py +0 -0
  88. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_runaway_repetition.py +0 -0
  89. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_server_probe.py +0 -0
  90. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_skills.py +0 -0
  91. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_stig.py +0 -0
  92. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_stop.py +0 -0
  93. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_streaming_newlines.py +0 -0
  94. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_subagent.py +0 -0
  95. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_system_prompt_help.py +0 -0
  96. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_todo.py +0 -0
  97. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_tool_arg_parsing.py +0 -0
  98. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_tools_undo.py +0 -0
  99. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_tuning.py +0 -0
  100. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_viewimage.py +0 -0
  101. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_vision_input.py +0 -0
  102. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_web_tools.py +0 -0
  103. {drydock_cli-3.0.83 → drydock_cli-3.0.85}/tests/test_xccdf.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drydock-cli
3
- Version: 3.0.83
3
+ Version: 3.0.85
4
4
  Summary: Drydock — a local, provider-agnostic terminal coding agent for local LLMs
5
5
  Author: Frank Bobe III
6
6
  License-Expression: Apache-2.0
@@ -156,10 +156,14 @@ stance).
156
156
  /advisor # show current config (key masked)
157
157
  ```
158
158
 
159
- **Use it** two ways:
160
- - **You:** `/ask <question>` — consults the advisor and shows its answer.
159
+ **Use it** three ways:
160
+ - **You (private):** `/ask <question>` — consults the advisor and shows its
161
+ answer to *you only* (not added to the agent's context).
162
+ - **You (feed the agent):** `/ask! <question>` — same, but also **injects** the
163
+ answer into the agent's context and has the primary model process it, so a
164
+ second opinion can steer the current task.
161
165
  - **The agent:** it can call the read-only `Consult` tool on its own when it hits
162
- something hard, passing the relevant code/error as context. You act on the advice.
166
+ something hard (the answer comes back as a tool result, so it's in context).
163
167
 
164
168
  **Pointing it at Gemini** — two options:
165
169
  1. **Gemini's official OpenAI-compatible endpoint** (if this box has internet):
@@ -132,10 +132,14 @@ stance).
132
132
  /advisor # show current config (key masked)
133
133
  ```
134
134
 
135
- **Use it** two ways:
136
- - **You:** `/ask <question>` — consults the advisor and shows its answer.
135
+ **Use it** three ways:
136
+ - **You (private):** `/ask <question>` — consults the advisor and shows its
137
+ answer to *you only* (not added to the agent's context).
138
+ - **You (feed the agent):** `/ask! <question>` — same, but also **injects** the
139
+ answer into the agent's context and has the primary model process it, so a
140
+ second opinion can steer the current task.
137
141
  - **The agent:** it can call the read-only `Consult` tool on its own when it hits
138
- something hard, passing the relevant code/error as context. You act on the advice.
142
+ something hard (the answer comes back as a tool result, so it's in context).
139
143
 
140
144
  **Pointing it at Gemini** — two options:
141
145
  1. **Gemini's official OpenAI-compatible endpoint** (if this box has internet):
@@ -91,8 +91,16 @@ def test_connection(config: dict) -> str:
91
91
  base = config.get("advisor_base_url")
92
92
  t0 = time.monotonic()
93
93
  try:
94
- reply = _call("Reply with exactly: OK", config, timeout=30.0, max_tokens=8)
94
+ # Generous read timeout: a slow/cold LOCAL model can take a while to
95
+ # return even a few tokens. A truly-down endpoint fails fast on the 10s
96
+ # connect timeout, so this only waits on a reachable-but-slow server.
97
+ reply = _call("Reply with exactly: OK", config, timeout=90.0, max_tokens=8)
95
98
  except Exception as e: # noqa: BLE001
99
+ emsg = str(e).lower()
100
+ if "timeout" in emsg or "timed out" in emsg:
101
+ return (f"✗ advisor timed out (>90s) — {model} at {base} is REACHABLE but slow "
102
+ "to respond (a cold or loaded local model?). `/ask` allows much longer, "
103
+ "so it may still work; or give the server a moment and retry.")
96
104
  return (f"✗ advisor unreachable — {model} at {base}\n {e}\n"
97
105
  " Check the endpoint is up, the URL ends in /v1, and the key/model are right.")
98
106
  dt = time.monotonic() - t0
@@ -491,6 +491,8 @@ class DrydockApp(App):
491
491
  self._cmd_advisor(arg)
492
492
  elif cmd == "/ask":
493
493
  self._cmd_ask(arg)
494
+ elif cmd == "/ask!":
495
+ self._cmd_ask(arg, inject=True)
494
496
  elif cmd == "/graphrag":
495
497
  self._cmd_graphrag(arg)
496
498
  elif cmd == "/status":
@@ -513,7 +515,7 @@ class DrydockApp(App):
513
515
  " /status session model, cwd, turns, tokens\n"
514
516
  " /compact shrink old context to free up the window\n"
515
517
  " /context view/set the context-window budget (e.g. /context 65536)\n"
516
- " /advisor set up a 2nd 'advisor' model (e.g. Gemini) · /ask <q> to consult it\n"
518
+ " /advisor set up a 2nd 'advisor' model (Gemini etc.); /ask <q> = you, /ask! = feed to agent\n"
517
519
  " /graphrag ingest docs into a knowledge base the agent can use\n"
518
520
  " build <path> · add <path> · query <q> · status · clear\n"
519
521
  " /skills list skills · /skills new <name> <prompt> to create one\n"
@@ -891,23 +893,43 @@ class DrydockApp(App):
891
893
  shown = "•••••" if keymap[sub] == "advisor_api_key" else val
892
894
  self._info(f"✓ advisor {sub} set to {shown} (saved to config.toml).")
893
895
 
894
- def _cmd_ask(self, arg: str) -> None:
895
- """Ask the second/advisor model directly and show its answer."""
896
+ def _cmd_ask(self, arg: str, *, inject: bool = False) -> None:
897
+ """Consult the advisor model. `/ask` shows the answer to YOU only;
898
+ `/ask!` also INJECTS it into the agent's context and has the primary model
899
+ process it (so a second opinion can steer the current task)."""
896
900
  from drydock import advisor
897
901
  q = (arg or "").strip()
898
902
  if not q:
899
- self._info("usage: /ask <question> consult your configured advisor (second) model")
903
+ self._info("usage: /ask <question> (show to you) · /ask! <question> "
904
+ "(also feed the answer to the agent)")
900
905
  return
901
906
  if not advisor.is_configured(self.config):
902
907
  self._info(advisor.not_configured_message())
903
908
  return
904
- self._info(f"Asking the advisor ({self.config.get('advisor_model')})")
905
- self.run_worker(lambda: self._ask_worker(q), thread=True)
909
+ self._info(f"Asking the advisor ({self.config.get('advisor_model')})"
910
+ + (" — its answer will be added to the agent's context…" if inject else "…"))
911
+ self.run_worker(lambda: self._ask_worker(q, inject), thread=True)
906
912
 
907
- def _ask_worker(self, question: str) -> None:
913
+ def _ask_worker(self, question: str, inject: bool = False) -> None:
908
914
  from drydock import advisor
909
915
  answer = advisor.consult(question, self.config)
910
- self.call_from_thread(self._info, f"💡 advisor:\n{answer}")
916
+ self.call_from_thread(self._deliver_advice, question, answer, inject)
917
+
918
+ def _deliver_advice(self, question: str, answer: str, inject: bool) -> None:
919
+ """Show the advisor's answer; if inject, feed it into the agent's context
920
+ so the primary model processes it (queued if a turn is already running)."""
921
+ failed = answer.startswith(("No advisor", "Could not reach", "Error", "✗"))
922
+ if not inject or failed:
923
+ self._info(f"💡 advisor:\n{answer}")
924
+ return
925
+ self._info(f"💡 advisor (added to the agent's context):\n{answer}")
926
+ turn = (f"[Second opinion from the advisor model — I asked it: {question}]\n\n"
927
+ f"{answer}\n\n[Consider this advice for the current task.]")
928
+ if self._busy:
929
+ self._queue.append(turn)
930
+ self._refresh_status()
931
+ else:
932
+ self._begin(turn)
911
933
 
912
934
  def _cmd_compact(self) -> None:
913
935
  """Manually compact the conversation to reclaim context NOW, without
@@ -153,8 +153,8 @@ class PromptHistory:
153
153
  # completes the prefix.
154
154
  SLASH_COMMANDS = [
155
155
  "/help", "/model", "/cwd", "/undo", "/back", "/stop", "/status",
156
- "/compact", "/context", "/advisor", "/ask", "/graphrag", "/skills", "/loop",
157
- "/mcp", "/rmf", "/stig",
156
+ "/compact", "/context", "/advisor", "/ask", "/ask!", "/graphrag", "/skills",
157
+ "/loop", "/mcp", "/rmf", "/stig",
158
158
  "/clear", "/quit",
159
159
  ]
160
160
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drydock-cli
3
- Version: 3.0.83
3
+ Version: 3.0.85
4
4
  Summary: Drydock — a local, provider-agnostic terminal coding agent for local LLMs
5
5
  Author: Frank Bobe III
6
6
  License-Expression: Apache-2.0
@@ -156,10 +156,14 @@ stance).
156
156
  /advisor # show current config (key masked)
157
157
  ```
158
158
 
159
- **Use it** two ways:
160
- - **You:** `/ask <question>` — consults the advisor and shows its answer.
159
+ **Use it** three ways:
160
+ - **You (private):** `/ask <question>` — consults the advisor and shows its
161
+ answer to *you only* (not added to the agent's context).
162
+ - **You (feed the agent):** `/ask! <question>` — same, but also **injects** the
163
+ answer into the agent's context and has the primary model process it, so a
164
+ second opinion can steer the current task.
161
165
  - **The agent:** it can call the read-only `Consult` tool on its own when it hits
162
- something hard, passing the relevant code/error as context. You act on the advice.
166
+ something hard (the answer comes back as a tool result, so it's in context).
163
167
 
164
168
  **Pointing it at Gemini** — two options:
165
169
  1. **Gemini's official OpenAI-compatible endpoint** (if this box has internet):
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
7
7
  # PyPI distribution name is drydock-cli (the established install name, continued
8
8
  # from the retired v2 fork); the import package + CLI command stay `drydock`.
9
9
  name = "drydock-cli"
10
- version = "3.0.83"
10
+ version = "3.0.85"
11
11
  description = "Drydock — a local, provider-agnostic terminal coding agent for local LLMs"
12
12
  readme = "README.md"
13
13
  requires-python = ">=3.11"
@@ -81,3 +81,10 @@ def test_test_connection_failure(monkeypatch):
81
81
 
82
82
  def test_test_connection_unconfigured():
83
83
  assert "No advisor" in advisor.test_connection({})
84
+
85
+
86
+ def test_test_connection_timeout_says_reachable_but_slow(monkeypatch):
87
+ def slow(*a, **k): raise TimeoutError("Request timed out.")
88
+ monkeypatch.setattr(advisor, "_call", slow)
89
+ out = advisor.test_connection({"advisor_base_url": "http://b/v1", "advisor_model": "m"})
90
+ assert out.startswith("✗") and "REACHABLE but slow" in out and "/ask" in out
@@ -422,3 +422,38 @@ def test_context_command_sets_and_persists(tmp_path, monkeypatch):
422
422
  import tomllib
423
423
  saved = tomllib.loads((tmp_path / ".drydock" / "config.toml").read_text())
424
424
  assert saved["context_limit"] == 65536 # survived to disk
425
+
426
+
427
+ def test_ask_bang_injects_advice_but_plain_ask_does_not():
428
+ """/ask! feeds the advisor's answer into the agent's context (starts a turn
429
+ with it); plain /ask (inject=False) only displays it."""
430
+ async def main():
431
+ app = DrydockApp({"model": "gemma4", "provider": "vllm", "cwd": "/tmp"})
432
+ async with app.run_test() as pilot:
433
+ await pilot.pause()
434
+ started: list[str] = []
435
+ app._run_agent = lambda text: started.append(text) # type: ignore[method-assign]
436
+
437
+ # inject=True → a turn begins carrying the advice
438
+ app._deliver_advice("what DS for an LRU cache?",
439
+ "Use a hash map + a doubly linked list.", True)
440
+ for _ in range(40):
441
+ await pilot.pause()
442
+ if started:
443
+ break
444
+ assert started, "/ask! should start a turn"
445
+ assert "hash map + a doubly linked list" in started[0]
446
+ assert "advisor model" in started[0] # framed as a second opinion
447
+
448
+ # inject=False → display only, no new turn
449
+ started.clear()
450
+ app._deliver_advice("q", "some answer", False)
451
+ await pilot.pause()
452
+ assert started == []
453
+
454
+ # a FAILED consult must not inject even with inject=True
455
+ app._deliver_advice("q", "Could not reach the advisor model at http://x: boom", True)
456
+ await pilot.pause()
457
+ assert started == []
458
+
459
+ asyncio.run(main())
File without changes
File without changes
File without changes