klaude-code 2.8.1__py3-none-any.whl → 2.9.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.
Files changed (93) hide show
  1. klaude_code/app/runtime.py +2 -1
  2. klaude_code/auth/antigravity/oauth.py +0 -9
  3. klaude_code/auth/antigravity/token_manager.py +0 -18
  4. klaude_code/auth/base.py +53 -0
  5. klaude_code/auth/codex/exceptions.py +0 -4
  6. klaude_code/auth/codex/oauth.py +32 -28
  7. klaude_code/auth/codex/token_manager.py +0 -18
  8. klaude_code/cli/cost_cmd.py +128 -39
  9. klaude_code/cli/list_model.py +27 -10
  10. klaude_code/cli/main.py +14 -3
  11. klaude_code/config/assets/builtin_config.yaml +8 -24
  12. klaude_code/config/config.py +47 -25
  13. klaude_code/config/sub_agent_model_helper.py +18 -13
  14. klaude_code/config/thinking.py +0 -8
  15. klaude_code/const.py +1 -1
  16. klaude_code/core/agent_profile.py +10 -52
  17. klaude_code/core/compaction/overflow.py +0 -4
  18. klaude_code/core/executor.py +33 -5
  19. klaude_code/core/manager/llm_clients.py +9 -1
  20. klaude_code/core/prompts/prompt-claude-code.md +4 -4
  21. klaude_code/core/reminders.py +21 -23
  22. klaude_code/core/task.py +0 -4
  23. klaude_code/core/tool/__init__.py +3 -2
  24. klaude_code/core/tool/file/apply_patch.py +0 -27
  25. klaude_code/core/tool/file/read_tool.md +3 -2
  26. klaude_code/core/tool/file/read_tool.py +15 -2
  27. klaude_code/core/tool/offload.py +0 -35
  28. klaude_code/core/tool/sub_agent/__init__.py +6 -0
  29. klaude_code/core/tool/sub_agent/image_gen.md +16 -0
  30. klaude_code/core/tool/sub_agent/image_gen.py +146 -0
  31. klaude_code/core/tool/sub_agent/task.md +20 -0
  32. klaude_code/core/tool/sub_agent/task.py +205 -0
  33. klaude_code/core/tool/tool_registry.py +0 -16
  34. klaude_code/core/turn.py +1 -1
  35. klaude_code/llm/anthropic/input.py +6 -5
  36. klaude_code/llm/antigravity/input.py +14 -7
  37. klaude_code/llm/codex/client.py +22 -0
  38. klaude_code/llm/codex/prompt_sync.py +237 -0
  39. klaude_code/llm/google/client.py +8 -6
  40. klaude_code/llm/google/input.py +20 -12
  41. klaude_code/llm/image.py +18 -11
  42. klaude_code/llm/input_common.py +14 -6
  43. klaude_code/llm/json_stable.py +37 -0
  44. klaude_code/llm/openai_compatible/input.py +0 -10
  45. klaude_code/llm/openai_compatible/stream.py +16 -1
  46. klaude_code/llm/registry.py +0 -5
  47. klaude_code/llm/responses/input.py +15 -5
  48. klaude_code/llm/usage.py +0 -8
  49. klaude_code/protocol/events.py +2 -1
  50. klaude_code/protocol/message.py +2 -2
  51. klaude_code/protocol/model.py +20 -1
  52. klaude_code/protocol/op.py +13 -0
  53. klaude_code/protocol/op_handler.py +5 -0
  54. klaude_code/protocol/sub_agent/AGENTS.md +5 -5
  55. klaude_code/protocol/sub_agent/__init__.py +13 -34
  56. klaude_code/protocol/sub_agent/explore.py +7 -34
  57. klaude_code/protocol/sub_agent/image_gen.py +3 -74
  58. klaude_code/protocol/sub_agent/task.py +3 -47
  59. klaude_code/protocol/sub_agent/web.py +8 -52
  60. klaude_code/protocol/tools.py +2 -0
  61. klaude_code/session/session.py +58 -21
  62. klaude_code/session/store.py +0 -4
  63. klaude_code/skill/assets/deslop/SKILL.md +9 -0
  64. klaude_code/skill/system_skills.py +0 -20
  65. klaude_code/tui/command/fork_session_cmd.py +5 -2
  66. klaude_code/tui/command/resume_cmd.py +9 -2
  67. klaude_code/tui/command/sub_agent_model_cmd.py +85 -18
  68. klaude_code/tui/components/assistant.py +0 -26
  69. klaude_code/tui/components/command_output.py +3 -1
  70. klaude_code/tui/components/developer.py +3 -0
  71. klaude_code/tui/components/diffs.py +2 -208
  72. klaude_code/tui/components/errors.py +4 -0
  73. klaude_code/tui/components/mermaid_viewer.py +2 -2
  74. klaude_code/tui/components/rich/markdown.py +0 -54
  75. klaude_code/tui/components/rich/theme.py +2 -0
  76. klaude_code/tui/components/sub_agent.py +2 -46
  77. klaude_code/tui/components/thinking.py +0 -33
  78. klaude_code/tui/components/tools.py +43 -21
  79. klaude_code/tui/input/images.py +21 -18
  80. klaude_code/tui/input/key_bindings.py +2 -2
  81. klaude_code/tui/input/prompt_toolkit.py +49 -49
  82. klaude_code/tui/machine.py +15 -11
  83. klaude_code/tui/renderer.py +11 -20
  84. klaude_code/tui/runner.py +2 -1
  85. klaude_code/tui/terminal/image.py +6 -34
  86. klaude_code/ui/common.py +0 -70
  87. {klaude_code-2.8.1.dist-info → klaude_code-2.9.0.dist-info}/METADATA +3 -6
  88. {klaude_code-2.8.1.dist-info → klaude_code-2.9.0.dist-info}/RECORD +90 -86
  89. klaude_code/core/tool/sub_agent_tool.py +0 -126
  90. klaude_code/llm/openai_compatible/tool_call_accumulator.py +0 -108
  91. klaude_code/tui/components/rich/searchable_text.py +0 -68
  92. {klaude_code-2.8.1.dist-info → klaude_code-2.9.0.dist-info}/WHEEL +0 -0
  93. {klaude_code-2.8.1.dist-info → klaude_code-2.9.0.dist-info}/entry_points.txt +0 -0
