klaude-code 1.2.14__py3-none-any.whl → 1.2.16__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/cli/main.py +66 -42
- klaude_code/cli/runtime.py +24 -13
- klaude_code/command/export_cmd.py +2 -2
- klaude_code/command/prompt-handoff.md +33 -0
- klaude_code/command/thinking_cmd.py +6 -2
- klaude_code/config/config.py +5 -5
- klaude_code/config/list_model.py +1 -1
- klaude_code/const/__init__.py +3 -0
- klaude_code/core/executor.py +2 -2
- klaude_code/core/manager/llm_clients_builder.py +1 -1
- klaude_code/core/manager/sub_agent_manager.py +30 -6
- klaude_code/core/prompt.py +15 -13
- klaude_code/core/prompts/{prompt-subagent-explore.md → prompt-sub-agent-explore.md} +0 -1
- klaude_code/core/prompts/{prompt-subagent-oracle.md → prompt-sub-agent-oracle.md} +1 -1
- klaude_code/core/reminders.py +75 -32
- klaude_code/core/task.py +10 -22
- klaude_code/core/tool/__init__.py +2 -0
- klaude_code/core/tool/report_back_tool.py +58 -0
- klaude_code/core/tool/sub_agent_tool.py +6 -0
- klaude_code/core/tool/tool_runner.py +9 -1
- klaude_code/core/turn.py +45 -4
- klaude_code/llm/anthropic/input.py +14 -5
- klaude_code/llm/input_common.py +1 -1
- klaude_code/llm/openrouter/input.py +14 -3
- klaude_code/llm/responses/input.py +19 -0
- klaude_code/protocol/events.py +1 -0
- klaude_code/protocol/model.py +24 -14
- klaude_code/protocol/sub_agent/__init__.py +117 -0
- klaude_code/protocol/sub_agent/explore.py +63 -0
- klaude_code/protocol/sub_agent/oracle.py +91 -0
- klaude_code/protocol/sub_agent/task.py +61 -0
- klaude_code/protocol/sub_agent/web_fetch.py +74 -0
- klaude_code/protocol/tools.py +1 -0
- klaude_code/session/export.py +12 -6
- klaude_code/session/session.py +12 -2
- klaude_code/session/templates/export_session.html +20 -24
- klaude_code/ui/modes/repl/completers.py +1 -1
- klaude_code/ui/modes/repl/event_handler.py +34 -3
- klaude_code/ui/modes/repl/renderer.py +9 -9
- klaude_code/ui/renderers/developer.py +18 -7
- klaude_code/ui/renderers/metadata.py +57 -84
- klaude_code/ui/renderers/sub_agent.py +59 -3
- klaude_code/ui/renderers/thinking.py +3 -3
- klaude_code/ui/renderers/tools.py +67 -30
- klaude_code/ui/rich/markdown.py +45 -57
- klaude_code/ui/rich/status.py +32 -14
- klaude_code/ui/rich/theme.py +18 -17
- {klaude_code-1.2.14.dist-info → klaude_code-1.2.16.dist-info}/METADATA +3 -2
- {klaude_code-1.2.14.dist-info → klaude_code-1.2.16.dist-info}/RECORD +53 -47
- klaude_code/protocol/sub_agent.py +0 -354
- /klaude_code/core/prompts/{prompt-subagent-webfetch.md → prompt-sub-agent-webfetch.md} +0 -0
- /klaude_code/core/prompts/{prompt-subagent.md → prompt-sub-agent.md} +0 -0
- {klaude_code-1.2.14.dist-info → klaude_code-1.2.16.dist-info}/WHEEL +0 -0
- {klaude_code-1.2.14.dist-info → klaude_code-1.2.16.dist-info}/entry_points.txt +0 -0
|
@@ -9,6 +9,7 @@ from rich.text import Text
|
|
|
9
9
|
|
|
10
10
|
from klaude_code.protocol import events, model
|
|
11
11
|
from klaude_code.trace import is_debug_enabled
|
|
12
|
+
from klaude_code.ui.renderers.common import create_grid
|
|
12
13
|
from klaude_code.ui.rich.theme import ThemeKey
|
|
13
14
|
from klaude_code.ui.utils.common import format_number
|
|
14
15
|
|
|
@@ -24,161 +25,133 @@ def _get_version() -> str:
|
|
|
24
25
|
def _render_task_metadata_block(
|
|
25
26
|
metadata: model.TaskMetadata,
|
|
26
27
|
*,
|
|
27
|
-
|
|
28
|
+
is_sub_agent: bool = False,
|
|
28
29
|
show_context_and_time: bool = True,
|
|
29
|
-
) ->
|
|
30
|
+
) -> RenderableType:
|
|
30
31
|
"""Render a single TaskMetadata block.
|
|
31
32
|
|
|
32
33
|
Args:
|
|
33
34
|
metadata: The TaskMetadata to render.
|
|
34
|
-
|
|
35
|
+
is_sub_agent: Whether this is a sub-agent block.
|
|
35
36
|
show_context_and_time: Whether to show context usage percent and time.
|
|
36
37
|
|
|
37
38
|
Returns:
|
|
38
|
-
|
|
39
|
+
A renderable for this metadata block.
|
|
39
40
|
"""
|
|
41
|
+
grid = create_grid()
|
|
42
|
+
|
|
40
43
|
# Get currency symbol
|
|
41
44
|
currency = metadata.usage.currency if metadata.usage else "USD"
|
|
42
45
|
currency_symbol = "¥" if currency == "CNY" else "$"
|
|
43
46
|
|
|
44
|
-
#
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
)
|
|
50
|
-
model_text = Text()
|
|
51
|
-
model_text.append_text(prefix).append_text(Text(metadata.model_name, style=ThemeKey.METADATA_BOLD))
|
|
47
|
+
# First column: mark only
|
|
48
|
+
mark = Text("└", style=ThemeKey.METADATA_DIM) if is_sub_agent else Text("•", style=ThemeKey.METADATA_BOLD)
|
|
49
|
+
|
|
50
|
+
# Second column: model@provider / tokens / cost / ...
|
|
51
|
+
content = Text()
|
|
52
|
+
content.append_text(Text(metadata.model_name, style=ThemeKey.METADATA_BOLD))
|
|
52
53
|
if metadata.provider is not None:
|
|
53
|
-
|
|
54
|
+
content.append_text(Text("@", style=ThemeKey.METADATA)).append_text(
|
|
54
55
|
Text(metadata.provider.lower().replace(" ", "-"), style=ThemeKey.METADATA)
|
|
55
56
|
)
|
|
56
57
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
# Line 2: Token consumption, Context, TPS, Cost
|
|
60
|
-
parts2: list[Text] = []
|
|
58
|
+
# All info parts (tokens, cost, context, etc.)
|
|
59
|
+
parts: list[Text] = []
|
|
61
60
|
|
|
62
61
|
if metadata.usage is not None:
|
|
63
|
-
#
|
|
64
|
-
|
|
65
|
-
(
|
|
66
|
-
|
|
62
|
+
# Tokens: ↑ 37k cache 5k ↓ 907 think 45k
|
|
63
|
+
token_parts: list[Text] = [
|
|
64
|
+
Text.assemble(
|
|
65
|
+
("↑ ", ThemeKey.METADATA_DIM), (format_number(metadata.usage.input_tokens), ThemeKey.METADATA)
|
|
66
|
+
)
|
|
67
67
|
]
|
|
68
|
-
if metadata.usage.input_cost is not None:
|
|
69
|
-
input_parts.append((f"({currency_symbol}{metadata.usage.input_cost:.4f})", ThemeKey.METADATA_DIM))
|
|
70
|
-
parts2.append(Text.assemble(*input_parts))
|
|
71
|
-
|
|
72
|
-
# Cached
|
|
73
68
|
if metadata.usage.cached_tokens > 0:
|
|
74
|
-
|
|
75
|
-
(
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
(format_number(metadata.usage.output_tokens), ThemeKey.METADATA_DIM),
|
|
86
|
-
]
|
|
87
|
-
if metadata.usage.output_cost is not None:
|
|
88
|
-
output_parts.append((f"({currency_symbol}{metadata.usage.output_cost:.4f})", ThemeKey.METADATA_DIM))
|
|
89
|
-
parts2.append(Text.assemble(*output_parts))
|
|
90
|
-
|
|
91
|
-
# Reasoning
|
|
69
|
+
token_parts.append(
|
|
70
|
+
Text.assemble(
|
|
71
|
+
Text("cache ", style=ThemeKey.METADATA_DIM),
|
|
72
|
+
Text(format_number(metadata.usage.cached_tokens), style=ThemeKey.METADATA),
|
|
73
|
+
)
|
|
74
|
+
)
|
|
75
|
+
token_parts.append(
|
|
76
|
+
Text.assemble(
|
|
77
|
+
("↓ ", ThemeKey.METADATA_DIM), (format_number(metadata.usage.output_tokens), ThemeKey.METADATA)
|
|
78
|
+
)
|
|
79
|
+
)
|
|
92
80
|
if metadata.usage.reasoning_tokens > 0:
|
|
93
|
-
|
|
81
|
+
token_parts.append(
|
|
94
82
|
Text.assemble(
|
|
95
|
-
("
|
|
96
|
-
(
|
|
97
|
-
(
|
|
98
|
-
format_number(metadata.usage.reasoning_tokens),
|
|
99
|
-
ThemeKey.METADATA_DIM,
|
|
100
|
-
),
|
|
83
|
+
("think ", ThemeKey.METADATA_DIM),
|
|
84
|
+
(format_number(metadata.usage.reasoning_tokens), ThemeKey.METADATA),
|
|
101
85
|
)
|
|
102
86
|
)
|
|
87
|
+
parts.append(Text(" · ").join(token_parts))
|
|
103
88
|
|
|
104
89
|
# Cost
|
|
105
90
|
if metadata.usage is not None and metadata.usage.total_cost is not None:
|
|
106
|
-
|
|
91
|
+
parts.append(
|
|
107
92
|
Text.assemble(
|
|
108
|
-
(
|
|
109
|
-
("
|
|
110
|
-
(f"{currency_symbol}{metadata.usage.total_cost:.4f}", ThemeKey.METADATA_DIM),
|
|
93
|
+
(currency_symbol, ThemeKey.METADATA_DIM),
|
|
94
|
+
(f"{metadata.usage.total_cost:.4f}", ThemeKey.METADATA),
|
|
111
95
|
)
|
|
112
96
|
)
|
|
113
|
-
if parts2:
|
|
114
|
-
line2 = Text(" / ", style=ThemeKey.METADATA_DIM).join(parts2)
|
|
115
|
-
renderables.append(Padding(line2, (0, 0, 0, indent + 2)))
|
|
116
|
-
|
|
117
|
-
parts3: list[Text] = []
|
|
118
97
|
if metadata.usage is not None:
|
|
119
98
|
# Context (only for main agent)
|
|
120
99
|
if show_context_and_time and metadata.usage.context_usage_percent is not None:
|
|
121
100
|
context_size = format_number(metadata.usage.context_size or 0)
|
|
122
|
-
|
|
101
|
+
parts.append(
|
|
123
102
|
Text.assemble(
|
|
124
|
-
(
|
|
125
|
-
("
|
|
126
|
-
(
|
|
127
|
-
f"{context_size}({metadata.usage.context_usage_percent:.1f}%)",
|
|
128
|
-
ThemeKey.METADATA_DIM,
|
|
129
|
-
),
|
|
103
|
+
(context_size, ThemeKey.METADATA),
|
|
104
|
+
(f" ({metadata.usage.context_usage_percent:.1f}%)", ThemeKey.METADATA_DIM),
|
|
130
105
|
)
|
|
131
106
|
)
|
|
132
107
|
|
|
133
108
|
# TPS
|
|
134
109
|
if metadata.usage.throughput_tps is not None:
|
|
135
|
-
|
|
110
|
+
parts.append(
|
|
136
111
|
Text.assemble(
|
|
112
|
+
(f"{metadata.usage.throughput_tps:.1f} ", ThemeKey.METADATA),
|
|
137
113
|
("tps", ThemeKey.METADATA_DIM),
|
|
138
|
-
(":", ThemeKey.METADATA_DIM),
|
|
139
|
-
(f"{metadata.usage.throughput_tps:.1f}", ThemeKey.METADATA_DIM),
|
|
140
114
|
)
|
|
141
115
|
)
|
|
142
116
|
|
|
143
117
|
# Duration
|
|
144
118
|
if show_context_and_time and metadata.task_duration_s is not None:
|
|
145
|
-
|
|
119
|
+
parts.append(
|
|
146
120
|
Text.assemble(
|
|
147
|
-
("
|
|
148
|
-
("
|
|
149
|
-
(f"{metadata.task_duration_s:.1f}s", ThemeKey.METADATA_DIM),
|
|
121
|
+
(f"{metadata.task_duration_s:.1f}", ThemeKey.METADATA),
|
|
122
|
+
("s", ThemeKey.METADATA_DIM),
|
|
150
123
|
)
|
|
151
124
|
)
|
|
152
125
|
|
|
153
126
|
# Turn count
|
|
154
127
|
if show_context_and_time and metadata.turn_count > 0:
|
|
155
|
-
|
|
128
|
+
parts.append(
|
|
156
129
|
Text.assemble(
|
|
157
|
-
(
|
|
158
|
-
("
|
|
159
|
-
(str(metadata.turn_count), ThemeKey.METADATA_DIM),
|
|
130
|
+
(str(metadata.turn_count), ThemeKey.METADATA),
|
|
131
|
+
(" turns", ThemeKey.METADATA_DIM),
|
|
160
132
|
)
|
|
161
133
|
)
|
|
162
134
|
|
|
163
|
-
if
|
|
164
|
-
|
|
165
|
-
|
|
135
|
+
if parts:
|
|
136
|
+
content.append_text(Text(" · ", style=ThemeKey.METADATA_DIM))
|
|
137
|
+
content.append_text(Text(" · ", style=ThemeKey.METADATA_DIM).join(parts))
|
|
166
138
|
|
|
167
|
-
|
|
139
|
+
grid.add_row(mark, content)
|
|
140
|
+
return grid if not is_sub_agent else Padding(grid, (0, 0, 0, 2))
|
|
168
141
|
|
|
169
142
|
|
|
170
143
|
def render_task_metadata(e: events.TaskMetadataEvent) -> RenderableType:
|
|
171
144
|
"""Render task metadata including main agent and sub-agents, aggregated by model+provider."""
|
|
172
145
|
renderables: list[RenderableType] = []
|
|
173
146
|
|
|
174
|
-
renderables.
|
|
147
|
+
renderables.append(_render_task_metadata_block(e.metadata.main, is_sub_agent=False, show_context_and_time=True))
|
|
175
148
|
|
|
176
149
|
# Aggregate by (model_name, provider), sorted by total_cost descending
|
|
177
150
|
sorted_items = model.TaskMetadata.aggregate_by_model(e.metadata.sub_agent_task_metadata)
|
|
178
151
|
|
|
179
152
|
# Render each aggregated model block
|
|
180
153
|
for meta in sorted_items:
|
|
181
|
-
renderables.
|
|
154
|
+
renderables.append(_render_task_metadata_block(meta, is_sub_agent=True, show_context_and_time=False))
|
|
182
155
|
|
|
183
156
|
return Group(*renderables)
|
|
184
157
|
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import json
|
|
2
|
+
from typing import Any, cast
|
|
2
3
|
|
|
3
4
|
from rich.console import Group, RenderableType
|
|
5
|
+
from rich.json import JSON
|
|
4
6
|
from rich.panel import Panel
|
|
5
7
|
from rich.style import Style
|
|
6
8
|
from rich.text import Text
|
|
@@ -12,20 +14,67 @@ from klaude_code.ui.rich.markdown import NoInsetMarkdown
|
|
|
12
14
|
from klaude_code.ui.rich.theme import ThemeKey
|
|
13
15
|
|
|
14
16
|
|
|
17
|
+
def _compact_schema_value(value: dict[str, Any]) -> str | list[Any] | dict[str, Any]:
|
|
18
|
+
"""Convert a JSON Schema value to compact representation."""
|
|
19
|
+
value_type = value.get("type", "any")
|
|
20
|
+
desc = value.get("description", "")
|
|
21
|
+
|
|
22
|
+
if value_type == "object":
|
|
23
|
+
props = value.get("properties", {})
|
|
24
|
+
return {k: _compact_schema_value(v) for k, v in props.items()}
|
|
25
|
+
elif value_type == "array":
|
|
26
|
+
items = value.get("items", {})
|
|
27
|
+
# If items have no description, use the array's description
|
|
28
|
+
if desc and not items.get("description"):
|
|
29
|
+
items = {**items, "description": desc}
|
|
30
|
+
return [_compact_schema_value(items)]
|
|
31
|
+
else:
|
|
32
|
+
if desc:
|
|
33
|
+
return f"{value_type} // {desc}"
|
|
34
|
+
return value_type
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _compact_schema(schema: dict[str, Any]) -> dict[str, Any] | list[Any] | str:
|
|
38
|
+
"""Convert JSON Schema to compact representation for display."""
|
|
39
|
+
return _compact_schema_value(schema)
|
|
40
|
+
|
|
41
|
+
|
|
15
42
|
def render_sub_agent_call(e: model.SubAgentState, style: Style | None = None) -> RenderableType:
|
|
16
43
|
"""Render sub-agent tool call header and prompt body."""
|
|
17
44
|
desc = Text(
|
|
18
45
|
f" {e.sub_agent_desc} ",
|
|
19
46
|
style=Style(color=style.color if style else None, bold=True, reverse=True),
|
|
20
47
|
)
|
|
21
|
-
|
|
48
|
+
elements: list[RenderableType] = [
|
|
22
49
|
Text.assemble((e.sub_agent_type, ThemeKey.TOOL_NAME), " ", desc),
|
|
23
50
|
Text(e.sub_agent_prompt, style=style or ""),
|
|
24
|
-
|
|
51
|
+
]
|
|
52
|
+
if e.output_schema:
|
|
53
|
+
elements.append(Text("\nExpected Output Format JSON:", style=style or ""))
|
|
54
|
+
compact = _compact_schema(e.output_schema)
|
|
55
|
+
schema_text = json.dumps(compact, ensure_ascii=False, indent=2)
|
|
56
|
+
elements.append(JSON(schema_text))
|
|
57
|
+
return Group(*elements)
|
|
25
58
|
|
|
26
59
|
|
|
27
|
-
def render_sub_agent_result(
|
|
60
|
+
def render_sub_agent_result(
|
|
61
|
+
result: str, *, code_theme: str, style: Style | None = None, has_structured_output: bool = False
|
|
62
|
+
) -> RenderableType:
|
|
28
63
|
stripped_result = result.strip()
|
|
64
|
+
|
|
65
|
+
# Use rich JSON for structured output
|
|
66
|
+
if has_structured_output:
|
|
67
|
+
return Panel.fit(
|
|
68
|
+
Group(
|
|
69
|
+
Text(
|
|
70
|
+
"use /export to view full output",
|
|
71
|
+
style=ThemeKey.TOOL_RESULT,
|
|
72
|
+
),
|
|
73
|
+
JSON(stripped_result),
|
|
74
|
+
),
|
|
75
|
+
border_style=ThemeKey.LINES,
|
|
76
|
+
)
|
|
77
|
+
|
|
29
78
|
lines = stripped_result.splitlines()
|
|
30
79
|
if len(lines) > const.SUB_AGENT_RESULT_MAX_LINES:
|
|
31
80
|
hidden_count = len(lines) - const.SUB_AGENT_RESULT_MAX_LINES
|
|
@@ -53,6 +102,7 @@ def build_sub_agent_state_from_tool_call(e: events.ToolCallEvent) -> model.SubAg
|
|
|
53
102
|
return None
|
|
54
103
|
description = profile.name
|
|
55
104
|
prompt = ""
|
|
105
|
+
output_schema: dict[str, Any] | None = None
|
|
56
106
|
if e.arguments:
|
|
57
107
|
try:
|
|
58
108
|
payload: dict[str, object] = json.loads(e.arguments)
|
|
@@ -64,8 +114,14 @@ def build_sub_agent_state_from_tool_call(e: events.ToolCallEvent) -> model.SubAg
|
|
|
64
114
|
prompt_value = payload.get("prompt") or payload.get("task")
|
|
65
115
|
if isinstance(prompt_value, str):
|
|
66
116
|
prompt = prompt_value.strip()
|
|
117
|
+
# Extract output_schema if profile supports it
|
|
118
|
+
if profile.output_schema_arg:
|
|
119
|
+
schema_value = payload.get(profile.output_schema_arg)
|
|
120
|
+
if isinstance(schema_value, dict):
|
|
121
|
+
output_schema = cast(dict[str, Any], schema_value)
|
|
67
122
|
return model.SubAgentState(
|
|
68
123
|
sub_agent_type=profile.name,
|
|
69
124
|
sub_agent_desc=description,
|
|
70
125
|
sub_agent_prompt=prompt,
|
|
126
|
+
output_schema=output_schema,
|
|
71
127
|
)
|
|
@@ -2,7 +2,7 @@ from rich.console import RenderableType
|
|
|
2
2
|
from rich.padding import Padding
|
|
3
3
|
from rich.text import Text
|
|
4
4
|
|
|
5
|
-
from klaude_code.ui.rich.markdown import
|
|
5
|
+
from klaude_code.ui.rich.markdown import ThinkingMarkdown
|
|
6
6
|
from klaude_code.ui.rich.theme import ThemeKey
|
|
7
7
|
|
|
8
8
|
|
|
@@ -16,7 +16,7 @@ def _normalize_thinking_content(content: str) -> str:
|
|
|
16
16
|
content.rstrip()
|
|
17
17
|
.replace("**\n\n", "** \n")
|
|
18
18
|
.replace("\\n\\n\n\n", "") # Weird case of Gemini 3
|
|
19
|
-
.replace("****", "**\n\n**") #
|
|
19
|
+
.replace("****", "**\n\n**") # Remove extra newlines after bold titles
|
|
20
20
|
)
|
|
21
21
|
|
|
22
22
|
|
|
@@ -30,7 +30,7 @@ def render_thinking(content: str, *, code_theme: str, style: str) -> RenderableT
|
|
|
30
30
|
return None
|
|
31
31
|
|
|
32
32
|
return Padding.indent(
|
|
33
|
-
|
|
33
|
+
ThinkingMarkdown(
|
|
34
34
|
_normalize_thinking_content(content),
|
|
35
35
|
code_theme=code_theme,
|
|
36
36
|
style=style,
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import json
|
|
2
2
|
from pathlib import Path
|
|
3
|
+
from typing import Any, cast
|
|
3
4
|
|
|
4
5
|
from rich.console import RenderableType
|
|
5
6
|
from rich.padding import Padding
|
|
6
7
|
from rich.text import Text
|
|
7
8
|
|
|
8
9
|
from klaude_code import const
|
|
9
|
-
from klaude_code.protocol import events, model
|
|
10
|
+
from klaude_code.protocol import events, model, tools
|
|
10
11
|
from klaude_code.protocol.sub_agent import is_sub_agent_tool as _is_sub_agent_tool
|
|
11
12
|
from klaude_code.ui.renderers import diffs as r_diffs
|
|
12
13
|
from klaude_code.ui.renderers.common import create_grid
|
|
@@ -58,6 +59,49 @@ def render_generic_tool_call(tool_name: str, arguments: str, markup: str = "•"
|
|
|
58
59
|
return grid
|
|
59
60
|
|
|
60
61
|
|
|
62
|
+
def render_bash_tool_call(arguments: str) -> RenderableType:
|
|
63
|
+
grid = create_grid()
|
|
64
|
+
tool_name_column = Text.assemble((">", ThemeKey.TOOL_MARK), " ", ("Bash", ThemeKey.TOOL_NAME))
|
|
65
|
+
|
|
66
|
+
try:
|
|
67
|
+
payload_raw: Any = json.loads(arguments) if arguments else {}
|
|
68
|
+
except json.JSONDecodeError:
|
|
69
|
+
summary = Text(
|
|
70
|
+
arguments.strip()[: const.INVALID_TOOL_CALL_MAX_LENGTH],
|
|
71
|
+
style=ThemeKey.INVALID_TOOL_CALL_ARGS,
|
|
72
|
+
)
|
|
73
|
+
grid.add_row(tool_name_column, summary)
|
|
74
|
+
return grid
|
|
75
|
+
|
|
76
|
+
if not isinstance(payload_raw, dict):
|
|
77
|
+
summary = Text(
|
|
78
|
+
str(payload_raw)[: const.INVALID_TOOL_CALL_MAX_LENGTH],
|
|
79
|
+
style=ThemeKey.INVALID_TOOL_CALL_ARGS,
|
|
80
|
+
)
|
|
81
|
+
grid.add_row(tool_name_column, summary)
|
|
82
|
+
return grid
|
|
83
|
+
|
|
84
|
+
payload: dict[str, object] = cast(dict[str, object], payload_raw)
|
|
85
|
+
|
|
86
|
+
summary = Text("", ThemeKey.TOOL_PARAM)
|
|
87
|
+
command = payload.get("command")
|
|
88
|
+
timeout_ms = payload.get("timeout_ms")
|
|
89
|
+
|
|
90
|
+
if isinstance(command, str) and command.strip():
|
|
91
|
+
summary.append(command.strip(), style=ThemeKey.TOOL_PARAM)
|
|
92
|
+
|
|
93
|
+
if isinstance(timeout_ms, int):
|
|
94
|
+
if summary:
|
|
95
|
+
summary.append(" ")
|
|
96
|
+
if timeout_ms >= 1000 and timeout_ms % 1000 == 0:
|
|
97
|
+
summary.append(f"{timeout_ms // 1000}s", style=ThemeKey.TOOL_TIMEOUT)
|
|
98
|
+
else:
|
|
99
|
+
summary.append(f"{timeout_ms}ms", style=ThemeKey.TOOL_TIMEOUT)
|
|
100
|
+
|
|
101
|
+
grid.add_row(tool_name_column, summary)
|
|
102
|
+
return grid
|
|
103
|
+
|
|
104
|
+
|
|
61
105
|
def render_update_plan_tool_call(arguments: str) -> RenderableType:
|
|
62
106
|
grid = create_grid()
|
|
63
107
|
tool_name_column = Text.assemble(("◎", ThemeKey.TOOL_MARK), " ", ("Update Plan", ThemeKey.TOOL_NAME))
|
|
@@ -391,35 +435,28 @@ def get_truncation_info(tr: events.ToolResultEvent) -> model.TruncationUIExtra |
|
|
|
391
435
|
return _extract_truncation(tr.ui_extra)
|
|
392
436
|
|
|
393
437
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
"
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
"Bash": ">",
|
|
401
|
-
"apply_patch": "→",
|
|
402
|
-
"TodoWrite": "◎",
|
|
403
|
-
"update_plan": "◎",
|
|
404
|
-
"Mermaid": "⧉",
|
|
405
|
-
"Memory": "★",
|
|
406
|
-
"Skill": "◈",
|
|
407
|
-
}
|
|
438
|
+
def render_report_back_tool_call() -> RenderableType:
|
|
439
|
+
grid = create_grid()
|
|
440
|
+
tool_name_column = Text.assemble(("✔", ThemeKey.TOOL_MARK), " ", ("Report Back", ThemeKey.TOOL_NAME))
|
|
441
|
+
grid.add_row(tool_name_column, "")
|
|
442
|
+
return grid
|
|
443
|
+
|
|
408
444
|
|
|
409
445
|
# Tool name to active form mapping (for spinner status)
|
|
410
446
|
_TOOL_ACTIVE_FORM: dict[str, str] = {
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
447
|
+
tools.BASH: "Bashing",
|
|
448
|
+
tools.APPLY_PATCH: "Patching",
|
|
449
|
+
tools.EDIT: "Editing",
|
|
450
|
+
tools.MULTI_EDIT: "Editing",
|
|
451
|
+
tools.READ: "Reading",
|
|
452
|
+
tools.WRITE: "Writing",
|
|
453
|
+
tools.TODO_WRITE: "Planning",
|
|
454
|
+
tools.UPDATE_PLAN: "Planning",
|
|
455
|
+
tools.SKILL: "Skilling",
|
|
456
|
+
tools.MERMAID: "Diagramming",
|
|
457
|
+
tools.MEMORY: "Memorizing",
|
|
458
|
+
tools.WEB_FETCH: "Fetching",
|
|
459
|
+
tools.REPORT_BACK: "Submitting Report",
|
|
423
460
|
}
|
|
424
461
|
|
|
425
462
|
|
|
@@ -446,7 +483,6 @@ def render_tool_call(e: events.ToolCallEvent) -> RenderableType | None:
|
|
|
446
483
|
|
|
447
484
|
Returns a Rich Renderable or None if the tool call should not be rendered.
|
|
448
485
|
"""
|
|
449
|
-
from klaude_code.protocol import tools
|
|
450
486
|
|
|
451
487
|
if is_sub_agent_tool(e.tool_name):
|
|
452
488
|
return None
|
|
@@ -461,7 +497,7 @@ def render_tool_call(e: events.ToolCallEvent) -> RenderableType | None:
|
|
|
461
497
|
case tools.MULTI_EDIT:
|
|
462
498
|
return render_multi_edit_tool_call(e.arguments)
|
|
463
499
|
case tools.BASH:
|
|
464
|
-
return
|
|
500
|
+
return render_bash_tool_call(e.arguments)
|
|
465
501
|
case tools.APPLY_PATCH:
|
|
466
502
|
return render_apply_patch_tool_call(e.arguments)
|
|
467
503
|
case tools.TODO_WRITE:
|
|
@@ -474,6 +510,8 @@ def render_tool_call(e: events.ToolCallEvent) -> RenderableType | None:
|
|
|
474
510
|
return render_memory_tool_call(e.arguments)
|
|
475
511
|
case tools.SKILL:
|
|
476
512
|
return render_generic_tool_call(e.tool_name, e.arguments, "◈")
|
|
513
|
+
case tools.REPORT_BACK:
|
|
514
|
+
return render_report_back_tool_call()
|
|
477
515
|
case _:
|
|
478
516
|
return render_generic_tool_call(e.tool_name, e.arguments)
|
|
479
517
|
|
|
@@ -489,7 +527,6 @@ def render_tool_result(e: events.ToolResultEvent) -> RenderableType | None:
|
|
|
489
527
|
|
|
490
528
|
Returns a Rich Renderable or None if the tool result should not be rendered.
|
|
491
529
|
"""
|
|
492
|
-
from klaude_code.protocol import tools
|
|
493
530
|
from klaude_code.ui.renderers import errors as r_errors
|
|
494
531
|
|
|
495
532
|
if is_sub_agent_tool(e.tool_name):
|