deepy-cli 0.2.27__tar.gz → 0.2.28__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.
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/PKG-INFO +1 -1
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/pyproject.toml +1 -1
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/__init__.py +1 -1
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tui/app.py +75 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tui/widgets.py +69 -5
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/README.md +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/__main__.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/audit.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/background_tasks.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/cli.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/config/__init__.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/config/settings.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/__init__.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/skills/skill-creator/SKILL.md +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/skills/skill-installer/SKILL.md +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/tools/AskUserQuestion.md +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/tools/Read.md +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/tools/Search.md +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/tools/Update.md +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/tools/WebFetch.md +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/tools/WebSearch.md +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/tools/Write.md +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/tools/__init__.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/tools/shell.md +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/tools/task_list.md +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/tools/task_output.md +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/tools/task_stop.md +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/tools/test_shell.md +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/data/tools/todo_write.md +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/errors.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/input_suggestions.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/llm/__init__.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/llm/agent.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/llm/cache_context.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/llm/compaction.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/llm/context.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/llm/events.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/llm/model_capabilities.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/llm/multimodal.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/llm/provider.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/llm/replay.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/llm/runner.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/llm/thinking.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/mcp.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/prompts/__init__.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/prompts/compact.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/prompts/init_agents.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/prompts/rules.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/prompts/runtime_context.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/prompts/system.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/prompts/tool_docs.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/session_cost.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/sessions/__init__.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/sessions/index.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/sessions/manager.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/sessions/session.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/sessions/store_helpers.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/skill_market.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/skills.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/status.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/subagents.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/todos.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tools/__init__.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tools/agents.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tools/builtin.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tools/file_state.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tools/result.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tools/search.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tools/shell_output.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tools/shell_utils.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tools/test_shell.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tui/__init__.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tui/commands.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tui/compat.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tui/diff.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tui/interaction_surfaces.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tui/runner.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tui/screens.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tui/state.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tui/theme.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/tui/transcript.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/types/__init__.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/types/sdk.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/types/tool_payloads.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/__init__.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/app.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/ask_user_question.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/audit_approval_panel.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/audit_approval_picker.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/exit_summary.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/file_mentions.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/image_input.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/loading_text.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/local_command.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/markdown.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/message_view.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/model_picker.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/prompt_buffer.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/prompt_input.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/session_list.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/session_picker.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/skill_picker.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/slash_commands.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/status_footer.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/styles.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/syntax.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/terminal.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/theme_picker.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/thinking_state.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/ui/welcome.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/update_check.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/usage.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/utils/__init__.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/utils/debug_logger.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/utils/error_logger.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/utils/json.py +0 -0
- {deepy_cli-0.2.27 → deepy_cli-0.2.28}/src/deepy/utils/notify.py +0 -0
|
@@ -224,6 +224,7 @@ class DeepyTuiApp(App[None]):
|
|
|
224
224
|
BINDINGS = [
|
|
225
225
|
Binding("ctrl+d", "confirm_quit", "Quit", priority=True),
|
|
226
226
|
Binding("escape", "interrupt_or_focus_prompt", "Interrupt"),
|
|
227
|
+
Binding("ctrl+c,super+c", "copy_focused_block", "Copy", show=False),
|
|
227
228
|
Binding("ctrl+o", "toggle_help_panel", "Panel"),
|
|
228
229
|
Binding("shift+tab", "cycle_audit_mode", "Audit", priority=True),
|
|
229
230
|
Binding("alt+up", "focus_previous_block", "Previous block"),
|
|
@@ -472,12 +473,38 @@ class DeepyTuiApp(App[None]):
|
|
|
472
473
|
color: $text;
|
|
473
474
|
}
|
|
474
475
|
|
|
476
|
+
.subagent-parameters {
|
|
477
|
+
margin: 0 0 0 2;
|
|
478
|
+
padding: 0 0 0 1;
|
|
479
|
+
border-left: solid $secondary;
|
|
480
|
+
color: $text-muted;
|
|
481
|
+
}
|
|
482
|
+
|
|
475
483
|
.tool-details {
|
|
476
484
|
margin: 1 0 0 0;
|
|
477
485
|
color: $text-muted;
|
|
478
486
|
display: none;
|
|
479
487
|
}
|
|
480
488
|
|
|
489
|
+
.subagent-block .tool-details {
|
|
490
|
+
margin: 0 0 0 2;
|
|
491
|
+
padding: 0 0 0 1;
|
|
492
|
+
border-left: solid $accent;
|
|
493
|
+
color: $text;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
.subagent-block.-running .tool-details {
|
|
497
|
+
border-left: solid $accent;
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
.subagent-block.-ok .tool-details {
|
|
501
|
+
border-left: solid $success;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
.subagent-block.-failed .tool-details {
|
|
505
|
+
border-left: solid $error;
|
|
506
|
+
}
|
|
507
|
+
|
|
481
508
|
.tool-block.-retryable .block-title {
|
|
482
509
|
color: $warning;
|
|
483
510
|
}
|
|
@@ -2749,6 +2776,27 @@ class DeepyTuiApp(App[None]):
|
|
|
2749
2776
|
prompt.prepare_clear_on_next_delete()
|
|
2750
2777
|
prompt.focus()
|
|
2751
2778
|
|
|
2779
|
+
def action_copy_focused_block(self) -> None:
|
|
2780
|
+
block = self._focused_transcript_block()
|
|
2781
|
+
if block is None:
|
|
2782
|
+
self._update_status("Focus a transcript block to copy")
|
|
2783
|
+
return
|
|
2784
|
+
text = _transcript_block_copy_text(block).strip()
|
|
2785
|
+
if not text:
|
|
2786
|
+
self._update_status("Nothing to copy")
|
|
2787
|
+
return
|
|
2788
|
+
self.copy_to_clipboard(text)
|
|
2789
|
+
self._update_status("Copied transcript block")
|
|
2790
|
+
|
|
2791
|
+
def _focused_transcript_block(self) -> Widget | None:
|
|
2792
|
+
node = self.focused
|
|
2793
|
+
while isinstance(node, Widget):
|
|
2794
|
+
if node.has_class("transcript-block"):
|
|
2795
|
+
return node
|
|
2796
|
+
parent = node.parent
|
|
2797
|
+
node = parent if isinstance(parent, Widget) else None
|
|
2798
|
+
return None
|
|
2799
|
+
|
|
2752
2800
|
def action_focus_next_block(self) -> None:
|
|
2753
2801
|
blocks = list(self.query(".transcript-block"))
|
|
2754
2802
|
if not blocks:
|
|
@@ -2771,6 +2819,33 @@ class DeepyTuiApp(App[None]):
|
|
|
2771
2819
|
panel.toggle_class("-visible")
|
|
2772
2820
|
|
|
2773
2821
|
|
|
2822
|
+
def _transcript_block_copy_text(block: Widget) -> str:
|
|
2823
|
+
if isinstance(block, UserBlock):
|
|
2824
|
+
return block.body
|
|
2825
|
+
if isinstance(block, AssistantBlock):
|
|
2826
|
+
return block.markdown
|
|
2827
|
+
if isinstance(block, ThinkingBlock):
|
|
2828
|
+
return block.body
|
|
2829
|
+
if isinstance(block, LocalCommandBlock):
|
|
2830
|
+
parts = [block.title, block.output_body]
|
|
2831
|
+
if block.meta_body:
|
|
2832
|
+
parts.append(block.meta_body)
|
|
2833
|
+
return "\n".join(part for part in parts if part)
|
|
2834
|
+
if isinstance(block, ToolBlock):
|
|
2835
|
+
parts = [block.title]
|
|
2836
|
+
if block.tool_name == "todo_write" and block.output_body:
|
|
2837
|
+
parts.append(block.output_body)
|
|
2838
|
+
if block.expanded and block.details:
|
|
2839
|
+
parts.append(block.details)
|
|
2840
|
+
return "\n\n".join(part for part in parts if part)
|
|
2841
|
+
if isinstance(block, DiffBlock):
|
|
2842
|
+
return block.body
|
|
2843
|
+
if isinstance(block, InfoBlock):
|
|
2844
|
+
return block.body
|
|
2845
|
+
body = getattr(block, "body", "")
|
|
2846
|
+
return body if isinstance(body, str) else ""
|
|
2847
|
+
|
|
2848
|
+
|
|
2774
2849
|
async def _load_session_items(project_root: Path, session_id: str) -> list[dict[str, Any]]:
|
|
2775
2850
|
try:
|
|
2776
2851
|
return await DeepySession.open(project_root, session_id).get_items()
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import contextlib
|
|
3
4
|
import time
|
|
4
5
|
from dataclasses import dataclass
|
|
5
6
|
from pathlib import Path
|
|
@@ -743,6 +744,8 @@ class ToolBlock(TranscriptBlock):
|
|
|
743
744
|
recovered_from_retry: bool = False,
|
|
744
745
|
) -> None:
|
|
745
746
|
classes = "tool-block todo-block" if tool_name == "todo_write" else transcript_display("tool").css_class
|
|
747
|
+
if _is_subagent_tool_name(tool_name):
|
|
748
|
+
classes += " subagent-block"
|
|
746
749
|
if retryable:
|
|
747
750
|
classes += " -retryable"
|
|
748
751
|
super().__init__(label, body, classes=classes, kind="tool")
|
|
@@ -765,6 +768,7 @@ class ToolBlock(TranscriptBlock):
|
|
|
765
768
|
title = f"{display_name} running"
|
|
766
769
|
if name == "shell" and params:
|
|
767
770
|
title = f"{title} - {params}"
|
|
771
|
+
details = _subagent_details(params, "") if _is_subagent_tool_name(name) else ""
|
|
768
772
|
body = (
|
|
769
773
|
"Waiting for user input."
|
|
770
774
|
if name == "AskUserQuestion"
|
|
@@ -777,6 +781,7 @@ class ToolBlock(TranscriptBlock):
|
|
|
777
781
|
body,
|
|
778
782
|
call_id=call_id,
|
|
779
783
|
arguments=params,
|
|
784
|
+
details=details,
|
|
780
785
|
tool_name=name or "tool",
|
|
781
786
|
)
|
|
782
787
|
|
|
@@ -809,7 +814,7 @@ class ToolBlock(TranscriptBlock):
|
|
|
809
814
|
self.title = f"{self.title} - {params}"
|
|
810
815
|
self.output_body = "Running"
|
|
811
816
|
self.body = ""
|
|
812
|
-
self.details = ""
|
|
817
|
+
self.details = _subagent_details(params, "") if _is_subagent_tool_name(self.tool_name) else ""
|
|
813
818
|
self.waiting_for_user = False
|
|
814
819
|
self.retryable = False
|
|
815
820
|
self.recovered_from_retry = True
|
|
@@ -818,7 +823,7 @@ class ToolBlock(TranscriptBlock):
|
|
|
818
823
|
self._update_visible_output()
|
|
819
824
|
details = self.query_one(".tool-details", Static)
|
|
820
825
|
details.update(self.details)
|
|
821
|
-
details.display =
|
|
826
|
+
details.display = self._details_visible()
|
|
822
827
|
self.set_class(False, "-waiting")
|
|
823
828
|
self.set_class(False, "-retryable")
|
|
824
829
|
self._sync_status_classes()
|
|
@@ -832,7 +837,11 @@ class ToolBlock(TranscriptBlock):
|
|
|
832
837
|
)
|
|
833
838
|
output_body = _tool_output_body(view)
|
|
834
839
|
self.output_body = output_body
|
|
835
|
-
self.details =
|
|
840
|
+
self.details = (
|
|
841
|
+
_subagent_details(self.arguments, output_body)
|
|
842
|
+
if _is_subagent_tool_name(view.name)
|
|
843
|
+
else _tool_output_details(view)
|
|
844
|
+
)
|
|
836
845
|
self.waiting_for_user = view.await_user_response
|
|
837
846
|
self.retryable = view.status == "retryable"
|
|
838
847
|
self.status_state = "waiting" if self.waiting_for_user else view.status
|
|
@@ -841,16 +850,20 @@ class ToolBlock(TranscriptBlock):
|
|
|
841
850
|
self._update_visible_output()
|
|
842
851
|
details = self.query_one(".tool-details", Static)
|
|
843
852
|
details.update(self.details)
|
|
844
|
-
details.display =
|
|
853
|
+
details.display = self._details_visible()
|
|
845
854
|
self.set_class(self.waiting_for_user, "-waiting")
|
|
846
855
|
self.set_class(self.retryable, "-retryable")
|
|
847
856
|
self._sync_status_classes()
|
|
848
857
|
self.set_class(view.name == "todo_write", "todo-block")
|
|
858
|
+
self.set_class(_is_subagent_tool_name(view.name), "subagent-block")
|
|
849
859
|
|
|
850
860
|
def compose(self) -> ComposeResult:
|
|
851
861
|
with Horizontal(classes="role-line tool-role-line"):
|
|
852
862
|
yield Label(self.display_model.label, classes="block-title role-marker tool-marker")
|
|
853
863
|
yield Static(self.title, classes="block-body tool-summary")
|
|
864
|
+
params = Static(_subagent_parameters(self.arguments), classes="block-body subagent-parameters")
|
|
865
|
+
params.display = self._subagent_parameters_visible()
|
|
866
|
+
yield params
|
|
854
867
|
visible_output = _tool_output_visible(self.tool_name, self.output_body)
|
|
855
868
|
output = Static(
|
|
856
869
|
_tool_output_renderable(self.tool_name, self.output_body) if visible_output else "",
|
|
@@ -864,7 +877,7 @@ class ToolBlock(TranscriptBlock):
|
|
|
864
877
|
|
|
865
878
|
def action_toggle_expand(self) -> None:
|
|
866
879
|
super().action_toggle_expand()
|
|
867
|
-
self.query_one(".tool-details", Static).display =
|
|
880
|
+
self.query_one(".tool-details", Static).display = self._details_visible()
|
|
868
881
|
|
|
869
882
|
def _sync_status_classes(self) -> None:
|
|
870
883
|
self.set_class(self.status_state == "running", "-running")
|
|
@@ -874,11 +887,21 @@ class ToolBlock(TranscriptBlock):
|
|
|
874
887
|
self.set_class(self.status_state == "failed", "-failed")
|
|
875
888
|
|
|
876
889
|
def _update_visible_output(self) -> None:
|
|
890
|
+
with contextlib.suppress(NoMatches):
|
|
891
|
+
params = self.query_one(".subagent-parameters", Static)
|
|
892
|
+
params.update(_subagent_parameters(self.arguments))
|
|
893
|
+
params.display = self._subagent_parameters_visible()
|
|
877
894
|
output = self.query_one(".tool-output", Static)
|
|
878
895
|
visible_output = _tool_output_visible(self.tool_name, self.output_body)
|
|
879
896
|
output.update(_tool_output_renderable(self.tool_name, self.output_body) if visible_output else "")
|
|
880
897
|
output.display = visible_output
|
|
881
898
|
|
|
899
|
+
def _details_visible(self) -> bool:
|
|
900
|
+
return bool(self.details and self.expanded and _is_subagent_tool_name(self.tool_name))
|
|
901
|
+
|
|
902
|
+
def _subagent_parameters_visible(self) -> bool:
|
|
903
|
+
return bool(self.arguments and _is_subagent_tool_name(self.tool_name))
|
|
904
|
+
|
|
882
905
|
|
|
883
906
|
class LocalCommandBlock(Vertical, can_focus=True):
|
|
884
907
|
def __init__(self, view: ToolOutputView, *, call_id: str = "") -> None:
|
|
@@ -1430,6 +1453,10 @@ def _tool_arguments_body(name: str, arguments: str) -> str:
|
|
|
1430
1453
|
return ""
|
|
1431
1454
|
if not arguments.strip():
|
|
1432
1455
|
return ""
|
|
1456
|
+
if _is_subagent_tool_name(name):
|
|
1457
|
+
task = _subagent_input_argument(arguments)
|
|
1458
|
+
if task:
|
|
1459
|
+
return task
|
|
1433
1460
|
if name == "shell":
|
|
1434
1461
|
command = _shell_command_argument(arguments)
|
|
1435
1462
|
if command:
|
|
@@ -1448,6 +1475,41 @@ def _shell_command_argument(arguments: str) -> str:
|
|
|
1448
1475
|
return command.strip() if isinstance(command, str) else ""
|
|
1449
1476
|
|
|
1450
1477
|
|
|
1478
|
+
def _is_subagent_tool_name(name: str) -> bool:
|
|
1479
|
+
return name.startswith("subagent_")
|
|
1480
|
+
|
|
1481
|
+
|
|
1482
|
+
def _subagent_input_argument(arguments: str) -> str:
|
|
1483
|
+
try:
|
|
1484
|
+
args = json_utils.loads(arguments)
|
|
1485
|
+
except json_utils.JSONDecodeError:
|
|
1486
|
+
return ""
|
|
1487
|
+
if not isinstance(args, dict):
|
|
1488
|
+
return ""
|
|
1489
|
+
value = args.get("input") or args.get("task") or args.get("prompt")
|
|
1490
|
+
return value.strip() if isinstance(value, str) else ""
|
|
1491
|
+
|
|
1492
|
+
|
|
1493
|
+
def _subagent_details(task: str, report: str) -> str:
|
|
1494
|
+
parts: list[str] = []
|
|
1495
|
+
compact_task = _compact_text(task, max_lines=4, max_chars=500)
|
|
1496
|
+
if compact_task:
|
|
1497
|
+
parts.extend(["Task", _indent_block(compact_task)])
|
|
1498
|
+
compact_report = _compact_text(report, max_lines=16, max_chars=1600)
|
|
1499
|
+
if compact_report:
|
|
1500
|
+
if parts:
|
|
1501
|
+
parts.append("")
|
|
1502
|
+
parts.extend(["Report", _indent_block(compact_report)])
|
|
1503
|
+
return "\n".join(parts)
|
|
1504
|
+
|
|
1505
|
+
|
|
1506
|
+
def _subagent_parameters(task: str) -> str:
|
|
1507
|
+
compact = _compact_text(task, max_lines=4, max_chars=700)
|
|
1508
|
+
if not compact:
|
|
1509
|
+
return ""
|
|
1510
|
+
return "Subagent Parameters\n" + _indent_block(compact)
|
|
1511
|
+
|
|
1512
|
+
|
|
1451
1513
|
def _tool_output_body(view: ToolOutputView) -> str:
|
|
1452
1514
|
if view.name == "AskUserQuestion":
|
|
1453
1515
|
return "Waiting for user input." if view.await_user_response else _compact_text(view.output or view.summary)
|
|
@@ -1470,6 +1532,8 @@ def _tool_output_body(view: ToolOutputView) -> str:
|
|
|
1470
1532
|
return _todo_body(view)
|
|
1471
1533
|
if view.name in {"WebSearch", "WebFetch"}:
|
|
1472
1534
|
return _web_body(view)
|
|
1535
|
+
if _is_subagent_tool_name(view.name):
|
|
1536
|
+
return (view.error or view.output or view.summary).strip()
|
|
1473
1537
|
if _is_mcp_view(view):
|
|
1474
1538
|
return _mcp_body(view)
|
|
1475
1539
|
if view.ok is False and view.metadata:
|
|
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
|
|
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
|
|
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
|