soothe-cli 0.5.5__tar.gz → 0.5.7__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.
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/PKG-INFO +1 -1
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/cli/execution/headless.py +1 -1
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/core/event_processor.py +1 -1
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/core/presentation_engine.py +1 -1
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/app/_execution.py +17 -3
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/textual_adapter/_turn.py +1 -1
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/widgets/loading.py +11 -3
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/widgets/messages.py +66 -9
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/.gitignore +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/README.md +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/pyproject.toml +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/__init__.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/cli/__init__.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/cli/commands/__init__.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/cli/commands/autopilot_cmd.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/cli/commands/loop_cmd.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/cli/commands/run_cmd.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/cli/execution/__init__.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/cli/execution/daemon.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/cli/execution/headless_renderer.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/cli/execution/launcher.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/cli/main.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/cli/stream/__init__.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/cli/stream/context.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/cli/stream/display_line.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/cli/stream/formatter.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/cli/stream/pipeline.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/cli/stream/task_scope.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/config/__init__.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/config/cli_config.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/plan/__init__.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/plan/rich_tree.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/__init__.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/commands/__init__.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/commands/command_router.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/commands/slash_commands.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/commands/subagent_routing.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/config_loader.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/core/__init__.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/core/processor_state.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/core/renderer_protocol.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/duration_format.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/events/__init__.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/events/display_policy.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/events/essential_events.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/events/explore_task_display.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/events/stream_accumulator.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/events/tui_trace_log.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/rendering/__init__.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/rendering/async_renderer_protocol.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/rendering/renderer_base.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/tools/__init__.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/tools/_utils.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/tools/message_processing.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/tools/rendering.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/tools/tool_call_resolution.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/tools/tool_card_payload.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/tools/tool_card_visibility.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/tools/tool_formatters/__init__.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/tools/tool_formatters/base.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/tools/tool_formatters/execution.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/tools/tool_formatters/fallback.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/tools/tool_formatters/file_ops.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/tools/tool_formatters/goal_formatter.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/tools/tool_formatters/media.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/tools/tool_formatters/structured.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/tools/tool_formatters/subagent.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/tools/tool_formatters/web.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/tools/tool_message_format.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/tools/tool_output_formatter.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/__init__.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/_ask_user_types.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/_cli_context.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/_env_vars.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/_session_stats.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/_version.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/app/__init__.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/app/_app.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/app/_commands.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/app/_history.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/app/_messages_mixin.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/app/_model.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/app/_module_init.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/app/_startup.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/app/_ui.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/app/app.tcss +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/command_registry.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/config.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/daemon_session.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/file_ops.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/formatting.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/hooks.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/input.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/media_utils.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/message_display_filter.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/model_config.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/output.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/preview_limits.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/project_utils.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/sessions.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/skills/__init__.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/skills/invocation.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/skills/load.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/textual_adapter/__init__.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/textual_adapter/_adapter.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/textual_adapter/_stream_formatting.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/textual_adapter/_stream_messages.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/textual_adapter/_turn_helpers.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/theme.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/tool_display.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/unicode_security.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/update_check.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/widgets/__init__.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/widgets/_links.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/widgets/approval.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/widgets/ask_user.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/widgets/autocomplete.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/widgets/autopilot_dashboard.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/widgets/autopilot_screen.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/widgets/chat_input.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/widgets/clipboard.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/widgets/diff.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/widgets/editor.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/widgets/history.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/widgets/loop_selector.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/widgets/mcp_viewer.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/widgets/message_store.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/widgets/model_selector.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/widgets/notification_settings.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/widgets/status.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/widgets/theme_selector.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/widgets/tool_renderers.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/widgets/tool_widgets.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/widgets/tools.py +0 -0
- {soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/widgets/welcome.py +0 -0
|
@@ -229,7 +229,7 @@ class EventProcessor:
|
|
|
229
229
|
"""Headless stdout: only RFC-614 loop-tagged finals (IG-343 / IG-345).
|
|
230
230
|
|
|
231
231
|
Suppresses unphased execute-wave narration on the main graph; ``goal_completion``,
|
|
232
|
-
``
|
|
232
|
+
``quiz``, etc. are routed via ``assistant_output_phase`` before this path.
|
|
233
233
|
|
|
234
234
|
Subgraph streams without RFC-614 phases are skipped in ``_handle_ai_message`` before
|
|
235
235
|
this runs; loop-tagged subgraph finals use ``_dispatch_loop_tagged_assistant_text``.
|
|
@@ -49,7 +49,7 @@ class PresentationEngine:
|
|
|
49
49
|
|
|
50
50
|
@property
|
|
51
51
|
def final_answer_locked(self) -> bool:
|
|
52
|
-
"""True after a custom final
|
|
52
|
+
"""True after a custom final or quiz-phase response was emitted for this turn."""
|
|
53
53
|
return self._state.final_answer_locked
|
|
54
54
|
|
|
55
55
|
def mark_final_answer_locked(self) -> None:
|
|
@@ -46,6 +46,19 @@ InputMode = Literal["normal", "shell", "command"]
|
|
|
46
46
|
|
|
47
47
|
logger = logging.getLogger(__name__)
|
|
48
48
|
|
|
49
|
+
# Partial match for daemon pool_runner RuntimeError when an OS worker exits mid-turn.
|
|
50
|
+
_DAEMON_WORKER_SUBPROCESS_LOST = "Worker subprocess exited unexpectedly during query execution"
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _friendly_agent_execution_error(exc: BaseException) -> str:
|
|
54
|
+
"""Map known daemon failures to concise TUI copy."""
|
|
55
|
+
if isinstance(exc, RuntimeError) and _DAEMON_WORKER_SUBPROCESS_LOST in str(exc):
|
|
56
|
+
return (
|
|
57
|
+
"The daemon execution worker stopped unexpectedly (for example after the pool "
|
|
58
|
+
"recycled an idle subprocess). Send your message again."
|
|
59
|
+
)
|
|
60
|
+
return str(exc)
|
|
61
|
+
|
|
49
62
|
|
|
50
63
|
class _ExecutionMixin:
|
|
51
64
|
"""Agent execution, message routing, queue, shell commands, and daemon events."""
|
|
@@ -859,13 +872,14 @@ class _ExecutionMixin:
|
|
|
859
872
|
)
|
|
860
873
|
except Exception as e: # Resilient tool rendering
|
|
861
874
|
logger.exception("Agent execution failed")
|
|
875
|
+
display_err = _friendly_agent_execution_error(e)
|
|
862
876
|
# Ensure any in-flight tool calls don't remain stuck in "Running..."
|
|
863
877
|
# when streaming aborts before tool results arrive.
|
|
864
878
|
if self._ui_adapter:
|
|
865
|
-
self._ui_adapter.finalize_pending_tools_with_error(f"Agent error: {
|
|
866
|
-
self._ui_adapter.finalize_pending_steps_with_error(f"Agent error: {
|
|
879
|
+
self._ui_adapter.finalize_pending_tools_with_error(f"Agent error: {display_err}")
|
|
880
|
+
self._ui_adapter.finalize_pending_steps_with_error(f"Agent error: {display_err}")
|
|
867
881
|
try:
|
|
868
|
-
await self._mount_message(ErrorMessage(f"Agent error: {
|
|
882
|
+
await self._mount_message(ErrorMessage(f"Agent error: {display_err}"))
|
|
869
883
|
except Exception:
|
|
870
884
|
logger.debug("Could not mount error message (app closing?)", exc_info=True)
|
|
871
885
|
finally:
|
|
@@ -841,7 +841,7 @@ async def execute_task_textual(
|
|
|
841
841
|
# Main graph: skip standalone AssistantMessage cards for
|
|
842
842
|
# intermediate AIMessage streams (execute_wave, unphased, etc.).
|
|
843
843
|
# ``goal_completion`` is handled above. Other RFC-614 user-output
|
|
844
|
-
# phases (
|
|
844
|
+
# phases (quiz, autonomous_goal) still use cards.
|
|
845
845
|
if (
|
|
846
846
|
is_main_agent
|
|
847
847
|
and assistant_output_phase(message)
|
|
@@ -137,7 +137,8 @@ class LoadingWidget(Static):
|
|
|
137
137
|
now = monotonic()
|
|
138
138
|
if self._turn_start_mono is None:
|
|
139
139
|
self._turn_start_mono = now
|
|
140
|
-
|
|
140
|
+
# Reduced from 0.1s (10fps) to 0.2s (5fps) to reduce UI thread contention
|
|
141
|
+
self._animation_timer = self.set_interval(0.2, self._update_animation)
|
|
141
142
|
|
|
142
143
|
def on_unmount(self) -> None:
|
|
143
144
|
"""Stop the animation timer when the widget leaves the DOM."""
|
|
@@ -159,13 +160,20 @@ class LoadingWidget(Static):
|
|
|
159
160
|
self._animation_timer = None
|
|
160
161
|
|
|
161
162
|
def _update_animation(self) -> None:
|
|
162
|
-
"""Update spinner and elapsed time."""
|
|
163
|
+
"""Update spinner and elapsed time (optimized to reduce render overhead)."""
|
|
163
164
|
if self._paused:
|
|
164
165
|
return
|
|
165
166
|
|
|
167
|
+
# Skip update if widget is not visible on screen
|
|
168
|
+
if not self.is_on_screen:
|
|
169
|
+
return
|
|
170
|
+
|
|
166
171
|
if self._spinner_widget:
|
|
167
172
|
frame = self._spinner.next_frame()
|
|
168
|
-
|
|
173
|
+
# Use refresh with layout=False to avoid expensive layout recalculation
|
|
174
|
+
self._spinner_widget._content = frame
|
|
175
|
+
self._spinner_widget._render_cache = None
|
|
176
|
+
self._spinner_widget.refresh(repaint=True, layout=False)
|
|
169
177
|
|
|
170
178
|
if self._hint_widget and self._turn_start_mono is not None:
|
|
171
179
|
now = monotonic()
|
|
@@ -59,7 +59,7 @@ _STEP_TOOL_PREVIEW_ROWS = STEP_TASK_CARD_COLLAPSE_LINE_THRESHOLD
|
|
|
59
59
|
"""Collapsed step/task activity preview shows this many rows (IG-402)."""
|
|
60
60
|
|
|
61
61
|
_MAX_STEP_STAT_TOOL_KINDS = 4
|
|
62
|
-
"""Max distinct tool display names in the
|
|
62
|
+
"""Max distinct tool display names in the running-line stats suffix before ``+N more``."""
|
|
63
63
|
|
|
64
64
|
_RUNNING_SPINNER_INTERVAL_SECONDS = 0.2
|
|
65
65
|
"""Spinner/status animation cadence for running cards."""
|
|
@@ -711,6 +711,10 @@ class AssistantMessage(Vertical):
|
|
|
711
711
|
}
|
|
712
712
|
"""
|
|
713
713
|
|
|
714
|
+
# Performance optimization: batch streaming updates to reduce render frequency
|
|
715
|
+
_STREAM_FLUSH_INTERVAL: float = 0.05 # 50ms batching for streaming
|
|
716
|
+
_VISIBILITY_REFRESH_INTERVAL: float = 0.1 # 100ms throttle for visibility refresh
|
|
717
|
+
|
|
714
718
|
def __init__(self, content: str = "", **kwargs: Any) -> None:
|
|
715
719
|
"""Initialize an assistant message.
|
|
716
720
|
|
|
@@ -726,6 +730,11 @@ class AssistantMessage(Vertical):
|
|
|
726
730
|
self._preview_widget: Static | None = None
|
|
727
731
|
self._hint_widget: Static | None = None
|
|
728
732
|
|
|
733
|
+
# Batching buffer for streaming content
|
|
734
|
+
self._pending_buffer: str = ""
|
|
735
|
+
self._flush_timer: Timer | None = None
|
|
736
|
+
self._last_visibility_refresh: float = 0.0
|
|
737
|
+
|
|
729
738
|
def compose(self) -> ComposeResult: # noqa: PLR6301 # Textual widget method convention
|
|
730
739
|
"""Compose markdown body, plain preview, and expand hint."""
|
|
731
740
|
from textual.widgets import Markdown
|
|
@@ -851,14 +860,50 @@ class AssistantMessage(Vertical):
|
|
|
851
860
|
else:
|
|
852
861
|
_show_timestamp_toast(self)
|
|
853
862
|
|
|
863
|
+
def _should_refresh_visibility(self) -> bool:
|
|
864
|
+
"""Check if visibility refresh should run (throttled)."""
|
|
865
|
+
from time import monotonic
|
|
866
|
+
|
|
867
|
+
now = monotonic()
|
|
868
|
+
if now - self._last_visibility_refresh > self._VISIBILITY_REFRESH_INTERVAL:
|
|
869
|
+
self._last_visibility_refresh = now
|
|
870
|
+
return True
|
|
871
|
+
return False
|
|
872
|
+
|
|
873
|
+
async def _flush_pending_content(self) -> None:
|
|
874
|
+
"""Flush buffered content to stream (batched update)."""
|
|
875
|
+
self._flush_timer = None
|
|
876
|
+
if not self._pending_buffer:
|
|
877
|
+
return
|
|
878
|
+
|
|
879
|
+
text = self._pending_buffer
|
|
880
|
+
self._pending_buffer = ""
|
|
881
|
+
|
|
882
|
+
stream = self._ensure_stream()
|
|
883
|
+
await stream.write(text)
|
|
884
|
+
|
|
885
|
+
# Throttle visibility refresh
|
|
886
|
+
if self._should_refresh_visibility():
|
|
887
|
+
self._refresh_body_visibility()
|
|
888
|
+
|
|
854
889
|
async def append_content(self, text: str) -> None:
|
|
855
|
-
"""Append content to the message (for streaming).
|
|
890
|
+
"""Append content to the message (for streaming with batching).
|
|
891
|
+
|
|
892
|
+
Uses internal buffering to batch writes and reduce render frequency.
|
|
893
|
+
"""
|
|
856
894
|
if not text:
|
|
857
895
|
return
|
|
896
|
+
|
|
897
|
+
# Accumulate content
|
|
858
898
|
self._content += text
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
899
|
+
self._pending_buffer += text
|
|
900
|
+
|
|
901
|
+
# Schedule batched flush if not already scheduled
|
|
902
|
+
if self._flush_timer is None:
|
|
903
|
+
self._flush_timer = self.set_timer(
|
|
904
|
+
self._STREAM_FLUSH_INTERVAL,
|
|
905
|
+
self._flush_pending_content,
|
|
906
|
+
)
|
|
862
907
|
|
|
863
908
|
async def write_initial_content(self) -> None:
|
|
864
909
|
"""Write initial content from constructor and finalize the stream."""
|
|
@@ -869,6 +914,15 @@ class AssistantMessage(Vertical):
|
|
|
869
914
|
|
|
870
915
|
async def stop_stream(self) -> None:
|
|
871
916
|
"""Stop the streaming and apply collapsed layout when appropriate."""
|
|
917
|
+
# Cancel any pending flush timer
|
|
918
|
+
if self._flush_timer is not None:
|
|
919
|
+
self._flush_timer.stop()
|
|
920
|
+
self._flush_timer = None
|
|
921
|
+
|
|
922
|
+
# Flush any remaining buffered content
|
|
923
|
+
if self._pending_buffer:
|
|
924
|
+
await self._flush_pending_content()
|
|
925
|
+
|
|
872
926
|
if self._stream is not None:
|
|
873
927
|
await self._stream.stop()
|
|
874
928
|
self._stream = None
|
|
@@ -878,6 +932,7 @@ class AssistantMessage(Vertical):
|
|
|
878
932
|
"""Set the full message content (stops any active stream)."""
|
|
879
933
|
await self.stop_stream()
|
|
880
934
|
self._content = content
|
|
935
|
+
self._pending_buffer = "" # Clear any pending buffer
|
|
881
936
|
if self._markdown:
|
|
882
937
|
await self._markdown.update(content)
|
|
883
938
|
self._refresh_body_visibility()
|
|
@@ -2461,8 +2516,9 @@ class _StepToolRow:
|
|
|
2461
2516
|
class CognitionStepMessage(Vertical):
|
|
2462
2517
|
"""Agent-loop act step card: aggregates main-agent tool calls (IG-402).
|
|
2463
2518
|
|
|
2464
|
-
Header
|
|
2465
|
-
|
|
2519
|
+
Header is the step description only; per-tool counts appear on the running
|
|
2520
|
+
status line (and match the prior header format). Body lists one CLI-style row
|
|
2521
|
+
per call. When there are more than ``_STEP_TOOL_PREVIEW_ROWS`` rows, click first folds or
|
|
2466
2522
|
unfolds the tool list; otherwise click toggles whole-card collapse. When tool
|
|
2467
2523
|
rows, subagent notes, and execute prose together exceed that same threshold,
|
|
2468
2524
|
the card body auto-collapses until the user expands it (a new ``set_running``
|
|
@@ -2630,7 +2686,7 @@ class CognitionStepMessage(Vertical):
|
|
|
2630
2686
|
return _assemble_card_header(
|
|
2631
2687
|
self,
|
|
2632
2688
|
"🚀 ",
|
|
2633
|
-
|
|
2689
|
+
self._description,
|
|
2634
2690
|
)
|
|
2635
2691
|
|
|
2636
2692
|
def compose(self) -> ComposeResult:
|
|
@@ -3169,7 +3225,8 @@ class CognitionStepMessage(Vertical):
|
|
|
3169
3225
|
toggle_icon = ""
|
|
3170
3226
|
if has_collapsible:
|
|
3171
3227
|
toggle_icon = f" {g.expand if self._card_collapsed else g.collapse}"
|
|
3172
|
-
|
|
3228
|
+
stats_suffix = self._stats_title_suffix()
|
|
3229
|
+
line = f"{gutter}{frame} Running...{elapsed}{stats_suffix}{toggle_icon}"
|
|
3173
3230
|
self._status_widget.update(Content.styled(line, colors.cognition))
|
|
3174
3231
|
now = monotonic()
|
|
3175
3232
|
if any(r.phase == "running" for r in self._rows) and (
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/rendering/async_renderer_protocol.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/tools/tool_formatters/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/tools/tool_formatters/execution.py
RENAMED
|
File without changes
|
{soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/tools/tool_formatters/fallback.py
RENAMED
|
File without changes
|
{soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/tools/tool_formatters/file_ops.py
RENAMED
|
File without changes
|
{soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/tools/tool_formatters/goal_formatter.py
RENAMED
|
File without changes
|
|
File without changes
|
{soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/tools/tool_formatters/structured.py
RENAMED
|
File without changes
|
{soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/shared/tools/tool_formatters/subagent.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/textual_adapter/_stream_formatting.py
RENAMED
|
File without changes
|
{soothe_cli-0.5.5 → soothe_cli-0.5.7}/src/soothe_cli/tui/textual_adapter/_stream_messages.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|