@@ -38,11 +38,6 @@ def _load_protocol(protocol: llm_param.LLMClientProtocol) -> None:
38
38
  _loaded_protocols.add(protocol)
39
39
 
40
40
 
41
- def load_protocol(protocol: llm_param.LLMClientProtocol) -> None:
42
- """Load the module for a specific protocol on demand."""
43
- _load_protocol(protocol)
44
-
45
-
46
41
  def register(name: llm_param.LLMClientProtocol) -> Callable[[_T], _T]:
47
42
  """Decorator to register an LLM client class for a protocol."""
48
43
 
@@ -7,6 +7,7 @@ from typing import Any, cast
7
7
  from openai.types import responses
8
8
 
9
9
  from klaude_code.const import EMPTY_TOOL_OUTPUT_MESSAGE
10
+ from klaude_code.llm.image import image_file_to_data_url
10
11
  from klaude_code.llm.input_common import (
11
12
  DeveloperAttachment,
12
13
  attach_developer_messages,
@@ -16,6 +17,12 @@ from klaude_code.llm.input_common import (
16
17
  from klaude_code.protocol import llm_param, message
17
18
 
18
19
 
20
+ def _image_to_url(image: message.ImageURLPart | message.ImageFilePart) -> str:
21
+ if isinstance(image, message.ImageFilePart):
22
+ return image_file_to_data_url(image)
23
+ return image.url
24
+
25
+
19
26
  def _build_user_content_parts(
20
27
  user: message.UserMessage,
21
28
  attachment: DeveloperAttachment,
@@ -24,11 +31,11 @@ def _build_user_content_parts(
24
31
  for part in user.parts:
25
32
  if isinstance(part, message.TextPart):
26
33
  parts.append(cast(responses.ResponseInputContentParam, {"type": "input_text", "text": part.text}))
27
- elif isinstance(part, message.ImageURLPart):
34
+ elif isinstance(part, (message.ImageURLPart, message.ImageFilePart)):
28
35
  parts.append(
29
36
  cast(
30
37
  responses.ResponseInputContentParam,
31
- {"type": "input_image", "detail": "auto", "image_url": part.url},
38
+ {"type": "input_image", "detail": "auto", "image_url": _image_to_url(part)},
32
39
  )
33
40
  )
34
41
  if attachment.text:
@@ -37,7 +44,7 @@ def _build_user_content_parts(
37
44
  parts.append(
38
45
  cast(
39
46
  responses.ResponseInputContentParam,
40
- {"type": "input_image", "detail": "auto", "image_url": image.url},
47
+ {"type": "input_image", "detail": "auto", "image_url": _image_to_url(image)},
41
48
  )
42
49
  )
43
50
  if not parts:
@@ -56,12 +63,15 @@ def _build_tool_result_item(
56
63
  )
57
64
  if text_output:
58
65
  content_parts.append(cast(responses.ResponseInputContentParam, {"type": "input_text", "text": text_output}))
59
- images = [part for part in tool.parts if isinstance(part, message.ImageURLPart)] + attachment.images
66
+ images: list[message.ImageURLPart | message.ImageFilePart] = [
67
+ part for part in tool.parts if isinstance(part, (message.ImageURLPart, message.ImageFilePart))
68
+ ]
69
+ images.extend(attachment.images)
60
70
  for image in images:
61
71
  content_parts.append(
62
72
  cast(
63
73
  responses.ResponseInputContentParam,
64
- {"type": "input_image", "detail": "auto", "image_url": image.url},
74
+ {"type": "input_image", "detail": "auto", "image_url": _image_to_url(image)},
65
75
  )
66
76
  )
67
77
 
klaude_code/llm/usage.py CHANGED
@@ -44,14 +44,6 @@ class MetadataTracker:
44
44
  self._usage = model.Usage()
45
45
  self._cost_config = cost_config
46
46
 
47
- @property
48
- def first_token_time(self) -> float | None:
49
- return self._first_token_time
50
-
51
- @property
52
- def last_token_time(self) -> float | None:
53
- return self._last_token_time
54
-
55
47
  def record_token(self) -> None:
56
48
  """Record a token arrival, updating first/last token times."""
57
49
  now = time.time()
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import time
4
+ from collections.abc import Sequence
4
5
  from typing import Literal
5
6
 
6
7
  from pydantic import BaseModel, Field
@@ -58,7 +59,7 @@ class ResponseEvent(Event):
58
59
 
59
60
  class UserMessageEvent(Event):
60
61
  content: str
61
- images: list[message.ImageURLPart] | None = None
62
+ images: Sequence[message.ImageURLPart | message.ImageFilePart] | None = None
62
63
 
63
64
 
64
65
  class DeveloperMessageEvent(Event):
@@ -213,7 +213,7 @@ class UserInputPayload(BaseModel):
213
213
  """
214
214
 
215
215
  text: str
216
- images: list[ImageURLPart] | None = None
216
+ images: Sequence[ImageURLPart | ImageFilePart] | None = None
217
217
 
218
218
 
219
219
  # Helper functions
@@ -225,7 +225,7 @@ def text_parts_from_str(text: str | None) -> list[Part]:
225
225
  return [TextPart(text=text)]
226
226
 
227
227
 
228
- def parts_from_text_and_images(text: str | None, images: list[ImageURLPart] | None) -> list[Part]:
228
+ def parts_from_text_and_images(text: str | None, images: Sequence[ImageURLPart | ImageFilePart] | None) -> list[Part]:
229
229
  parts: list[Part] = []
230
230
  if text:
231
231
  parts.append(TextPart(text=text))
@@ -217,6 +217,11 @@ class MermaidLinkUIExtra(BaseModel):
217
217
  line_count: int
218
218
 
219
219
 
220
+ class ImageUIExtra(BaseModel):
221
+ type: Literal["image"] = "image"
222
+ file_path: str
223
+
224
+
220
225
  class MarkdownDocUIExtra(BaseModel):
221
226
  type: Literal["markdown_doc"] = "markdown_doc"
222
227
  file_path: str
@@ -231,7 +236,13 @@ class SessionStatusUIExtra(BaseModel):
231
236
 
232
237
 
233
238
  MultiUIExtraItem = (
234
- DiffUIExtra | TodoListUIExtra | SessionIdUIExtra | MermaidLinkUIExtra | MarkdownDocUIExtra | SessionStatusUIExtra
239
+ DiffUIExtra
240
+ | TodoListUIExtra
241
+ | SessionIdUIExtra
242
+ | MermaidLinkUIExtra
243
+ | ImageUIExtra
244
+ | MarkdownDocUIExtra
245
+ | SessionStatusUIExtra
235
246
  )
236
247
 
237
248
 
@@ -251,6 +262,7 @@ ToolResultUIExtra = Annotated[
251
262
  | TodoListUIExtra
252
263
  | SessionIdUIExtra
253
264
  | MermaidLinkUIExtra
265
+ | ImageUIExtra
254
266
  | MarkdownDocUIExtra
255
267
  | SessionStatusUIExtra
256
268
  | MultiUIExtra,
@@ -292,6 +304,7 @@ class AtFileOpsUIItem(BaseModel):
292
304
  class UserImagesUIItem(BaseModel):
293
305
  type: Literal["user_images"] = "user_images"
294
306
  count: int
307
+ paths: list[str] = []
295
308
 
296
309
 
297
310
  class SkillActivatedUIItem(BaseModel):
@@ -299,6 +312,11 @@ class SkillActivatedUIItem(BaseModel):
299
312
  name: str
300
313
 
301
314
 
315
+ class AtFileImagesUIItem(BaseModel):
316
+ type: Literal["at_file_images"] = "at_file_images"
317
+ paths: list[str]
318
+
319
+
302
320
  type DeveloperUIItem = (
303
321
  MemoryLoadedUIItem
304
322
  | ExternalFileChangesUIItem
@@ -306,6 +324,7 @@ type DeveloperUIItem = (
306
324
  | AtFileOpsUIItem
307
325
  | UserImagesUIItem
308
326
  | SkillActivatedUIItem
327
+ | AtFileImagesUIItem
309
328
  )
310
329
 
311
330
 
@@ -27,6 +27,7 @@ class OperationType(Enum):
27
27
  CONTINUE_AGENT = "continue_agent"
28
28
  COMPACT_SESSION = "compact_session"
29
29
  CHANGE_MODEL = "change_model"
30
+ CHANGE_COMPACT_MODEL = "change_compact_model"
30
31
  CHANGE_SUB_AGENT_MODEL = "change_sub_agent_model"
31
32
  CHANGE_THINKING = "change_thinking"
32
33
  CLEAR_SESSION = "clear_session"
@@ -108,6 +109,18 @@ class ChangeModelOperation(Operation):
108
109
  await handler.handle_change_model(self)
109
110
 
110
111
 
112
+ class ChangeCompactModelOperation(Operation):
113
+ """Operation for changing the compact model (used for session compaction)."""
114
+
115
+ type: OperationType = OperationType.CHANGE_COMPACT_MODEL
116
+ session_id: str
117
+ model_name: str | None
118
+ save_as_default: bool = False
119
+
120
+ async def execute(self, handler: OperationHandler) -> None:
121
+ await handler.handle_change_compact_model(self)
122
+
123
+
111
124
  class ChangeThinkingOperation(Operation):
112
125
  """Operation for changing the thinking/reasoning configuration."""
113
126
 
@@ -10,6 +10,7 @@ from typing import TYPE_CHECKING, Protocol
10
10
 
11
11
  if TYPE_CHECKING:
12
12
  from klaude_code.protocol.op import (
13
+ ChangeCompactModelOperation,
13
14
  ChangeModelOperation,
14
15
  ChangeSubAgentModelOperation,
15
16
  ChangeThinkingOperation,
@@ -43,6 +44,10 @@ class OperationHandler(Protocol):
43
44
  """Handle a change model operation."""
44
45
  ...
45
46
 
47
+ async def handle_change_compact_model(self, operation: ChangeCompactModelOperation) -> None:
48
+ """Handle a change compact model operation."""
49
+ ...
50
+
46
51
  async def handle_change_thinking(self, operation: ChangeThinkingOperation) -> None:
47
52
  """Handle a change thinking operation."""
48
53
  ...
@@ -1,6 +1,6 @@
1
1
  # Sub-Agent Protocol
2
2
 
3
- Sub-agents are specialized agents invoked by the main agent as tools. This module defines profiles and registration.
3
+ Sub-agents are specialized agent types invoked by tools like Task and ImageGen. This module defines profiles and registration.
4
4
 
5
5
  ## Key Constraint
6
6
 
@@ -9,7 +9,7 @@ The `protocol` layer cannot import from `config` or `core` (enforced by import-l
9
9
  ## Core Files
10
10
 
11
11
  - `__init__.py` - `SubAgentProfile` dataclass and registration. Defines `AVAILABILITY_*` constants.
12
- - `image_gen.py`, `task.py`, `explore.py`, `web.py` - Individual sub-agent definitions.
12
+ - `image_gen.py`, `task.py`, `explore.py`, `web.py` - Individual sub-agent type definitions.
13
13
 
14
14
  ## Availability Requirement Flow
15
15
 
@@ -23,6 +23,6 @@ Some sub-agents require specific model capabilities (e.g., ImageGen needs an ima
23
23
  ## Model Selection
24
24
 
25
25
  For sub-agents with `availability_requirement`, priority is:
26
- 1. Explicit config in `sub_agent_models`
27
- 2. Auto-resolve via requirement
28
- 3. If neither found, sub-agent is unavailable (no fallback to main agent model)
26
+ 1. Explicit config in `sub_agent_models` for the specific type
27
+ 2. Fallback to the Task model config (if present)
28
+ 3. Otherwise inherit the main agent model
@@ -1,7 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  from collections.abc import Callable
4
- from dataclasses import dataclass, field
4
+ from dataclasses import dataclass
5
5
  from typing import TYPE_CHECKING, Any
6
6
 
7
7
  from klaude_code.protocol import tools
@@ -38,32 +38,22 @@ class SubAgentProfile:
38
38
  3. Build the prompt for the sub agent
39
39
  """
40
40
 
41
- # Identity - single name used for type, tool_name, config_key, and prompt_key
42
- name: str # e.g., "Task", "Explore", "WebAgent"
41
+ # Identity - single name used for type, config_key, and prompt_key
42
+ name: str # e.g., "Task", "Explore", "Web", "ImageGen"
43
43
 
44
- # Tool schema
45
- description: str # Tool description shown to the main agent
46
- parameters: dict[str, Any] = field(
47
- default_factory=lambda: dict[str, Any](), hash=False
48
- ) # JSON Schema for tool parameters
49
-
50
- # System prompt
44
+ # Sub-agent run configuration
51
45
  prompt_file: str = "" # Resource file path relative to core package (e.g., "prompts/prompt-sub-agent.md")
52
-
53
- # Sub agent configuration
54
46
  tool_set: tuple[str, ...] = () # Tools available to this sub agent
55
47
  prompt_builder: PromptBuilder = _default_prompt_builder # Builds the sub agent prompt from tool arguments
56
48
 
49
+ # Entry-point metadata for Task tool (RunSubAgent)
50
+ invoker_type: str | None = None # Tool-level type mapping (e.g., "general-purpose", "explore", "web")
51
+ invoker_summary: str = "" # Short description shown under Task tool supported types
52
+ standalone_tool: bool = False # True for sub-agents invoked by dedicated tools (e.g., ImageGen)
53
+
57
54
  # UI display
58
55
  active_form: str = "" # Active form for spinner status (e.g., "Tasking", "Exploring")
59
56
 
60
- # Availability
61
- enabled_by_default: bool = True
62
- show_in_main_agent: bool = True
63
-
64
- # Structured output support: specifies which parameter in the tool schema contains the output schema
65
- output_schema_arg: str | None = None
66
-
67
57
  # Config-based availability requirement (e.g., "image_model" means requires an image model)
68
58
  # The actual check is performed in the core layer to avoid circular imports.
69
59
  availability_requirement: str | None = None
@@ -85,25 +75,14 @@ def get_sub_agent_profile(sub_agent_type: tools.SubAgentType) -> SubAgentProfile
85
75
  raise KeyError(f"Unknown sub agent type: {sub_agent_type}") from exc
86
76
 
87
77
 
88
- def iter_sub_agent_profiles(enabled_only: bool = False) -> list[SubAgentProfile]:
89
- profiles = list(_PROFILES.values())
90
- if not enabled_only:
91
- return profiles
92
- return [p for p in profiles if p.enabled_by_default]
93
-
94
-
95
- def get_sub_agent_profile_by_tool(tool_name: str) -> SubAgentProfile | None:
96
- return _PROFILES.get(tool_name)
78
+ def iter_sub_agent_profiles() -> list[SubAgentProfile]:
79
+ return list(_PROFILES.values())
97
80
 
98
81
 
99
82
  def is_sub_agent_tool(tool_name: str) -> bool:
100
- return tool_name in _PROFILES
101
-
83
+ from klaude_code.protocol import tools
102
84
 
103
- def sub_agent_tool_names(enabled_only: bool = False) -> list[str]:
104
- return [
105
- profile.name for profile in iter_sub_agent_profiles(enabled_only=enabled_only) if profile.show_in_main_agent
106
- ]
85
+ return tool_name in {tools.TASK, tools.IMAGE_GEN}
107
86
 
108
87
 
109
88
  # Import sub-agent modules to trigger registration
@@ -3,46 +3,19 @@ from __future__ import annotations
3
3
  from klaude_code.protocol import tools
4
4
  from klaude_code.protocol.sub_agent import SubAgentProfile, register_sub_agent
5
5
 
6
- EXPLORE_DESCRIPTION = """\
7
- Spin up a fast agent specialized for exploring codebases. Use this when you need to quickly find files by patterns (eg. "src/components/**/*.tsx"), \
8
- search code for keywords (eg. "API endpoints"), or answer questions about the codebase (eg. "how do API endpoints work?")\
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
- Always spawn multiple search agents in parallel to maximise speed.
11
- """
12
-
13
- EXPLORE_PARAMETERS = {
14
- "type": "object",
15
- "properties": {
16
- "resume": {
17
- "type": "string",
18
- "description": "Optional agent ID to resume from. If provided, the agent will continue from the previous execution transcript.",
19
- },
20
- "description": {
21
- "type": "string",
22
- "description": "Short (3-5 words) label for the exploration goal",
23
- },
24
- "prompt": {
25
- "type": "string",
26
- "description": "The task for the agent to perform",
27
- },
28
- "output_format": {
29
- "type": "object",
30
- "description": "Optional JSON Schema for sub-agent structured output",
31
- },
32
- },
33
- "required": ["description", "prompt"],
34
- "additionalProperties": False,
35
- }
36
-
6
+ EXPLORE_SUMMARY = (
7
+ "Fast codebase exploration: find files by patterns, search for keywords, and summarize how things work.\n"
8
+ "Always spawn multiple search agents in parallel to maximise speed. "
9
+ "(Tools: Bash, Read)"
10
+ )
37
11
 
38
12
  register_sub_agent(
39
13
  SubAgentProfile(
40
14
  name="Explore",
41
- description=EXPLORE_DESCRIPTION,
42
- parameters=EXPLORE_PARAMETERS,
43
15
  prompt_file="prompts/prompt-sub-agent-explore.md",
44
16
  tool_set=(tools.BASH, tools.READ),
17
+ invoker_type="explore",
18
+ invoker_summary=EXPLORE_SUMMARY,
45
19
  active_form="Exploring",
46
- output_schema_arg="output_format",
47
20
  )
48
21
  )
@@ -4,76 +4,6 @@ from typing import Any, cast
4
4
 
5
5
  from klaude_code.protocol.sub_agent import AVAILABILITY_IMAGE_MODEL, SubAgentProfile, register_sub_agent
6
6
 
7
- IMAGE_GEN_DESCRIPTION = """\
8
- Generate one or more images from a text prompt.
9
-
10
- This tool invokes an Image Gen model to generate images. The generated image paths are automatically \
11
- returned in the response.
12
-
13
- Inputs:
14
- - `prompt`: The main instruction describing the desired image.
15
- - `image_paths` (optional): Local image file paths to use as references for editing or style guidance.
16
- - `generation` (optional): Per-call image generation settings (aspect ratio / size).
17
-
18
- Notes:
19
- - Provide a short textual description of the generated image(s).
20
- - Do NOT include base64 image data in text output.
21
- - When providing multiple input images, describe each image's characteristics and purpose in the prompt, \
22
- not just "image 1, image 2" - the image model cannot distinguish image order. \
23
- For example: "Edit the first image (a photo of a cat sitting on a windowsill) to match the style of \
24
- the second image (Van Gogh's Starry Night painting with swirling blue brushstrokes)."
25
-
26
- Multi-turn image editing:
27
- - Use `resume` to continue editing a previously generated image. The agent preserves its full context \
28
- including the generated image, so you don't need to pass `image_paths` again.
29
- - Example workflow:
30
- 1. Call ImageGen with prompt="Generate a watercolor painting of a mountain lake" -> returns agent_id
31
- 2. Call ImageGen with resume=agent_id, prompt="Add a wooden cabin on the shore" -> edits the previous image
32
- 3. Call ImageGen with resume=agent_id, prompt="Change to sunset lighting" -> continues editing
33
- """
34
-
35
-
36
- IMAGE_GEN_PARAMETERS: dict[str, Any] = {
37
- "type": "object",
38
- "properties": {
39
- "resume": {
40
- "type": "string",
41
- "description": "Optional agent ID to resume from. If provided, the agent will continue from the previous execution transcript.",
42
- },
43
- "description": {
44
- "type": "string",
45
- "description": "A short (3-5 word) description of the request.",
46
- },
47
- "prompt": {
48
- "type": "string",
49
- "description": "Text prompt for image generation.",
50
- },
51
- "image_paths": {
52
- "type": "array",
53
- "items": {"type": "string"},
54
- "description": "Optional local image file paths used as references.",
55
- },
56
- "generation": {
57
- "type": "object",
58
- "description": "Optional per-call image generation settings.",
59
- "properties": {
60
- "aspect_ratio": {
61
- "type": "string",
62
- "description": "Aspect ratio, e.g. '16:9', '1:1', '9:16'.",
63
- },
64
- "image_size": {
65
- "type": "string",
66
- "enum": ["1K", "2K", "4K"],
67
- "description": "Output size for Nano Banana Pro (must use uppercase K).",
68
- },
69
- },
70
- "additionalProperties": False,
71
- },
72
- },
73
- "required": ["prompt"],
74
- "additionalProperties": False,
75
- }
76
-
77
7
 
78
8
  def _quote_at_pattern_path(path: str) -> str:
79
9
  if any(ch.isspace() for ch in path) or '"' in path:
@@ -82,7 +12,7 @@ def _quote_at_pattern_path(path: str) -> str:
82
12
  return f"@{path}"
83
13
 
84
14
 
85
- def _build_image_gen_prompt(args: dict[str, Any]) -> str:
15
+ def build_image_gen_prompt(args: dict[str, Any]) -> str:
86
16
  prompt = str(args.get("prompt") or "").strip()
87
17
  image_paths = args.get("image_paths")
88
18
 
@@ -98,12 +28,11 @@ def _build_image_gen_prompt(args: dict[str, Any]) -> str:
98
28
  register_sub_agent(
99
29
  SubAgentProfile(
100
30
  name="ImageGen",
101
- description=IMAGE_GEN_DESCRIPTION,
102
- parameters=IMAGE_GEN_PARAMETERS,
103
31
  prompt_file="prompts/prompt-sub-agent-image-gen.md",
104
32
  tool_set=(),
105
- prompt_builder=_build_image_gen_prompt,
33
+ prompt_builder=build_image_gen_prompt,
106
34
  active_form="Generating Image",
107
35
  availability_requirement=AVAILABILITY_IMAGE_MODEL,
36
+ standalone_tool=True,
108
37
  )
109
38
  )
@@ -3,59 +3,15 @@ from __future__ import annotations
3
3
  from klaude_code.protocol import tools
4
4
  from klaude_code.protocol.sub_agent import SubAgentProfile, register_sub_agent
5
5
 
6
- TASK_DESCRIPTION = """\
7
- Launch a new agent to handle complex, multi-step tasks autonomously. \
8
-
9
- When NOT to use the Task tool:
10
- - If you want to read a specific file path, use the Read or Bash tool for `rg` instead of the Task tool, to find the match more quickly
11
- - If you are searching for a specific class definition like "class Foo", use the Bash tool for `rg` instead, to find the match more quickly
12
- - If you are searching for code within a specific file or set of 2-3 files, use the Read tool instead of the Task tool, to find the match more quickly
13
- - Other tasks that are not related to the agent descriptions above
14
-
15
- Usage notes:
16
- - Launch multiple agents concurrently whenever possible, to maximize performance; to do that, use a single message with multiple tool uses
17
- - When the agent is done, it will return a single message back to you. The result returned by the agent is not visible to the user. To show the user the result, you should send a text message back to the user with a concise summary of the result.
18
- - Each agent invocation is stateless. You will not be able to send additional messages to the agent, nor will the agent be able to communicate with you outside of its final report. Therefore, your prompt should contain a highly detailed task description for the agent to perform autonomously and you should specify exactly what information the agent should return back to you in its final and only message to you.
19
- - The agent's outputs should generally be trusted
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
- - 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
- - 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
-
25
- TASK_PARAMETERS = {
26
- "type": "object",
27
- "properties": {
28
- "resume": {
29
- "type": "string",
30
- "description": "Optional agent ID to resume from. If provided, the agent will continue from the previous execution transcript.",
31
- },
32
- "description": {
33
- "type": "string",
34
- "description": "A short (3-5 word) description of the task",
35
- },
36
- "prompt": {
37
- "type": "string",
38
- "description": "The task for the agent to perform",
39
- },
40
- "output_format": {
41
- "type": "object",
42
- "description": (
43
- "Optional JSON Schema for structured output, better with examples in argument descriptions."
44
- ),
45
- },
46
- },
47
- "required": ["description", "prompt"],
48
- "additionalProperties": False,
49
- }
6
+ TASK_SUMMARY = "General-purpose agent for researching complex questions, searching for code, and executing multi-step tasks. (Tools: All Tools)"
50
7
 
51
8
  register_sub_agent(
52
9
  SubAgentProfile(
53
10
  name="Task",
54
- description=TASK_DESCRIPTION,
55
- parameters=TASK_PARAMETERS,
56
11
  prompt_file="prompts/prompt-sub-agent.md",
57
12
  tool_set=(tools.BASH, tools.READ, tools.EDIT, tools.WRITE),
13
+ invoker_type="general-purpose",
14
+ invoker_summary=TASK_SUMMARY,
58
15
  active_form="Tasking",
59
- output_schema_arg="output_format",
60
16
  )
61
17
  )
@@ -3,63 +3,19 @@ from __future__ import annotations
3
3
  from klaude_code.protocol import tools
4
4
  from klaude_code.protocol.sub_agent import SubAgentProfile, register_sub_agent
5
5
 
6
- WEB_AGENT_DESCRIPTION = """\
7
- Launch a sub-agent to search the web, fetch pages, and analyze content. Use this for:
8
- - Accessing up-to-date information beyond your knowledge cutoff (current events, recent releases, latest docs)
9
- - Researching topics, news, APIs, or technical references
10
- - Fetching and analyzing specific URLs
11
- - Gathering comprehensive information from multiple web sources
12
-
13
- Capabilities:
14
- - Search the web to find relevant pages
15
- - Fetch and parse web pages
16
- - Follow links across multiple pages
17
- - Aggregate findings from multiple sources
18
-
19
- How to use:
20
- - Write a clear prompt describing what information you need - the agent will search and fetch as needed
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
- - Provide the url if you already know the target page
23
-
24
- What you receive:
25
- - The agent returns a text response summarizing its findings
26
- - The response is the agent's analysis, not raw web content
27
- - Web content is saved to local files (paths included in Sources) - read them directly if you need full content
28
- """
29
-
30
- WEB_AGENT_PARAMETERS = {
31
- "type": "object",
32
- "properties": {
33
- "resume": {
34
- "type": "string",
35
- "description": "Optional agent ID to resume from. If provided, the agent will continue from the previous execution transcript.",
36
- },
37
- "description": {
38
- "type": "string",
39
- "description": "A short (3-5 word) description of the task",
40
- },
41
- "prompt": {
42
- "type": "string",
43
- "description": "Instructions for searching, analyzing, or extracting content from the web page",
44
- },
45
- "output_format": {
46
- "type": "object",
47
- "description": "Optional JSON Schema for sub-agent structured output",
48
- },
49
- },
50
- "required": ["description", "prompt"],
51
- "additionalProperties": False,
52
- }
53
-
6
+ WEB_SUMMARY = (
7
+ "Web research: search, fetch, and analyze pages for up-to-date info; summarize findings with sources. "
8
+ "Include dates in queries when recency matters. "
9
+ "(Tools: WebSearch, WebFetch, Bash, Read, Write)"
10
+ )
54
11
 
55
12
  register_sub_agent(
56
13
  SubAgentProfile(
57
- name="WebAgent",
58
- description=WEB_AGENT_DESCRIPTION,
59
- parameters=WEB_AGENT_PARAMETERS,
14
+ name="Web",
60
15
  prompt_file="prompts/prompt-sub-agent-web.md",
61
16
  tool_set=(tools.BASH, tools.READ, tools.WEB_FETCH, tools.WEB_SEARCH, tools.WRITE),
17
+ invoker_type="web",
18
+ invoker_summary=WEB_SUMMARY,
62
19
  active_form="Surfing",
63
- output_schema_arg="output_format",
64
20
  )
65
21
  )
@@ -10,6 +10,8 @@ MERMAID = "Mermaid"
10
10
  WEB_FETCH = "WebFetch"
11
11
  WEB_SEARCH = "WebSearch"
12
12
  REPORT_BACK = "report_back"
13
+ TASK = "Task"
14
+ IMAGE_GEN = "ImageGen"
13
15
 
14
16
  # SubAgentType is just a string alias now; agent types are defined via SubAgentProfile
15
17
  SubAgentType = str