klaude-code 2.4.1__py3-none-any.whl → 2.5.0__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/app/runtime.py +2 -6
- klaude_code/cli/main.py +0 -1
- klaude_code/config/assets/builtin_config.yaml +7 -0
- klaude_code/const.py +7 -4
- klaude_code/core/agent.py +10 -1
- klaude_code/core/agent_profile.py +47 -35
- klaude_code/core/executor.py +6 -21
- klaude_code/core/manager/sub_agent_manager.py +17 -1
- klaude_code/core/prompts/prompt-sub-agent-web.md +4 -4
- klaude_code/core/task.py +65 -4
- klaude_code/core/tool/__init__.py +0 -5
- klaude_code/core/tool/context.py +12 -1
- klaude_code/core/tool/offload.py +311 -0
- klaude_code/core/tool/shell/bash_tool.md +1 -43
- klaude_code/core/tool/sub_agent_tool.py +1 -0
- klaude_code/core/tool/todo/todo_write_tool.md +0 -23
- klaude_code/core/tool/tool_runner.py +14 -9
- klaude_code/core/tool/web/web_fetch_tool.md +1 -1
- klaude_code/core/tool/web/web_fetch_tool.py +14 -39
- klaude_code/core/turn.py +128 -138
- klaude_code/llm/anthropic/client.py +176 -82
- klaude_code/llm/bedrock/client.py +8 -12
- klaude_code/llm/claude/client.py +11 -15
- klaude_code/llm/client.py +31 -4
- klaude_code/llm/codex/client.py +7 -11
- klaude_code/llm/google/client.py +150 -69
- klaude_code/llm/openai_compatible/client.py +10 -15
- klaude_code/llm/openai_compatible/stream.py +68 -6
- klaude_code/llm/openrouter/client.py +9 -15
- klaude_code/llm/partial_message.py +35 -0
- klaude_code/llm/responses/client.py +134 -68
- klaude_code/llm/usage.py +30 -0
- klaude_code/protocol/commands.py +0 -4
- klaude_code/protocol/events/metadata.py +1 -0
- klaude_code/protocol/events/streaming.py +1 -0
- klaude_code/protocol/events/system.py +0 -4
- klaude_code/protocol/model.py +2 -15
- klaude_code/protocol/sub_agent/explore.py +0 -10
- klaude_code/protocol/sub_agent/image_gen.py +0 -7
- klaude_code/protocol/sub_agent/task.py +0 -10
- klaude_code/protocol/sub_agent/web.py +4 -12
- klaude_code/session/templates/export_session.html +4 -4
- klaude_code/skill/manager.py +2 -1
- klaude_code/tui/components/metadata.py +41 -49
- klaude_code/tui/components/rich/markdown.py +1 -3
- klaude_code/tui/components/rich/theme.py +2 -2
- klaude_code/tui/components/sub_agent.py +9 -1
- klaude_code/tui/components/tools.py +0 -31
- klaude_code/tui/components/welcome.py +1 -32
- klaude_code/tui/input/prompt_toolkit.py +25 -9
- klaude_code/tui/machine.py +40 -8
- klaude_code/tui/renderer.py +1 -0
- {klaude_code-2.4.1.dist-info → klaude_code-2.5.0.dist-info}/METADATA +2 -2
- {klaude_code-2.4.1.dist-info → klaude_code-2.5.0.dist-info}/RECORD +56 -56
- klaude_code/core/prompts/prompt-nano-banana.md +0 -1
- klaude_code/core/tool/truncation.py +0 -203
- {klaude_code-2.4.1.dist-info → klaude_code-2.5.0.dist-info}/WHEEL +0 -0
- {klaude_code-2.4.1.dist-info → klaude_code-2.5.0.dist-info}/entry_points.txt +0 -0
|
@@ -9,11 +9,12 @@ from openai.types import responses
|
|
|
9
9
|
from openai.types.responses.response_create_params import ResponseCreateParamsStreaming
|
|
10
10
|
|
|
11
11
|
from klaude_code.const import LLM_HTTP_TIMEOUT_CONNECT, LLM_HTTP_TIMEOUT_READ, LLM_HTTP_TIMEOUT_TOTAL
|
|
12
|
-
from klaude_code.llm.client import LLMClientABC
|
|
12
|
+
from klaude_code.llm.client import LLMClientABC, LLMStreamABC
|
|
13
13
|
from klaude_code.llm.input_common import apply_config_defaults
|
|
14
|
+
from klaude_code.llm.partial_message import degrade_thinking_to_text
|
|
14
15
|
from klaude_code.llm.registry import register
|
|
15
16
|
from klaude_code.llm.responses.input import convert_history_to_input, convert_tool_schema
|
|
16
|
-
from klaude_code.llm.usage import MetadataTracker,
|
|
17
|
+
from klaude_code.llm.usage import MetadataTracker, error_llm_stream
|
|
17
18
|
from klaude_code.log import DebugType, log_debug
|
|
18
19
|
from klaude_code.protocol import llm_param, message, model
|
|
19
20
|
|
|
@@ -56,46 +57,79 @@ def build_payload(param: llm_param.LLMCallParameter) -> ResponseCreateParamsStre
|
|
|
56
57
|
return payload
|
|
57
58
|
|
|
58
59
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
if accumulated_thinking:
|
|
77
|
-
assistant_parts.append(
|
|
60
|
+
class ResponsesStreamStateManager:
|
|
61
|
+
"""Manages streaming state for Responses API and provides partial message access."""
|
|
62
|
+
|
|
63
|
+
def __init__(self, model_id: str) -> None:
|
|
64
|
+
self.model_id = model_id
|
|
65
|
+
self.response_id: str | None = None
|
|
66
|
+
self.stage: Literal["waiting", "thinking", "assistant", "tool"] = "waiting"
|
|
67
|
+
self.accumulated_thinking: list[str] = []
|
|
68
|
+
self.accumulated_text: list[str] = []
|
|
69
|
+
self.pending_signature: str | None = None
|
|
70
|
+
self.assistant_parts: list[message.Part] = []
|
|
71
|
+
self.stop_reason: model.StopReason | None = None
|
|
72
|
+
|
|
73
|
+
def flush_thinking(self) -> None:
|
|
74
|
+
"""Flush accumulated thinking content into parts."""
|
|
75
|
+
if self.accumulated_thinking:
|
|
76
|
+
self.assistant_parts.append(
|
|
78
77
|
message.ThinkingTextPart(
|
|
79
|
-
text="".join(accumulated_thinking),
|
|
80
|
-
model_id=
|
|
78
|
+
text="".join(self.accumulated_thinking),
|
|
79
|
+
model_id=self.model_id,
|
|
81
80
|
)
|
|
82
81
|
)
|
|
83
|
-
accumulated_thinking.clear()
|
|
84
|
-
if pending_signature:
|
|
85
|
-
assistant_parts.append(
|
|
82
|
+
self.accumulated_thinking.clear()
|
|
83
|
+
if self.pending_signature:
|
|
84
|
+
self.assistant_parts.append(
|
|
86
85
|
message.ThinkingSignaturePart(
|
|
87
|
-
signature=pending_signature,
|
|
88
|
-
model_id=
|
|
86
|
+
signature=self.pending_signature,
|
|
87
|
+
model_id=self.model_id,
|
|
89
88
|
format="openai_reasoning",
|
|
90
89
|
)
|
|
91
90
|
)
|
|
92
|
-
pending_signature = None
|
|
91
|
+
self.pending_signature = None
|
|
93
92
|
|
|
94
|
-
def flush_text() -> None:
|
|
95
|
-
|
|
93
|
+
def flush_text(self) -> None:
|
|
94
|
+
"""Flush accumulated text content into parts."""
|
|
95
|
+
if not self.accumulated_text:
|
|
96
96
|
return
|
|
97
|
-
assistant_parts.append(message.TextPart(text="".join(accumulated_text)))
|
|
98
|
-
accumulated_text.clear()
|
|
97
|
+
self.assistant_parts.append(message.TextPart(text="".join(self.accumulated_text)))
|
|
98
|
+
self.accumulated_text.clear()
|
|
99
|
+
|
|
100
|
+
def flush_all(self) -> list[message.Part]:
|
|
101
|
+
"""Flush all accumulated content and return parts."""
|
|
102
|
+
self.flush_thinking()
|
|
103
|
+
self.flush_text()
|
|
104
|
+
return list(self.assistant_parts)
|
|
105
|
+
|
|
106
|
+
def get_partial_message(self) -> message.AssistantMessage | None:
|
|
107
|
+
"""Build a partial AssistantMessage from accumulated state."""
|
|
108
|
+
parts = self.flush_all()
|
|
109
|
+
filtered_parts: list[message.Part] = []
|
|
110
|
+
for part in parts:
|
|
111
|
+
if isinstance(part, message.ToolCallPart):
|
|
112
|
+
continue
|
|
113
|
+
filtered_parts.append(part)
|
|
114
|
+
|
|
115
|
+
filtered_parts = degrade_thinking_to_text(filtered_parts)
|
|
116
|
+
if not filtered_parts:
|
|
117
|
+
return None
|
|
118
|
+
return message.AssistantMessage(
|
|
119
|
+
parts=filtered_parts,
|
|
120
|
+
response_id=self.response_id,
|
|
121
|
+
stop_reason="aborted",
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
async def parse_responses_stream(
|
|
126
|
+
stream: "AsyncStream[ResponseStreamEvent]",
|
|
127
|
+
*,
|
|
128
|
+
state: ResponsesStreamStateManager,
|
|
129
|
+
param: llm_param.LLMCallParameter,
|
|
130
|
+
metadata_tracker: MetadataTracker,
|
|
131
|
+
) -> AsyncGenerator[message.LLMStreamItem]:
|
|
132
|
+
"""Parse OpenAI Responses API stream events into stream items."""
|
|
99
133
|
|
|
100
134
|
def map_stop_reason(status: str | None, reason: str | None) -> model.StopReason | None:
|
|
101
135
|
if reason:
|
|
@@ -122,31 +156,31 @@ async def parse_responses_stream(
|
|
|
122
156
|
)
|
|
123
157
|
match event:
|
|
124
158
|
case responses.ResponseCreatedEvent() as event:
|
|
125
|
-
response_id = event.response.id
|
|
159
|
+
state.response_id = event.response.id
|
|
126
160
|
case responses.ResponseReasoningSummaryTextDeltaEvent() as event:
|
|
127
161
|
if event.delta:
|
|
128
162
|
metadata_tracker.record_token()
|
|
129
|
-
if stage == "assistant":
|
|
130
|
-
flush_text()
|
|
131
|
-
stage = "thinking"
|
|
132
|
-
accumulated_thinking.append(event.delta)
|
|
133
|
-
yield message.ThinkingTextDelta(content=event.delta, response_id=response_id)
|
|
163
|
+
if state.stage == "assistant":
|
|
164
|
+
state.flush_text()
|
|
165
|
+
state.stage = "thinking"
|
|
166
|
+
state.accumulated_thinking.append(event.delta)
|
|
167
|
+
yield message.ThinkingTextDelta(content=event.delta, response_id=state.response_id)
|
|
134
168
|
case responses.ResponseReasoningSummaryTextDoneEvent() as event:
|
|
135
|
-
if event.text and not accumulated_thinking:
|
|
136
|
-
accumulated_thinking.append(event.text)
|
|
169
|
+
if event.text and not state.accumulated_thinking:
|
|
170
|
+
state.accumulated_thinking.append(event.text)
|
|
137
171
|
case responses.ResponseTextDeltaEvent() as event:
|
|
138
172
|
if event.delta:
|
|
139
173
|
metadata_tracker.record_token()
|
|
140
|
-
if stage == "thinking":
|
|
141
|
-
flush_thinking()
|
|
142
|
-
stage = "assistant"
|
|
143
|
-
accumulated_text.append(event.delta)
|
|
144
|
-
yield message.AssistantTextDelta(content=event.delta, response_id=response_id)
|
|
174
|
+
if state.stage == "thinking":
|
|
175
|
+
state.flush_thinking()
|
|
176
|
+
state.stage = "assistant"
|
|
177
|
+
state.accumulated_text.append(event.delta)
|
|
178
|
+
yield message.AssistantTextDelta(content=event.delta, response_id=state.response_id)
|
|
145
179
|
case responses.ResponseOutputItemAddedEvent() as event:
|
|
146
180
|
if isinstance(event.item, responses.ResponseFunctionToolCall):
|
|
147
181
|
metadata_tracker.record_token()
|
|
148
182
|
yield message.ToolCallStartDelta(
|
|
149
|
-
response_id=response_id,
|
|
183
|
+
response_id=state.response_id,
|
|
150
184
|
call_id=event.item.call_id,
|
|
151
185
|
name=event.item.name,
|
|
152
186
|
)
|
|
@@ -154,9 +188,9 @@ async def parse_responses_stream(
|
|
|
154
188
|
match event.item:
|
|
155
189
|
case responses.ResponseReasoningItem() as item:
|
|
156
190
|
if item.encrypted_content:
|
|
157
|
-
pending_signature = item.encrypted_content
|
|
191
|
+
state.pending_signature = item.encrypted_content
|
|
158
192
|
case responses.ResponseOutputMessage() as item:
|
|
159
|
-
if not accumulated_text:
|
|
193
|
+
if not state.accumulated_text:
|
|
160
194
|
text_content = "\n".join(
|
|
161
195
|
[
|
|
162
196
|
part.text
|
|
@@ -165,13 +199,13 @@ async def parse_responses_stream(
|
|
|
165
199
|
]
|
|
166
200
|
)
|
|
167
201
|
if text_content:
|
|
168
|
-
accumulated_text.append(text_content)
|
|
202
|
+
state.accumulated_text.append(text_content)
|
|
169
203
|
case responses.ResponseFunctionToolCall() as item:
|
|
170
204
|
metadata_tracker.record_token()
|
|
171
|
-
flush_thinking()
|
|
172
|
-
flush_text()
|
|
173
|
-
stage = "tool"
|
|
174
|
-
assistant_parts.append(
|
|
205
|
+
state.flush_thinking()
|
|
206
|
+
state.flush_text()
|
|
207
|
+
state.stage = "tool"
|
|
208
|
+
state.assistant_parts.append(
|
|
175
209
|
message.ToolCallPart(
|
|
176
210
|
call_id=item.call_id,
|
|
177
211
|
id=item.id,
|
|
@@ -198,8 +232,8 @@ async def parse_responses_stream(
|
|
|
198
232
|
)
|
|
199
233
|
)
|
|
200
234
|
metadata_tracker.set_model_name(str(param.model_id))
|
|
201
|
-
metadata_tracker.set_response_id(response_id)
|
|
202
|
-
stop_reason = map_stop_reason(event.response.status, error_reason)
|
|
235
|
+
metadata_tracker.set_response_id(state.response_id)
|
|
236
|
+
state.stop_reason = map_stop_reason(event.response.status, error_reason)
|
|
203
237
|
if event.response.status != "completed":
|
|
204
238
|
error_message = f"LLM response finished with status '{event.response.status}'"
|
|
205
239
|
if error_reason:
|
|
@@ -221,18 +255,53 @@ async def parse_responses_stream(
|
|
|
221
255
|
except (openai.OpenAIError, httpx.HTTPError) as e:
|
|
222
256
|
yield message.StreamErrorItem(error=f"{e.__class__.__name__} {e!s}")
|
|
223
257
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
metadata_tracker.set_response_id(response_id)
|
|
258
|
+
parts = state.flush_all()
|
|
259
|
+
metadata_tracker.set_response_id(state.response_id)
|
|
227
260
|
metadata = metadata_tracker.finalize()
|
|
228
261
|
yield message.AssistantMessage(
|
|
229
|
-
parts=
|
|
230
|
-
response_id=response_id,
|
|
262
|
+
parts=parts,
|
|
263
|
+
response_id=state.response_id,
|
|
231
264
|
usage=metadata,
|
|
232
|
-
stop_reason=stop_reason,
|
|
265
|
+
stop_reason=state.stop_reason,
|
|
233
266
|
)
|
|
234
267
|
|
|
235
268
|
|
|
269
|
+
class ResponsesLLMStream(LLMStreamABC):
|
|
270
|
+
"""LLMStream implementation for Responses API clients."""
|
|
271
|
+
|
|
272
|
+
def __init__(
|
|
273
|
+
self,
|
|
274
|
+
stream: "AsyncStream[ResponseStreamEvent]",
|
|
275
|
+
*,
|
|
276
|
+
param: llm_param.LLMCallParameter,
|
|
277
|
+
metadata_tracker: MetadataTracker,
|
|
278
|
+
) -> None:
|
|
279
|
+
self._stream = stream
|
|
280
|
+
self._param = param
|
|
281
|
+
self._metadata_tracker = metadata_tracker
|
|
282
|
+
self._state = ResponsesStreamStateManager(str(param.model_id))
|
|
283
|
+
self._completed = False
|
|
284
|
+
|
|
285
|
+
def __aiter__(self) -> AsyncGenerator[message.LLMStreamItem]:
|
|
286
|
+
return self._iterate()
|
|
287
|
+
|
|
288
|
+
async def _iterate(self) -> AsyncGenerator[message.LLMStreamItem]:
|
|
289
|
+
async for item in parse_responses_stream(
|
|
290
|
+
self._stream,
|
|
291
|
+
state=self._state,
|
|
292
|
+
param=self._param,
|
|
293
|
+
metadata_tracker=self._metadata_tracker,
|
|
294
|
+
):
|
|
295
|
+
if isinstance(item, message.AssistantMessage):
|
|
296
|
+
self._completed = True
|
|
297
|
+
yield item
|
|
298
|
+
|
|
299
|
+
def get_partial_message(self) -> message.AssistantMessage | None:
|
|
300
|
+
if self._completed:
|
|
301
|
+
return None
|
|
302
|
+
return self._state.get_partial_message()
|
|
303
|
+
|
|
304
|
+
|
|
236
305
|
@register(llm_param.LLMClientProtocol.RESPONSES)
|
|
237
306
|
class ResponsesClient(LLMClientABC):
|
|
238
307
|
def __init__(self, config: llm_param.LLMConfigParameter):
|
|
@@ -264,7 +333,7 @@ class ResponsesClient(LLMClientABC):
|
|
|
264
333
|
return cls(config)
|
|
265
334
|
|
|
266
335
|
@override
|
|
267
|
-
async def call(self, param: llm_param.LLMCallParameter) ->
|
|
336
|
+
async def call(self, param: llm_param.LLMCallParameter) -> LLMStreamABC:
|
|
268
337
|
param = apply_config_defaults(param, self.get_llm_config())
|
|
269
338
|
|
|
270
339
|
metadata_tracker = MetadataTracker(cost_config=self.get_llm_config().cost)
|
|
@@ -283,9 +352,6 @@ class ResponsesClient(LLMClientABC):
|
|
|
283
352
|
)
|
|
284
353
|
except (openai.OpenAIError, httpx.HTTPError) as e:
|
|
285
354
|
error_message = f"{e.__class__.__name__} {e!s}"
|
|
286
|
-
|
|
287
|
-
yield item
|
|
288
|
-
return
|
|
355
|
+
return error_llm_stream(metadata_tracker, error=error_message)
|
|
289
356
|
|
|
290
|
-
|
|
291
|
-
yield item
|
|
357
|
+
return ResponsesLLMStream(stream, param=param, metadata_tracker=metadata_tracker)
|
klaude_code/llm/usage.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import time
|
|
2
|
+
from collections.abc import AsyncGenerator
|
|
2
3
|
|
|
3
4
|
import openai.types
|
|
4
5
|
|
|
5
6
|
from klaude_code.const import THROUGHPUT_MIN_DURATION_SEC
|
|
7
|
+
from klaude_code.llm.client import LLMStreamABC
|
|
6
8
|
from klaude_code.protocol import llm_param, message, model
|
|
7
9
|
|
|
8
10
|
|
|
@@ -114,6 +116,34 @@ def error_stream_items(
|
|
|
114
116
|
]
|
|
115
117
|
|
|
116
118
|
|
|
119
|
+
class ErrorLLMStream(LLMStreamABC):
|
|
120
|
+
"""LLMStream implementation for error scenarios."""
|
|
121
|
+
|
|
122
|
+
def __init__(self, items: list[message.LLMStreamItem]) -> None:
|
|
123
|
+
self._items = list(items)
|
|
124
|
+
|
|
125
|
+
def __aiter__(self) -> AsyncGenerator[message.LLMStreamItem]:
|
|
126
|
+
return self._iterate()
|
|
127
|
+
|
|
128
|
+
async def _iterate(self) -> AsyncGenerator[message.LLMStreamItem]:
|
|
129
|
+
for item in self._items:
|
|
130
|
+
yield item
|
|
131
|
+
|
|
132
|
+
def get_partial_message(self) -> message.AssistantMessage | None:
|
|
133
|
+
return None
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def error_llm_stream(
|
|
137
|
+
metadata_tracker: MetadataTracker,
|
|
138
|
+
*,
|
|
139
|
+
error: str,
|
|
140
|
+
response_id: str | None = None,
|
|
141
|
+
) -> ErrorLLMStream:
|
|
142
|
+
"""Create an LLMStream that yields error items."""
|
|
143
|
+
items = error_stream_items(metadata_tracker, error=error, response_id=response_id)
|
|
144
|
+
return ErrorLLMStream(items)
|
|
145
|
+
|
|
146
|
+
|
|
117
147
|
def convert_usage(
|
|
118
148
|
usage: openai.types.CompletionUsage,
|
|
119
149
|
context_limit: int | None = None,
|
klaude_code/protocol/commands.py
CHANGED
|
@@ -28,10 +28,6 @@ class CommandName(str, Enum):
|
|
|
28
28
|
FORK_SESSION = "fork-session"
|
|
29
29
|
RESUME = "resume"
|
|
30
30
|
COPY = "copy"
|
|
31
|
-
# PLAN and DOC are dynamically registered now, but kept here if needed for reference
|
|
32
|
-
# or we can remove them if no code explicitly imports them.
|
|
33
|
-
# PLAN = "plan"
|
|
34
|
-
# DOC = "doc"
|
|
35
31
|
|
|
36
32
|
def __str__(self) -> str:
|
|
37
33
|
return self.value
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from pydantic import Field
|
|
4
|
-
|
|
5
3
|
from klaude_code.protocol import llm_param
|
|
6
4
|
from klaude_code.protocol.events.chat import DeveloperMessageEvent, UserMessageEvent
|
|
7
5
|
from klaude_code.protocol.events.lifecycle import TaskFinishEvent, TaskStartEvent, TurnStartEvent
|
|
@@ -16,8 +14,6 @@ class WelcomeEvent(Event):
|
|
|
16
14
|
work_dir: str
|
|
17
15
|
llm_config: llm_param.LLMConfigParameter
|
|
18
16
|
show_klaude_code_info: bool = True
|
|
19
|
-
show_sub_agent_models: bool = True
|
|
20
|
-
sub_agent_models: dict[str, llm_param.LLMConfigParameter] = Field(default_factory=dict)
|
|
21
17
|
|
|
22
18
|
|
|
23
19
|
class ErrorEvent(Event):
|
klaude_code/protocol/model.py
CHANGED
|
@@ -75,6 +75,7 @@ class TaskMetadata(BaseModel):
|
|
|
75
75
|
usage: Usage | None = None
|
|
76
76
|
model_name: str = ""
|
|
77
77
|
provider: str | None = None
|
|
78
|
+
description: str | None = None
|
|
78
79
|
task_duration_s: float | None = None
|
|
79
80
|
turn_count: int = 0
|
|
80
81
|
|
|
@@ -216,13 +217,6 @@ class MermaidLinkUIExtra(BaseModel):
|
|
|
216
217
|
line_count: int
|
|
217
218
|
|
|
218
219
|
|
|
219
|
-
class TruncationUIExtra(BaseModel):
|
|
220
|
-
type: Literal["truncation"] = "truncation"
|
|
221
|
-
saved_file_path: str
|
|
222
|
-
original_length: int
|
|
223
|
-
truncated_length: int
|
|
224
|
-
|
|
225
|
-
|
|
226
220
|
class MarkdownDocUIExtra(BaseModel):
|
|
227
221
|
type: Literal["markdown_doc"] = "markdown_doc"
|
|
228
222
|
file_path: str
|
|
@@ -237,13 +231,7 @@ class SessionStatusUIExtra(BaseModel):
|
|
|
237
231
|
|
|
238
232
|
|
|
239
233
|
MultiUIExtraItem = (
|
|
240
|
-
DiffUIExtra
|
|
241
|
-
| TodoListUIExtra
|
|
242
|
-
| SessionIdUIExtra
|
|
243
|
-
| MermaidLinkUIExtra
|
|
244
|
-
| TruncationUIExtra
|
|
245
|
-
| MarkdownDocUIExtra
|
|
246
|
-
| SessionStatusUIExtra
|
|
234
|
+
DiffUIExtra | TodoListUIExtra | SessionIdUIExtra | MermaidLinkUIExtra | MarkdownDocUIExtra | SessionStatusUIExtra
|
|
247
235
|
)
|
|
248
236
|
|
|
249
237
|
|
|
@@ -263,7 +251,6 @@ ToolResultUIExtra = Annotated[
|
|
|
263
251
|
| TodoListUIExtra
|
|
264
252
|
| SessionIdUIExtra
|
|
265
253
|
| MermaidLinkUIExtra
|
|
266
|
-
| TruncationUIExtra
|
|
267
254
|
| MarkdownDocUIExtra
|
|
268
255
|
| SessionStatusUIExtra
|
|
269
256
|
| MultiUIExtra,
|
|
@@ -8,16 +8,6 @@ Spin up a fast agent specialized for exploring codebases. Use this when you need
|
|
|
8
8
|
search code for keywords (eg. "API endpoints"), or answer questions about the codebase (eg. "how do API endpoints work?")\
|
|
9
9
|
When calling this agent, specify the desired thoroughness level: "quick" for basic searches, "medium" for moderate exploration, or "very thorough" for comprehensive analysis across multiple locations and naming conventions.
|
|
10
10
|
Always spawn multiple search agents in parallel to maximise speed.
|
|
11
|
-
|
|
12
|
-
Structured output:
|
|
13
|
-
- Provide an `output_format` (JSON Schema) parameter for structured data back from the sub-agent
|
|
14
|
-
- Example: `output_format={"type": "object", "properties": {"files": {"type": "array", "items": {"type": "string"}, "description": "List of file paths that match the search criteria, e.g. ['src/main.py', 'src/utils/helper.py']"}}, "required": ["files"]}`\
|
|
15
|
-
|
|
16
|
-
- Agents can be resumed using the `resume` parameter by passing the agent ID from a previous invocation. When resumed, the agent
|
|
17
|
-
continues with its full previous context preserved. When NOT resuming, each invocation starts fresh and you should provide a detailed
|
|
18
|
-
task description with all necessary context.
|
|
19
|
-
- When the agent is done, it will return a single message back to you along with its agent ID. You can use this ID to resume the agent
|
|
20
|
-
later if needed for follow-up work.
|
|
21
11
|
"""
|
|
22
12
|
|
|
23
13
|
EXPLORE_PARAMETERS = {
|
|
@@ -30,13 +30,6 @@ including the generated image, so you don't need to pass `image_paths` again.
|
|
|
30
30
|
1. Call ImageGen with prompt="Generate a watercolor painting of a mountain lake" -> returns agent_id
|
|
31
31
|
2. Call ImageGen with resume=agent_id, prompt="Add a wooden cabin on the shore" -> edits the previous image
|
|
32
32
|
3. Call ImageGen with resume=agent_id, prompt="Change to sunset lighting" -> continues editing
|
|
33
|
-
|
|
34
|
-
- Agents can be resumed using the `resume` parameter by passing the agent ID from a previous invocation. When resumed, the agent
|
|
35
|
-
continues with its full previous context preserved. When NOT resuming, each invocation starts fresh and you should provide a detailed
|
|
36
|
-
task description with all necessary context.
|
|
37
|
-
- When the agent is done, it will return a single message back to you along with its agent ID. You can use this ID to resume the agent
|
|
38
|
-
later if needed for follow-up work.
|
|
39
|
-
|
|
40
33
|
"""
|
|
41
34
|
|
|
42
35
|
|
|
@@ -20,16 +20,6 @@ Usage notes:
|
|
|
20
20
|
- Clearly tell the agent whether you expect it to write code or just to do research (search, file reads, etc.), since it is not aware of the user's intent
|
|
21
21
|
- If the agent description mentions that it should be used proactively, then you should try your best to use it without the user having to ask for it first. Use your judgement.
|
|
22
22
|
- If the user specifies that they want you to run agents "in parallel", you MUST send a single message with multiple Task tool use content blocks. For example, if you need to launch both a code-reviewer agent and a test-runner agent in parallel, send a single message with both tool calls.
|
|
23
|
-
|
|
24
|
-
Structured output:
|
|
25
|
-
- Provide an `output_format` (JSON Schema) parameter for structured data back from the agent
|
|
26
|
-
- Example: `output_format={"type": "object", "properties": {"files": {"type": "array", "items": {"type": "string"}, "description": "List of file paths that match the search criteria, e.g. ['src/main.py', 'src/utils/helper.py']"}}, "required": ["files"]}`\
|
|
27
|
-
|
|
28
|
-
- Agents can be resumed using the `resume` parameter by passing the agent ID from a previous invocation. When resumed, the agent
|
|
29
|
-
continues with its full previous context preserved. When NOT resuming, each invocation starts fresh and you should provide a detailed
|
|
30
|
-
task description with all necessary context.
|
|
31
|
-
- When the agent is done, it will return a single message back to you along with its agent ID. You can use this ID to resume the agent
|
|
32
|
-
later if needed for follow-up work.
|
|
33
23
|
"""
|
|
34
24
|
|
|
35
25
|
TASK_PARAMETERS = {
|
|
@@ -11,28 +11,20 @@ Launch a sub-agent to search the web, fetch pages, and analyze content. Use this
|
|
|
11
11
|
- Gathering comprehensive information from multiple web sources
|
|
12
12
|
|
|
13
13
|
Capabilities:
|
|
14
|
-
- Search the web to find relevant pages
|
|
15
|
-
- Fetch and parse web pages
|
|
16
|
-
- Follow links across multiple pages
|
|
14
|
+
- Search the web to find relevant pages
|
|
15
|
+
- Fetch and parse web pages
|
|
16
|
+
- Follow links across multiple pages
|
|
17
17
|
- Aggregate findings from multiple sources
|
|
18
18
|
|
|
19
19
|
How to use:
|
|
20
20
|
- Write a clear prompt describing what information you need - the agent will search and fetch as needed
|
|
21
21
|
- Account for "Today's date" in <env>. For example, if <env> says "Today's date: 2025-07-01", and the user wants the latest docs, do not use 2024 in the search query. Use 2025.
|
|
22
22
|
- Provide the url if you already know the target page
|
|
23
|
-
- Use `output_format` (JSON Schema) to get structured data back from the agent
|
|
24
23
|
|
|
25
24
|
What you receive:
|
|
26
25
|
- The agent returns a text response summarizing its findings
|
|
27
|
-
- With `output_format`, you receive structured JSON matching your schema
|
|
28
26
|
- The response is the agent's analysis, not raw web content
|
|
29
|
-
- Web content is saved to local files (paths included in Sources) - read them directly if you need full content
|
|
30
|
-
|
|
31
|
-
- Agents can be resumed using the `resume` parameter by passing the agent ID from a previous invocation. When resumed, the agent
|
|
32
|
-
continues with its full previous context preserved. When NOT resuming, each invocation starts fresh and you should provide a detailed
|
|
33
|
-
task description with all necessary context.
|
|
34
|
-
- When the agent is done, it will return a single message back to you along with its agent ID. You can use this ID to resume the agent
|
|
35
|
-
later if needed for follow-up work.
|
|
27
|
+
- Web content is saved to local files (paths included in Sources) - read them directly if you need full content
|
|
36
28
|
"""
|
|
37
29
|
|
|
38
30
|
WEB_AGENT_PARAMETERS = {
|
|
@@ -9,11 +9,11 @@
|
|
|
9
9
|
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 24 24%22 fill=%22none%22 stroke=%22%230b5bd3%22 stroke-width=%222%22 stroke-linecap=%22round%22 stroke-linejoin=%22round%22><polyline points=%2216 18 22 12 16 6%22></polyline><polyline points=%228 6 2 12 8 18%22></polyline></svg>"
|
|
10
10
|
/>
|
|
11
11
|
<link
|
|
12
|
-
href="https://cdn.jsdelivr.net/npm/@fontsource/
|
|
12
|
+
href="https://cdn.jsdelivr.net/npm/@fontsource/lilex/400.css"
|
|
13
13
|
rel="stylesheet"
|
|
14
14
|
/>
|
|
15
15
|
<link
|
|
16
|
-
href="https://cdn.jsdelivr.net/npm/@fontsource/
|
|
16
|
+
href="https://cdn.jsdelivr.net/npm/@fontsource/lilex/700.css"
|
|
17
17
|
rel="stylesheet"
|
|
18
18
|
/>
|
|
19
19
|
<link
|
|
@@ -57,8 +57,8 @@
|
|
|
57
57
|
--error: #c62828;
|
|
58
58
|
--fg-inline-code: #1f4fbf;
|
|
59
59
|
--font-sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
|
|
60
|
-
--font-mono: "
|
|
61
|
-
--font-markdown-mono: "
|
|
60
|
+
--font-mono: "Lilex", "TX-02", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
|
|
61
|
+
--font-markdown-mono: "Lilex", "TX-02", ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, Courier New, monospace;
|
|
62
62
|
--font-markdown: var(--font-sans);
|
|
63
63
|
--font-weight-bold: 700;
|
|
64
64
|
--font-size-xs: 12px;
|