klaude-code 1.9.0__py3-none-any.whl → 2.0.1__py3-none-any.whl
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.
- klaude_code/auth/base.py +2 -6
- klaude_code/cli/auth_cmd.py +4 -4
- klaude_code/cli/cost_cmd.py +1 -1
- klaude_code/cli/list_model.py +1 -1
- klaude_code/cli/main.py +1 -1
- klaude_code/cli/runtime.py +7 -5
- klaude_code/cli/self_update.py +1 -1
- klaude_code/cli/session_cmd.py +1 -1
- klaude_code/command/clear_cmd.py +6 -2
- klaude_code/command/command_abc.py +2 -2
- klaude_code/command/debug_cmd.py +4 -4
- klaude_code/command/export_cmd.py +2 -2
- klaude_code/command/export_online_cmd.py +12 -12
- klaude_code/command/fork_session_cmd.py +29 -23
- klaude_code/command/help_cmd.py +4 -4
- klaude_code/command/model_cmd.py +4 -4
- klaude_code/command/model_select.py +1 -1
- klaude_code/command/prompt-commit.md +11 -2
- klaude_code/command/prompt_command.py +3 -3
- klaude_code/command/refresh_cmd.py +2 -2
- klaude_code/command/registry.py +7 -5
- klaude_code/command/release_notes_cmd.py +4 -4
- klaude_code/command/resume_cmd.py +15 -11
- klaude_code/command/status_cmd.py +4 -4
- klaude_code/command/terminal_setup_cmd.py +8 -8
- klaude_code/command/thinking_cmd.py +4 -4
- klaude_code/config/assets/builtin_config.yaml +20 -0
- klaude_code/config/builtin_config.py +16 -5
- klaude_code/config/config.py +7 -2
- klaude_code/const.py +147 -91
- klaude_code/core/agent.py +3 -12
- klaude_code/core/executor.py +18 -39
- klaude_code/core/manager/sub_agent_manager.py +71 -7
- klaude_code/core/prompts/prompt-sub-agent-image-gen.md +1 -0
- klaude_code/core/prompts/prompt-sub-agent-web.md +27 -1
- klaude_code/core/reminders.py +88 -69
- klaude_code/core/task.py +44 -45
- klaude_code/core/tool/file/apply_patch_tool.py +9 -9
- klaude_code/core/tool/file/diff_builder.py +3 -5
- klaude_code/core/tool/file/edit_tool.py +23 -23
- klaude_code/core/tool/file/move_tool.py +43 -43
- klaude_code/core/tool/file/read_tool.py +44 -39
- klaude_code/core/tool/file/write_tool.py +14 -14
- klaude_code/core/tool/report_back_tool.py +4 -4
- klaude_code/core/tool/shell/bash_tool.py +23 -23
- klaude_code/core/tool/skill/skill_tool.py +7 -7
- klaude_code/core/tool/sub_agent_tool.py +38 -9
- klaude_code/core/tool/todo/todo_write_tool.py +9 -10
- klaude_code/core/tool/todo/update_plan_tool.py +6 -6
- klaude_code/core/tool/tool_abc.py +2 -2
- klaude_code/core/tool/tool_context.py +27 -0
- klaude_code/core/tool/tool_runner.py +88 -42
- klaude_code/core/tool/truncation.py +38 -20
- klaude_code/core/tool/web/mermaid_tool.py +6 -7
- klaude_code/core/tool/web/web_fetch_tool.py +68 -30
- klaude_code/core/tool/web/web_search_tool.py +15 -17
- klaude_code/core/turn.py +120 -73
- klaude_code/llm/anthropic/client.py +79 -44
- klaude_code/llm/anthropic/input.py +116 -108
- klaude_code/llm/bedrock/client.py +8 -5
- klaude_code/llm/claude/client.py +18 -8
- klaude_code/llm/client.py +4 -3
- klaude_code/llm/codex/client.py +15 -9
- klaude_code/llm/google/client.py +122 -60
- klaude_code/llm/google/input.py +94 -108
- klaude_code/llm/image.py +123 -0
- klaude_code/llm/input_common.py +136 -189
- klaude_code/llm/openai_compatible/client.py +17 -7
- klaude_code/llm/openai_compatible/input.py +36 -66
- klaude_code/llm/openai_compatible/stream.py +119 -67
- klaude_code/llm/openai_compatible/tool_call_accumulator.py +23 -11
- klaude_code/llm/openrouter/client.py +34 -9
- klaude_code/llm/openrouter/input.py +63 -64
- klaude_code/llm/openrouter/reasoning.py +22 -24
- klaude_code/llm/registry.py +20 -17
- klaude_code/llm/responses/client.py +107 -45
- klaude_code/llm/responses/input.py +115 -98
- klaude_code/llm/usage.py +52 -25
- klaude_code/protocol/__init__.py +1 -0
- klaude_code/protocol/events.py +16 -12
- klaude_code/protocol/llm_param.py +20 -2
- klaude_code/protocol/message.py +250 -0
- klaude_code/protocol/model.py +95 -285
- klaude_code/protocol/op.py +2 -15
- klaude_code/protocol/op_handler.py +0 -5
- klaude_code/protocol/sub_agent/__init__.py +1 -0
- klaude_code/protocol/sub_agent/explore.py +10 -0
- klaude_code/protocol/sub_agent/image_gen.py +119 -0
- klaude_code/protocol/sub_agent/task.py +10 -0
- klaude_code/protocol/sub_agent/web.py +10 -0
- klaude_code/session/codec.py +6 -6
- klaude_code/session/export.py +261 -62
- klaude_code/session/selector.py +7 -24
- klaude_code/session/session.py +126 -54
- klaude_code/session/store.py +5 -32
- klaude_code/session/templates/export_session.html +1 -1
- klaude_code/session/templates/mermaid_viewer.html +1 -1
- klaude_code/trace/log.py +11 -6
- klaude_code/ui/core/input.py +1 -1
- klaude_code/ui/core/stage_manager.py +1 -8
- klaude_code/ui/modes/debug/display.py +2 -2
- klaude_code/ui/modes/repl/clipboard.py +2 -2
- klaude_code/ui/modes/repl/completers.py +18 -10
- klaude_code/ui/modes/repl/event_handler.py +138 -132
- klaude_code/ui/modes/repl/input_prompt_toolkit.py +1 -1
- klaude_code/ui/modes/repl/key_bindings.py +136 -2
- klaude_code/ui/modes/repl/renderer.py +107 -15
- klaude_code/ui/renderers/assistant.py +2 -2
- klaude_code/ui/renderers/bash_syntax.py +36 -4
- klaude_code/ui/renderers/common.py +70 -10
- klaude_code/ui/renderers/developer.py +7 -6
- klaude_code/ui/renderers/diffs.py +11 -11
- klaude_code/ui/renderers/mermaid_viewer.py +49 -2
- klaude_code/ui/renderers/metadata.py +33 -5
- klaude_code/ui/renderers/sub_agent.py +57 -16
- klaude_code/ui/renderers/thinking.py +37 -2
- klaude_code/ui/renderers/tools.py +188 -178
- klaude_code/ui/rich/live.py +3 -1
- klaude_code/ui/rich/markdown.py +39 -7
- klaude_code/ui/rich/quote.py +76 -1
- klaude_code/ui/rich/status.py +14 -8
- klaude_code/ui/rich/theme.py +20 -14
- klaude_code/ui/terminal/image.py +34 -0
- klaude_code/ui/terminal/notifier.py +2 -1
- klaude_code/ui/terminal/progress_bar.py +4 -4
- klaude_code/ui/terminal/selector.py +22 -4
- klaude_code/ui/utils/common.py +11 -2
- {klaude_code-1.9.0.dist-info → klaude_code-2.0.1.dist-info}/METADATA +4 -2
- klaude_code-2.0.1.dist-info/RECORD +229 -0
- klaude_code-1.9.0.dist-info/RECORD +0 -224
- {klaude_code-1.9.0.dist-info → klaude_code-2.0.1.dist-info}/WHEEL +0 -0
- {klaude_code-1.9.0.dist-info → klaude_code-2.0.1.dist-info}/entry_points.txt +0 -0
|
@@ -4,20 +4,26 @@ from typing import Any, cast
|
|
|
4
4
|
|
|
5
5
|
from rich import box
|
|
6
6
|
from rich.console import Group, RenderableType
|
|
7
|
-
from rich.padding import Padding
|
|
8
7
|
from rich.panel import Panel
|
|
9
8
|
from rich.style import Style
|
|
10
9
|
from rich.text import Text
|
|
11
10
|
|
|
12
|
-
from klaude_code import
|
|
11
|
+
from klaude_code.const import (
|
|
12
|
+
BASH_OUTPUT_PANEL_THRESHOLD,
|
|
13
|
+
INVALID_TOOL_CALL_MAX_LENGTH,
|
|
14
|
+
QUERY_DISPLAY_TRUNCATE_LENGTH,
|
|
15
|
+
URL_TRUNCATE_MAX_LENGTH,
|
|
16
|
+
WEB_SEARCH_DEFAULT_MAX_RESULTS,
|
|
17
|
+
)
|
|
13
18
|
from klaude_code.protocol import events, model, tools
|
|
14
19
|
from klaude_code.protocol.sub_agent import is_sub_agent_tool as _is_sub_agent_tool
|
|
15
20
|
from klaude_code.ui.renderers import diffs as r_diffs
|
|
16
21
|
from klaude_code.ui.renderers import mermaid_viewer as r_mermaid_viewer
|
|
17
22
|
from klaude_code.ui.renderers.bash_syntax import highlight_bash_command
|
|
18
|
-
from klaude_code.ui.renderers.common import create_grid,
|
|
23
|
+
from klaude_code.ui.renderers.common import create_grid, truncate_middle
|
|
19
24
|
from klaude_code.ui.rich.code_panel import CodePanel
|
|
20
25
|
from klaude_code.ui.rich.markdown import NoInsetMarkdown
|
|
26
|
+
from klaude_code.ui.rich.quote import TreeQuote
|
|
21
27
|
from klaude_code.ui.rich.theme import ThemeKey
|
|
22
28
|
|
|
23
29
|
# Tool markers (Unicode symbols for UI display)
|
|
@@ -56,86 +62,106 @@ def render_path(path: str, style: str, is_directory: bool = False) -> Text:
|
|
|
56
62
|
return Text(path, style=style)
|
|
57
63
|
|
|
58
64
|
|
|
59
|
-
def
|
|
60
|
-
|
|
65
|
+
def _render_tool_call_tree(
|
|
66
|
+
*,
|
|
67
|
+
mark: str,
|
|
68
|
+
tool_name: str,
|
|
69
|
+
details: RenderableType | None,
|
|
70
|
+
) -> RenderableType:
|
|
71
|
+
grid = create_grid(overflow="ellipsis")
|
|
72
|
+
grid.add_row(
|
|
73
|
+
Text(tool_name, style=ThemeKey.TOOL_NAME),
|
|
74
|
+
details if details is not None else Text(""),
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
return TreeQuote.for_tool_call(
|
|
78
|
+
grid,
|
|
79
|
+
mark=mark,
|
|
80
|
+
style=ThemeKey.TOOL_RESULT_TREE_PREFIX,
|
|
81
|
+
style_first=ThemeKey.TOOL_MARK,
|
|
82
|
+
)
|
|
61
83
|
|
|
62
|
-
|
|
63
|
-
|
|
84
|
+
|
|
85
|
+
def render_generic_tool_call(tool_name: str, arguments: str, markup: str = MARK_GENERIC) -> RenderableType:
|
|
64
86
|
if not arguments:
|
|
65
|
-
|
|
66
|
-
|
|
87
|
+
return _render_tool_call_tree(mark=markup, tool_name=tool_name, details=None)
|
|
88
|
+
|
|
89
|
+
details: RenderableType
|
|
67
90
|
try:
|
|
68
|
-
|
|
69
|
-
if len(json_dict) == 0:
|
|
70
|
-
arguments_column = Text("", ThemeKey.TOOL_PARAM)
|
|
71
|
-
elif len(json_dict) == 1:
|
|
72
|
-
arguments_column = Text(str(next(iter(json_dict.values()))), ThemeKey.TOOL_PARAM)
|
|
73
|
-
else:
|
|
74
|
-
arguments_column = Text(
|
|
75
|
-
", ".join([f"{k}: {v}" for k, v in json_dict.items()]),
|
|
76
|
-
ThemeKey.TOOL_PARAM,
|
|
77
|
-
)
|
|
91
|
+
payload = json.loads(arguments)
|
|
78
92
|
except json.JSONDecodeError:
|
|
79
|
-
|
|
80
|
-
arguments.strip()[:
|
|
93
|
+
details = Text(
|
|
94
|
+
arguments.strip()[:INVALID_TOOL_CALL_MAX_LENGTH],
|
|
81
95
|
style=ThemeKey.INVALID_TOOL_CALL_ARGS,
|
|
82
96
|
)
|
|
83
|
-
|
|
84
|
-
|
|
97
|
+
else:
|
|
98
|
+
if isinstance(payload, dict):
|
|
99
|
+
payload_dict = cast(dict[str, Any], payload)
|
|
100
|
+
if len(payload_dict) == 0:
|
|
101
|
+
details = Text("", ThemeKey.TOOL_PARAM)
|
|
102
|
+
elif len(payload_dict) == 1:
|
|
103
|
+
details = Text(str(next(iter(payload_dict.values()))), ThemeKey.TOOL_PARAM)
|
|
104
|
+
else:
|
|
105
|
+
details = Text(
|
|
106
|
+
", ".join([f"{k}: {v}" for k, v in payload_dict.items()]),
|
|
107
|
+
ThemeKey.TOOL_PARAM,
|
|
108
|
+
)
|
|
109
|
+
else:
|
|
110
|
+
details = Text(str(payload)[:INVALID_TOOL_CALL_MAX_LENGTH], style=ThemeKey.INVALID_TOOL_CALL_ARGS)
|
|
111
|
+
|
|
112
|
+
return _render_tool_call_tree(mark=markup, tool_name=tool_name, details=details)
|
|
85
113
|
|
|
86
114
|
|
|
87
115
|
def render_bash_tool_call(arguments: str) -> RenderableType:
|
|
88
|
-
|
|
89
|
-
tool_name_column = Text.assemble((MARK_BASH, ThemeKey.TOOL_MARK), " ", ("Bash", ThemeKey.TOOL_NAME))
|
|
116
|
+
tool_name = "Bash"
|
|
90
117
|
|
|
91
118
|
try:
|
|
92
119
|
payload_raw: Any = json.loads(arguments) if arguments else {}
|
|
93
120
|
except json.JSONDecodeError:
|
|
94
|
-
|
|
95
|
-
arguments.strip()[:
|
|
121
|
+
details: RenderableType = Text(
|
|
122
|
+
arguments.strip()[:INVALID_TOOL_CALL_MAX_LENGTH],
|
|
96
123
|
style=ThemeKey.INVALID_TOOL_CALL_ARGS,
|
|
97
124
|
)
|
|
98
|
-
|
|
99
|
-
return grid
|
|
125
|
+
return _render_tool_call_tree(mark=MARK_BASH, tool_name=tool_name, details=details)
|
|
100
126
|
|
|
101
127
|
if not isinstance(payload_raw, dict):
|
|
102
|
-
|
|
103
|
-
str(payload_raw)[:
|
|
128
|
+
details = Text(
|
|
129
|
+
str(payload_raw)[:INVALID_TOOL_CALL_MAX_LENGTH],
|
|
104
130
|
style=ThemeKey.INVALID_TOOL_CALL_ARGS,
|
|
105
131
|
)
|
|
106
|
-
|
|
107
|
-
return grid
|
|
132
|
+
return _render_tool_call_tree(mark=MARK_BASH, tool_name=tool_name, details=details)
|
|
108
133
|
|
|
109
134
|
payload: dict[str, object] = cast(dict[str, object], payload_raw)
|
|
110
135
|
|
|
111
136
|
command = payload.get("command")
|
|
112
137
|
timeout_ms = payload.get("timeout_ms")
|
|
113
138
|
|
|
114
|
-
# Build the command display with optional timeout suffix
|
|
115
139
|
if isinstance(command, str) and command.strip():
|
|
116
140
|
cmd_str = command.strip()
|
|
117
|
-
line_count = len(cmd_str.splitlines())
|
|
118
|
-
|
|
119
141
|
highlighted = highlight_bash_command(cmd_str)
|
|
120
142
|
|
|
121
|
-
|
|
122
|
-
|
|
143
|
+
display_line_count = len(highlighted.plain.splitlines())
|
|
144
|
+
|
|
145
|
+
if display_line_count > BASH_OUTPUT_PANEL_THRESHOLD:
|
|
123
146
|
code_panel = CodePanel(highlighted, border_style=ThemeKey.LINES)
|
|
124
147
|
if isinstance(timeout_ms, int):
|
|
125
148
|
if timeout_ms >= 1000 and timeout_ms % 1000 == 0:
|
|
126
149
|
timeout_text = Text(f"{timeout_ms // 1000}s", style=ThemeKey.TOOL_TIMEOUT)
|
|
127
150
|
else:
|
|
128
151
|
timeout_text = Text(f"{timeout_ms}ms", style=ThemeKey.TOOL_TIMEOUT)
|
|
129
|
-
|
|
152
|
+
return _render_tool_call_tree(
|
|
153
|
+
mark=MARK_BASH,
|
|
154
|
+
tool_name=tool_name,
|
|
155
|
+
details=Group(code_panel, timeout_text),
|
|
156
|
+
)
|
|
130
157
|
else:
|
|
131
|
-
|
|
132
|
-
return grid
|
|
158
|
+
return _render_tool_call_tree(mark=MARK_BASH, tool_name=tool_name, details=code_panel)
|
|
133
159
|
if isinstance(timeout_ms, int):
|
|
134
160
|
if timeout_ms >= 1000 and timeout_ms % 1000 == 0:
|
|
135
161
|
highlighted.append(f" {timeout_ms // 1000}s", style=ThemeKey.TOOL_TIMEOUT)
|
|
136
162
|
else:
|
|
137
163
|
highlighted.append(f" {timeout_ms}ms", style=ThemeKey.TOOL_TIMEOUT)
|
|
138
|
-
|
|
164
|
+
return _render_tool_call_tree(mark=MARK_BASH, tool_name=tool_name, details=highlighted)
|
|
139
165
|
else:
|
|
140
166
|
summary = Text("", ThemeKey.TOOL_PARAM)
|
|
141
167
|
if isinstance(timeout_ms, int):
|
|
@@ -143,77 +169,73 @@ def render_bash_tool_call(arguments: str) -> RenderableType:
|
|
|
143
169
|
summary.append(f"{timeout_ms // 1000}s", style=ThemeKey.TOOL_TIMEOUT)
|
|
144
170
|
else:
|
|
145
171
|
summary.append(f"{timeout_ms}ms", style=ThemeKey.TOOL_TIMEOUT)
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
return grid
|
|
172
|
+
bash_details: RenderableType | None = summary if summary.plain else None
|
|
173
|
+
return _render_tool_call_tree(mark=MARK_BASH, tool_name=tool_name, details=bash_details)
|
|
149
174
|
|
|
150
175
|
|
|
151
176
|
def render_update_plan_tool_call(arguments: str) -> RenderableType:
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
explanation_column = Text("")
|
|
177
|
+
tool_name = "Update Plan"
|
|
178
|
+
details: RenderableType | None = None
|
|
155
179
|
|
|
156
180
|
if arguments:
|
|
157
181
|
try:
|
|
158
182
|
payload = json.loads(arguments)
|
|
159
183
|
except json.JSONDecodeError:
|
|
160
|
-
|
|
161
|
-
arguments.strip()[:
|
|
184
|
+
details = Text(
|
|
185
|
+
arguments.strip()[:INVALID_TOOL_CALL_MAX_LENGTH],
|
|
162
186
|
style=ThemeKey.INVALID_TOOL_CALL_ARGS,
|
|
163
187
|
)
|
|
164
188
|
else:
|
|
165
189
|
explanation = payload.get("explanation")
|
|
166
190
|
if isinstance(explanation, str) and explanation.strip():
|
|
167
|
-
|
|
191
|
+
details = Text(explanation.strip(), style=ThemeKey.TODO_EXPLANATION)
|
|
168
192
|
|
|
169
|
-
|
|
170
|
-
return grid
|
|
193
|
+
return _render_tool_call_tree(mark=MARK_PLAN, tool_name=tool_name, details=details)
|
|
171
194
|
|
|
172
195
|
|
|
173
196
|
def render_read_tool_call(arguments: str) -> RenderableType:
|
|
174
|
-
|
|
175
|
-
|
|
197
|
+
tool_name = "Read"
|
|
198
|
+
details = Text("", ThemeKey.TOOL_PARAM)
|
|
176
199
|
try:
|
|
177
200
|
json_dict = json.loads(arguments)
|
|
178
201
|
file_path = json_dict.get("file_path")
|
|
179
202
|
limit = json_dict.get("limit", None)
|
|
180
203
|
offset = json_dict.get("offset", None)
|
|
181
|
-
|
|
204
|
+
if isinstance(file_path, str) and file_path:
|
|
205
|
+
details.append_text(render_path(file_path, ThemeKey.TOOL_PARAM_FILE_PATH))
|
|
206
|
+
else:
|
|
207
|
+
details.append("(no file_path)", style=ThemeKey.TOOL_PARAM)
|
|
182
208
|
if limit is not None and offset is not None:
|
|
183
|
-
|
|
184
|
-
|
|
209
|
+
details = (
|
|
210
|
+
details.append_text(Text(" "))
|
|
185
211
|
.append_text(Text(str(offset), ThemeKey.TOOL_PARAM_BOLD))
|
|
186
212
|
.append_text(Text(":", ThemeKey.TOOL_PARAM))
|
|
187
213
|
.append_text(Text(str(offset + limit - 1), ThemeKey.TOOL_PARAM_BOLD))
|
|
188
214
|
)
|
|
189
215
|
elif limit is not None:
|
|
190
|
-
|
|
191
|
-
|
|
216
|
+
details = (
|
|
217
|
+
details.append_text(Text(" "))
|
|
192
218
|
.append_text(Text("1", ThemeKey.TOOL_PARAM_BOLD))
|
|
193
219
|
.append_text(Text(":", ThemeKey.TOOL_PARAM))
|
|
194
220
|
.append_text(Text(str(limit), ThemeKey.TOOL_PARAM_BOLD))
|
|
195
221
|
)
|
|
196
222
|
elif offset is not None:
|
|
197
|
-
|
|
198
|
-
|
|
223
|
+
details = (
|
|
224
|
+
details.append_text(Text(" "))
|
|
199
225
|
.append_text(Text(str(offset), ThemeKey.TOOL_PARAM_BOLD))
|
|
200
226
|
.append_text(Text(":", ThemeKey.TOOL_PARAM))
|
|
201
227
|
.append_text(Text("-", ThemeKey.TOOL_PARAM_BOLD))
|
|
202
228
|
)
|
|
203
229
|
except json.JSONDecodeError:
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
style=ThemeKey.INVALID_TOOL_CALL_ARGS,
|
|
208
|
-
)
|
|
230
|
+
details = Text(
|
|
231
|
+
arguments.strip()[:INVALID_TOOL_CALL_MAX_LENGTH],
|
|
232
|
+
style=ThemeKey.INVALID_TOOL_CALL_ARGS,
|
|
209
233
|
)
|
|
210
|
-
|
|
211
|
-
return grid
|
|
234
|
+
return _render_tool_call_tree(mark=MARK_READ, tool_name=tool_name, details=details)
|
|
212
235
|
|
|
213
236
|
|
|
214
237
|
def render_edit_tool_call(arguments: str) -> RenderableType:
|
|
215
|
-
|
|
216
|
-
tool_name_column = Text.assemble((MARK_EDIT, ThemeKey.TOOL_MARK), " ", ("Edit", ThemeKey.TOOL_NAME))
|
|
238
|
+
tool_name = "Edit"
|
|
217
239
|
try:
|
|
218
240
|
json_dict = json.loads(arguments)
|
|
219
241
|
file_path = json_dict.get("file_path")
|
|
@@ -226,59 +248,52 @@ def render_edit_tool_call(arguments: str) -> RenderableType:
|
|
|
226
248
|
replace_info.append(old_string, ThemeKey.BASH_STRING)
|
|
227
249
|
replace_info.append(" → ", ThemeKey.BASH_OPERATOR)
|
|
228
250
|
replace_info.append(new_string, ThemeKey.BASH_STRING)
|
|
229
|
-
|
|
251
|
+
details: RenderableType = Group(path_text, replace_info)
|
|
230
252
|
else:
|
|
231
|
-
|
|
253
|
+
details = path_text
|
|
232
254
|
except json.JSONDecodeError:
|
|
233
|
-
|
|
234
|
-
arguments.strip()[:
|
|
255
|
+
details = Text(
|
|
256
|
+
arguments.strip()[:INVALID_TOOL_CALL_MAX_LENGTH],
|
|
235
257
|
style=ThemeKey.INVALID_TOOL_CALL_ARGS,
|
|
236
258
|
)
|
|
237
|
-
|
|
238
|
-
return grid
|
|
259
|
+
return _render_tool_call_tree(mark=MARK_EDIT, tool_name=tool_name, details=details)
|
|
239
260
|
|
|
240
261
|
|
|
241
262
|
def render_write_tool_call(arguments: str) -> RenderableType:
|
|
242
|
-
|
|
263
|
+
tool_name = "Write"
|
|
243
264
|
try:
|
|
244
265
|
json_dict = json.loads(arguments)
|
|
245
266
|
file_path = json_dict.get("file_path", "")
|
|
246
|
-
tool_name_column = Text.assemble((MARK_WRITE, ThemeKey.TOOL_MARK), " ", ("Write", ThemeKey.TOOL_NAME))
|
|
247
267
|
# Markdown files show path in result panel, skip here to avoid duplication
|
|
248
268
|
if file_path.endswith(".md"):
|
|
249
|
-
|
|
269
|
+
details: RenderableType | None = None
|
|
250
270
|
else:
|
|
251
|
-
|
|
271
|
+
details = render_path(file_path, ThemeKey.TOOL_PARAM_FILE_PATH)
|
|
252
272
|
except json.JSONDecodeError:
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
arguments.strip()[: const.INVALID_TOOL_CALL_MAX_LENGTH],
|
|
273
|
+
details = Text(
|
|
274
|
+
arguments.strip()[:INVALID_TOOL_CALL_MAX_LENGTH],
|
|
256
275
|
style=ThemeKey.INVALID_TOOL_CALL_ARGS,
|
|
257
276
|
)
|
|
258
|
-
|
|
259
|
-
return grid
|
|
277
|
+
return _render_tool_call_tree(mark=MARK_WRITE, tool_name=tool_name, details=details)
|
|
260
278
|
|
|
261
279
|
|
|
262
280
|
def render_move_tool_call(arguments: str) -> RenderableType:
|
|
263
|
-
|
|
264
|
-
tool_name_column = Text.assemble((MARK_MOVE, ThemeKey.TOOL_MARK), " ", ("Move", ThemeKey.TOOL_NAME))
|
|
281
|
+
tool_name = "Move"
|
|
265
282
|
|
|
266
283
|
try:
|
|
267
284
|
payload = json.loads(arguments)
|
|
268
285
|
except json.JSONDecodeError:
|
|
269
|
-
|
|
270
|
-
arguments.strip()[:
|
|
286
|
+
details = Text(
|
|
287
|
+
arguments.strip()[:INVALID_TOOL_CALL_MAX_LENGTH],
|
|
271
288
|
style=ThemeKey.INVALID_TOOL_CALL_ARGS,
|
|
272
289
|
)
|
|
273
|
-
|
|
274
|
-
return grid
|
|
290
|
+
return _render_tool_call_tree(mark=MARK_MOVE, tool_name=tool_name, details=details)
|
|
275
291
|
|
|
276
292
|
source_path = payload.get("source_file_path", "")
|
|
277
293
|
target_path = payload.get("target_file_path", "")
|
|
278
294
|
start_line = payload.get("start_line", "")
|
|
279
295
|
end_line = payload.get("end_line", "")
|
|
280
296
|
|
|
281
|
-
# Build display: source:start-end -> target
|
|
282
297
|
parts = Text()
|
|
283
298
|
if source_path:
|
|
284
299
|
parts.append_text(render_path(source_path, ThemeKey.TOOL_PARAM_FILE_PATH))
|
|
@@ -288,26 +303,23 @@ def render_move_tool_call(arguments: str) -> RenderableType:
|
|
|
288
303
|
if target_path:
|
|
289
304
|
parts.append_text(render_path(target_path, ThemeKey.TOOL_PARAM_FILE_PATH))
|
|
290
305
|
|
|
291
|
-
|
|
292
|
-
return grid
|
|
306
|
+
return _render_tool_call_tree(mark=MARK_MOVE, tool_name=tool_name, details=parts)
|
|
293
307
|
|
|
294
308
|
|
|
295
309
|
def render_apply_patch_tool_call(arguments: str) -> RenderableType:
|
|
296
|
-
|
|
297
|
-
tool_name_column = Text.assemble((MARK_EDIT, ThemeKey.TOOL_MARK), " ", ("Apply Patch", ThemeKey.TOOL_NAME))
|
|
310
|
+
tool_name = "Apply Patch"
|
|
298
311
|
|
|
299
312
|
try:
|
|
300
313
|
payload = json.loads(arguments)
|
|
301
314
|
except json.JSONDecodeError:
|
|
302
|
-
|
|
303
|
-
arguments.strip()[:
|
|
315
|
+
details = Text(
|
|
316
|
+
arguments.strip()[:INVALID_TOOL_CALL_MAX_LENGTH],
|
|
304
317
|
style=ThemeKey.INVALID_TOOL_CALL_ARGS,
|
|
305
318
|
)
|
|
306
|
-
|
|
307
|
-
return grid
|
|
319
|
+
return _render_tool_call_tree(mark=MARK_EDIT, tool_name=tool_name, details=details)
|
|
308
320
|
|
|
309
321
|
patch_content = payload.get("patch", "")
|
|
310
|
-
|
|
322
|
+
details = Text("", ThemeKey.TOOL_PARAM)
|
|
311
323
|
|
|
312
324
|
if isinstance(patch_content, str):
|
|
313
325
|
update_count = 0
|
|
@@ -330,15 +342,14 @@ def render_apply_patch_tool_call(arguments: str) -> RenderableType:
|
|
|
330
342
|
parts.append(f"Delete File × {delete_count}" if delete_count > 1 else "Delete File")
|
|
331
343
|
|
|
332
344
|
if parts:
|
|
333
|
-
|
|
345
|
+
details = Text(", ".join(parts), ThemeKey.TOOL_PARAM)
|
|
334
346
|
else:
|
|
335
|
-
|
|
336
|
-
str(patch_content)[:
|
|
347
|
+
details = Text(
|
|
348
|
+
str(patch_content)[:INVALID_TOOL_CALL_MAX_LENGTH],
|
|
337
349
|
ThemeKey.INVALID_TOOL_CALL_ARGS,
|
|
338
350
|
)
|
|
339
351
|
|
|
340
|
-
|
|
341
|
-
return grid
|
|
352
|
+
return _render_tool_call_tree(mark=MARK_EDIT, tool_name=tool_name, details=details)
|
|
342
353
|
|
|
343
354
|
|
|
344
355
|
def render_todo(tr: events.ToolResultEvent) -> RenderableType:
|
|
@@ -364,33 +375,34 @@ def render_todo(tr: events.ToolResultEvent) -> RenderableType:
|
|
|
364
375
|
text.stylize(text_style)
|
|
365
376
|
todo_grid.add_row(Text(mark, style=mark_style), text)
|
|
366
377
|
|
|
367
|
-
return
|
|
378
|
+
return todo_grid
|
|
368
379
|
|
|
369
380
|
|
|
370
381
|
def render_generic_tool_result(result: str, *, is_error: bool = False) -> RenderableType:
|
|
371
|
-
"""Render a generic tool result as
|
|
382
|
+
"""Render a generic tool result as truncated text."""
|
|
372
383
|
style = ThemeKey.ERROR if is_error else ThemeKey.TOOL_RESULT
|
|
373
|
-
|
|
384
|
+
text = truncate_middle(result, base_style=style)
|
|
385
|
+
# Tool results should not reflow/wrap; use ellipsis when exceeding terminal width.
|
|
386
|
+
text.no_wrap = True
|
|
387
|
+
text.overflow = "ellipsis"
|
|
388
|
+
return text
|
|
374
389
|
|
|
375
390
|
|
|
376
391
|
def _extract_mermaid_link(
|
|
377
392
|
ui_extra: model.ToolResultUIExtra | None,
|
|
378
393
|
) -> model.MermaidLinkUIExtra | None:
|
|
379
|
-
if isinstance(ui_extra, model.MermaidLinkUIExtra)
|
|
380
|
-
return ui_extra
|
|
381
|
-
return None
|
|
394
|
+
return ui_extra if isinstance(ui_extra, model.MermaidLinkUIExtra) else None
|
|
382
395
|
|
|
383
396
|
|
|
384
397
|
def render_mermaid_tool_call(arguments: str) -> RenderableType:
|
|
385
|
-
|
|
386
|
-
tool_name_column = Text.assemble((MARK_MERMAID, ThemeKey.TOOL_MARK), " ", ("Mermaid", ThemeKey.TOOL_NAME))
|
|
398
|
+
tool_name = "Mermaid"
|
|
387
399
|
summary = Text("", ThemeKey.TOOL_PARAM)
|
|
388
400
|
|
|
389
401
|
try:
|
|
390
402
|
payload: dict[str, str] = json.loads(arguments)
|
|
391
403
|
except json.JSONDecodeError:
|
|
392
404
|
summary = Text(
|
|
393
|
-
arguments.strip()[:
|
|
405
|
+
arguments.strip()[:INVALID_TOOL_CALL_MAX_LENGTH],
|
|
394
406
|
style=ThemeKey.INVALID_TOOL_CALL_ARGS,
|
|
395
407
|
)
|
|
396
408
|
else:
|
|
@@ -401,11 +413,10 @@ def render_mermaid_tool_call(arguments: str) -> RenderableType:
|
|
|
401
413
|
else:
|
|
402
414
|
summary = Text("0 lines", ThemeKey.TOOL_PARAM)
|
|
403
415
|
|
|
404
|
-
|
|
405
|
-
return grid
|
|
416
|
+
return _render_tool_call_tree(mark=MARK_MERMAID, tool_name=tool_name, details=summary)
|
|
406
417
|
|
|
407
418
|
|
|
408
|
-
def _truncate_url(url: str, max_length: int =
|
|
419
|
+
def _truncate_url(url: str, max_length: int = URL_TRUNCATE_MAX_LENGTH) -> str:
|
|
409
420
|
"""Truncate URL for display, preserving domain and path structure."""
|
|
410
421
|
if len(url) <= max_length:
|
|
411
422
|
return url
|
|
@@ -418,7 +429,7 @@ def _truncate_url(url: str, max_length: int = 400) -> str:
|
|
|
418
429
|
if len(display_url) <= max_length:
|
|
419
430
|
return display_url
|
|
420
431
|
# Truncate with ellipsis
|
|
421
|
-
return display_url[: max_length -
|
|
432
|
+
return display_url[: max_length - 1] + "…"
|
|
422
433
|
|
|
423
434
|
|
|
424
435
|
def _render_mermaid_viewer_link(
|
|
@@ -429,7 +440,7 @@ def _render_mermaid_viewer_link(
|
|
|
429
440
|
) -> RenderableType:
|
|
430
441
|
viewer_path = r_mermaid_viewer.build_viewer(code=link_info.code, link=link_info.link, tool_call_id=tr.tool_call_id)
|
|
431
442
|
if viewer_path is None:
|
|
432
|
-
return Text(link_info.link, style=ThemeKey.TOOL_RESULT_MERMAID, overflow="
|
|
443
|
+
return Text(link_info.link, style=ThemeKey.TOOL_RESULT_MERMAID, overflow="ellipsis", no_wrap=True)
|
|
433
444
|
|
|
434
445
|
display_path = str(viewer_path)
|
|
435
446
|
|
|
@@ -440,7 +451,7 @@ def _render_mermaid_viewer_link(
|
|
|
440
451
|
except ValueError:
|
|
441
452
|
file_url = f"file://{viewer_path.as_posix()}"
|
|
442
453
|
|
|
443
|
-
rendered = Text.assemble(("
|
|
454
|
+
rendered = Text.assemble(("View diagram in ", ThemeKey.TOOL_RESULT), " ")
|
|
444
455
|
start = len(rendered)
|
|
445
456
|
rendered.append(display_path, ThemeKey.TOOL_RESULT_MERMAID)
|
|
446
457
|
end = len(rendered)
|
|
@@ -452,39 +463,34 @@ def _render_mermaid_viewer_link(
|
|
|
452
463
|
|
|
453
464
|
|
|
454
465
|
def render_web_fetch_tool_call(arguments: str) -> RenderableType:
|
|
455
|
-
|
|
456
|
-
tool_name_column = Text.assemble((MARK_WEB_FETCH, ThemeKey.TOOL_MARK), " ", ("Fetch", ThemeKey.TOOL_NAME))
|
|
466
|
+
tool_name = "Fetch"
|
|
457
467
|
|
|
458
468
|
try:
|
|
459
469
|
payload: dict[str, str] = json.loads(arguments)
|
|
460
470
|
except json.JSONDecodeError:
|
|
461
471
|
summary = Text(
|
|
462
|
-
arguments.strip()[:
|
|
472
|
+
arguments.strip()[:INVALID_TOOL_CALL_MAX_LENGTH],
|
|
463
473
|
style=ThemeKey.INVALID_TOOL_CALL_ARGS,
|
|
464
474
|
)
|
|
465
|
-
|
|
466
|
-
return grid
|
|
475
|
+
return _render_tool_call_tree(mark=MARK_WEB_FETCH, tool_name=tool_name, details=summary)
|
|
467
476
|
|
|
468
477
|
url = payload.get("url", "")
|
|
469
478
|
summary = Text(_truncate_url(url), ThemeKey.TOOL_PARAM_FILE_PATH) if url else Text("(no url)", ThemeKey.TOOL_PARAM)
|
|
470
479
|
|
|
471
|
-
|
|
472
|
-
return grid
|
|
480
|
+
return _render_tool_call_tree(mark=MARK_WEB_FETCH, tool_name=tool_name, details=summary)
|
|
473
481
|
|
|
474
482
|
|
|
475
483
|
def render_web_search_tool_call(arguments: str) -> RenderableType:
|
|
476
|
-
|
|
477
|
-
tool_name_column = Text.assemble((MARK_WEB_SEARCH, ThemeKey.TOOL_MARK), " ", ("Web Search", ThemeKey.TOOL_NAME))
|
|
484
|
+
tool_name = "Web Search"
|
|
478
485
|
|
|
479
486
|
try:
|
|
480
487
|
payload: dict[str, Any] = json.loads(arguments)
|
|
481
488
|
except json.JSONDecodeError:
|
|
482
489
|
summary = Text(
|
|
483
|
-
arguments.strip()[:
|
|
490
|
+
arguments.strip()[:INVALID_TOOL_CALL_MAX_LENGTH],
|
|
484
491
|
style=ThemeKey.INVALID_TOOL_CALL_ARGS,
|
|
485
492
|
)
|
|
486
|
-
|
|
487
|
-
return grid
|
|
493
|
+
return _render_tool_call_tree(mark=MARK_WEB_SEARCH, tool_name=tool_name, details=summary)
|
|
488
494
|
|
|
489
495
|
query = payload.get("query", "")
|
|
490
496
|
max_results = payload.get("max_results")
|
|
@@ -492,19 +498,24 @@ def render_web_search_tool_call(arguments: str) -> RenderableType:
|
|
|
492
498
|
summary = Text("", ThemeKey.TOOL_PARAM)
|
|
493
499
|
if query:
|
|
494
500
|
# Truncate long queries
|
|
495
|
-
display_query =
|
|
501
|
+
display_query = (
|
|
502
|
+
query if len(query) <= QUERY_DISPLAY_TRUNCATE_LENGTH else query[: QUERY_DISPLAY_TRUNCATE_LENGTH - 1] + "…"
|
|
503
|
+
)
|
|
496
504
|
summary.append(display_query, ThemeKey.TOOL_PARAM)
|
|
497
505
|
else:
|
|
498
506
|
summary.append("(no query)", ThemeKey.TOOL_PARAM)
|
|
499
507
|
|
|
500
|
-
if isinstance(max_results, int) and max_results !=
|
|
508
|
+
if isinstance(max_results, int) and max_results != WEB_SEARCH_DEFAULT_MAX_RESULTS:
|
|
501
509
|
summary.append(f" (max {max_results})", ThemeKey.TOOL_TIMEOUT)
|
|
502
510
|
|
|
503
|
-
|
|
504
|
-
return grid
|
|
511
|
+
return _render_tool_call_tree(mark=MARK_WEB_SEARCH, tool_name=tool_name, details=summary)
|
|
505
512
|
|
|
506
513
|
|
|
507
|
-
def render_mermaid_tool_result(
|
|
514
|
+
def render_mermaid_tool_result(
|
|
515
|
+
tr: events.ToolResultEvent,
|
|
516
|
+
*,
|
|
517
|
+
session_id: str | None = None,
|
|
518
|
+
) -> RenderableType:
|
|
508
519
|
from klaude_code.ui.terminal import supports_osc8_hyperlinks
|
|
509
520
|
|
|
510
521
|
link_info = _extract_mermaid_link(tr.ui_extra)
|
|
@@ -513,15 +524,14 @@ def render_mermaid_tool_result(tr: events.ToolResultEvent) -> RenderableType:
|
|
|
513
524
|
|
|
514
525
|
use_osc8 = supports_osc8_hyperlinks()
|
|
515
526
|
viewer = _render_mermaid_viewer_link(tr, link_info, use_osc8=use_osc8)
|
|
516
|
-
|
|
527
|
+
|
|
528
|
+
return viewer
|
|
517
529
|
|
|
518
530
|
|
|
519
531
|
def _extract_truncation(
|
|
520
532
|
ui_extra: model.ToolResultUIExtra | None,
|
|
521
533
|
) -> model.TruncationUIExtra | None:
|
|
522
|
-
if isinstance(ui_extra, model.TruncationUIExtra)
|
|
523
|
-
return ui_extra
|
|
524
|
-
return None
|
|
534
|
+
return ui_extra if isinstance(ui_extra, model.TruncationUIExtra) else None
|
|
525
535
|
|
|
526
536
|
|
|
527
537
|
def render_truncation_info(ui_extra: model.TruncationUIExtra) -> RenderableType:
|
|
@@ -533,7 +543,9 @@ def render_truncation_info(ui_extra: model.TruncationUIExtra) -> RenderableType:
|
|
|
533
543
|
(ui_extra.saved_file_path, ThemeKey.TOOL_RESULT_TRUNCATED),
|
|
534
544
|
(f", {truncated_kb:.1f}KB truncated", ThemeKey.TOOL_RESULT_TRUNCATED),
|
|
535
545
|
)
|
|
536
|
-
|
|
546
|
+
text.no_wrap = True
|
|
547
|
+
text.overflow = "ellipsis"
|
|
548
|
+
return text
|
|
537
549
|
|
|
538
550
|
|
|
539
551
|
def get_truncation_info(tr: events.ToolResultEvent) -> model.TruncationUIExtra | None:
|
|
@@ -542,10 +554,7 @@ def get_truncation_info(tr: events.ToolResultEvent) -> model.TruncationUIExtra |
|
|
|
542
554
|
|
|
543
555
|
|
|
544
556
|
def render_report_back_tool_call() -> RenderableType:
|
|
545
|
-
|
|
546
|
-
tool_name_column = Text.assemble((MARK_DONE, ThemeKey.TOOL_MARK), " ", ("Report Back", ThemeKey.TOOL_NAME))
|
|
547
|
-
grid.add_row(tool_name_column, "")
|
|
548
|
-
return grid
|
|
557
|
+
return _render_tool_call_tree(mark=MARK_DONE, tool_name="Report Back", details=None)
|
|
549
558
|
|
|
550
559
|
|
|
551
560
|
# Tool name to active form mapping (for spinner status)
|
|
@@ -655,75 +664,76 @@ def render_markdown_doc(md_ui: model.MarkdownDocUIExtra, *, code_theme: str) ->
|
|
|
655
664
|
)
|
|
656
665
|
|
|
657
666
|
|
|
658
|
-
def render_tool_result(
|
|
667
|
+
def render_tool_result(
|
|
668
|
+
e: events.ToolResultEvent,
|
|
669
|
+
*,
|
|
670
|
+
code_theme: str = "monokai",
|
|
671
|
+
session_id: str | None = None,
|
|
672
|
+
) -> RenderableType | None:
|
|
659
673
|
"""Unified entry point for rendering tool results.
|
|
660
674
|
|
|
661
675
|
Returns a Rich Renderable or None if the tool result should not be rendered.
|
|
662
676
|
"""
|
|
663
|
-
from klaude_code.ui.renderers import errors as r_errors
|
|
664
|
-
|
|
665
677
|
if is_sub_agent_tool(e.tool_name):
|
|
666
678
|
return None
|
|
667
679
|
|
|
680
|
+
def wrap(content: RenderableType) -> TreeQuote:
|
|
681
|
+
return TreeQuote.for_tool_result(content, is_last=e.is_last_in_turn)
|
|
682
|
+
|
|
668
683
|
# Handle error case
|
|
669
684
|
if e.status == "error" and e.ui_extra is None:
|
|
670
|
-
|
|
671
|
-
return r_errors.render_tool_error(error_msg)
|
|
685
|
+
return wrap(render_generic_tool_result(e.result, is_error=True))
|
|
672
686
|
|
|
673
687
|
# Render multiple ui blocks if present
|
|
674
688
|
if isinstance(e.ui_extra, model.MultiUIExtra) and e.ui_extra.items:
|
|
675
689
|
rendered: list[RenderableType] = []
|
|
676
690
|
for item in e.ui_extra.items:
|
|
677
691
|
if isinstance(item, model.MarkdownDocUIExtra):
|
|
678
|
-
rendered.append(
|
|
692
|
+
rendered.append(render_markdown_doc(item, code_theme=code_theme))
|
|
679
693
|
elif isinstance(item, model.DiffUIExtra):
|
|
680
694
|
show_file_name = e.tool_name in (tools.APPLY_PATCH, tools.MOVE)
|
|
681
|
-
rendered.append(
|
|
682
|
-
|
|
683
|
-
)
|
|
684
|
-
return Group(*rendered) if rendered else None
|
|
695
|
+
rendered.append(r_diffs.render_structured_diff(item, show_file_name=show_file_name))
|
|
696
|
+
return wrap(Group(*rendered)) if rendered else None
|
|
685
697
|
|
|
686
698
|
# Show truncation info if output was truncated and saved to file
|
|
687
699
|
truncation_info = get_truncation_info(e)
|
|
688
700
|
if truncation_info:
|
|
689
|
-
|
|
701
|
+
result = render_generic_tool_result(e.result, is_error=e.status == "error")
|
|
702
|
+
return wrap(Group(render_truncation_info(truncation_info), result))
|
|
690
703
|
|
|
691
704
|
diff_ui = _extract_diff(e.ui_extra)
|
|
692
705
|
md_ui = _extract_markdown_doc(e.ui_extra)
|
|
693
706
|
|
|
707
|
+
def _render_fallback() -> TreeQuote:
|
|
708
|
+
if len(e.result.strip()) == 0:
|
|
709
|
+
return wrap(render_generic_tool_result("(no content)"))
|
|
710
|
+
return wrap(render_generic_tool_result(e.result, is_error=e.status == "error"))
|
|
711
|
+
|
|
694
712
|
match e.tool_name:
|
|
695
713
|
case tools.READ:
|
|
696
714
|
return None
|
|
697
715
|
case tools.EDIT:
|
|
698
|
-
return
|
|
716
|
+
return wrap(r_diffs.render_structured_diff(diff_ui) if diff_ui else Text(""))
|
|
699
717
|
case tools.WRITE:
|
|
700
718
|
if md_ui:
|
|
701
|
-
return
|
|
702
|
-
return
|
|
719
|
+
return wrap(render_markdown_doc(md_ui, code_theme=code_theme))
|
|
720
|
+
return wrap(r_diffs.render_structured_diff(diff_ui) if diff_ui else Text(""))
|
|
703
721
|
case tools.MOVE:
|
|
704
722
|
# Same-file move returns single DiffUIExtra, cross-file returns MultiUIExtra (handled above)
|
|
705
723
|
if diff_ui:
|
|
706
|
-
return
|
|
724
|
+
return wrap(r_diffs.render_structured_diff(diff_ui, show_file_name=True))
|
|
707
725
|
return None
|
|
708
726
|
case tools.APPLY_PATCH:
|
|
709
727
|
if md_ui:
|
|
710
|
-
return
|
|
728
|
+
return wrap(render_markdown_doc(md_ui, code_theme=code_theme))
|
|
711
729
|
if diff_ui:
|
|
712
|
-
return
|
|
713
|
-
|
|
714
|
-
return render_generic_tool_result("(no content)")
|
|
715
|
-
return render_generic_tool_result(e.result)
|
|
730
|
+
return wrap(r_diffs.render_structured_diff(diff_ui, show_file_name=True))
|
|
731
|
+
return _render_fallback()
|
|
716
732
|
case tools.TODO_WRITE | tools.UPDATE_PLAN:
|
|
717
|
-
return render_todo(e)
|
|
733
|
+
return wrap(render_todo(e))
|
|
718
734
|
case tools.MERMAID:
|
|
719
|
-
return render_mermaid_tool_result(e)
|
|
735
|
+
return wrap(render_mermaid_tool_result(e, session_id=session_id))
|
|
720
736
|
case tools.BASH:
|
|
721
|
-
|
|
722
|
-
return r_diffs.render_diff_panel(e.result, show_file_name=True)
|
|
723
|
-
if len(e.result.strip()) == 0:
|
|
724
|
-
return render_generic_tool_result("(no content)")
|
|
725
|
-
return render_generic_tool_result(e.result)
|
|
737
|
+
return _render_fallback()
|
|
726
738
|
case _:
|
|
727
|
-
|
|
728
|
-
return render_generic_tool_result("(no content)")
|
|
729
|
-
return render_generic_tool_result(e.result)
|
|
739
|
+
return _render_fallback()
|