klaude-code 1.2.19__py3-none-any.whl → 1.2.20__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 (57) hide show
  1. klaude_code/cli/runtime.py +5 -0
  2. klaude_code/command/__init__.py +1 -3
  3. klaude_code/command/clear_cmd.py +5 -4
  4. klaude_code/command/command_abc.py +5 -40
  5. klaude_code/command/debug_cmd.py +2 -2
  6. klaude_code/command/diff_cmd.py +2 -1
  7. klaude_code/command/export_cmd.py +14 -49
  8. klaude_code/command/export_online_cmd.py +2 -1
  9. klaude_code/command/help_cmd.py +2 -1
  10. klaude_code/command/model_cmd.py +7 -5
  11. klaude_code/command/prompt-jj-workspace.md +18 -0
  12. klaude_code/command/prompt_command.py +16 -9
  13. klaude_code/command/refresh_cmd.py +3 -2
  14. klaude_code/command/registry.py +31 -6
  15. klaude_code/command/release_notes_cmd.py +2 -1
  16. klaude_code/command/status_cmd.py +2 -1
  17. klaude_code/command/terminal_setup_cmd.py +2 -1
  18. klaude_code/command/thinking_cmd.py +2 -1
  19. klaude_code/core/executor.py +177 -190
  20. klaude_code/core/manager/sub_agent_manager.py +3 -0
  21. klaude_code/core/prompt.py +4 -1
  22. klaude_code/core/prompts/prompt-sub-agent-web.md +3 -3
  23. klaude_code/core/reminders.py +70 -26
  24. klaude_code/core/task.py +4 -5
  25. klaude_code/core/tool/__init__.py +2 -0
  26. klaude_code/core/tool/file/apply_patch_tool.py +3 -1
  27. klaude_code/core/tool/file/edit_tool.py +7 -5
  28. klaude_code/core/tool/file/multi_edit_tool.py +7 -5
  29. klaude_code/core/tool/file/read_tool.py +5 -2
  30. klaude_code/core/tool/file/write_tool.py +8 -6
  31. klaude_code/core/tool/shell/bash_tool.py +89 -17
  32. klaude_code/core/tool/sub_agent_tool.py +5 -1
  33. klaude_code/core/tool/tool_abc.py +18 -0
  34. klaude_code/core/tool/tool_context.py +6 -6
  35. klaude_code/core/tool/tool_runner.py +7 -7
  36. klaude_code/core/tool/web/web_fetch_tool.py +77 -22
  37. klaude_code/core/tool/web/web_search_tool.py +5 -1
  38. klaude_code/protocol/model.py +8 -1
  39. klaude_code/protocol/op.py +47 -0
  40. klaude_code/protocol/op_handler.py +25 -1
  41. klaude_code/protocol/sub_agent/web.py +1 -1
  42. klaude_code/session/codec.py +71 -0
  43. klaude_code/session/export.py +21 -11
  44. klaude_code/session/session.py +177 -333
  45. klaude_code/session/store.py +215 -0
  46. klaude_code/session/templates/export_session.html +13 -14
  47. klaude_code/ui/modes/repl/completers.py +1 -2
  48. klaude_code/ui/modes/repl/event_handler.py +7 -23
  49. klaude_code/ui/modes/repl/input_prompt_toolkit.py +4 -6
  50. klaude_code/ui/rich/__init__.py +10 -1
  51. klaude_code/ui/rich/cjk_wrap.py +228 -0
  52. klaude_code/ui/rich/status.py +0 -1
  53. {klaude_code-1.2.19.dist-info → klaude_code-1.2.20.dist-info}/METADATA +2 -1
  54. {klaude_code-1.2.19.dist-info → klaude_code-1.2.20.dist-info}/RECORD +56 -53
  55. klaude_code/ui/utils/debouncer.py +0 -42
  56. {klaude_code-1.2.19.dist-info → klaude_code-1.2.20.dist-info}/WHEEL +0 -0
  57. {klaude_code-1.2.19.dist-info → klaude_code-1.2.20.dist-info}/entry_points.txt +0 -0
@@ -17,6 +17,7 @@ from klaude_code.core.executor import Executor
17
17
  from klaude_code.core.manager import build_llm_clients
18
18
  from klaude_code.protocol import events, op
19
19
  from klaude_code.protocol.model import UserInputPayload
20
+ from klaude_code.session.session import close_default_store
20
21
  from klaude_code.trace import DebugType, log, set_debug_logging
21
22
  from klaude_code.ui.modes.repl import build_repl_status_snapshot
22
23
  from klaude_code.ui.modes.repl.input_prompt_toolkit import REPLStatusSnapshot
@@ -184,6 +185,10 @@ async def cleanup_app_components(components: AppComponents) -> None:
184
185
  # Clean shutdown
185
186
  await components.executor.stop()
186
187
  components.executor_task.cancel()
188
+ with contextlib.suppress(asyncio.CancelledError):
189
+ await components.executor_task
190
+ with contextlib.suppress(Exception):
191
+ await close_default_store()
187
192
 
188
193
  # Signal UI to stop
189
194
  await components.event_queue.put(events.EndEvent())
@@ -1,4 +1,4 @@
1
- from .command_abc import CommandABC, CommandResult, InputAction, InputActionType
1
+ from .command_abc import CommandABC, CommandResult
2
2
  from .registry import (
3
3
  dispatch_command,
4
4
  get_commands,
@@ -89,8 +89,6 @@ __all__ = [
89
89
  # "StatusCommand", "TerminalSetupCommand",
90
90
  "CommandABC",
91
91
  "CommandResult",
92
- "InputAction",
93
- "InputActionType",
94
92
  "dispatch_command",
95
93
  "ensure_commands_loaded",
96
94
  "get_commands",
@@ -1,5 +1,5 @@
1
- from klaude_code.command.command_abc import Agent, CommandABC, CommandResult, InputAction
2
- from klaude_code.protocol import commands
1
+ from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
2
+ from klaude_code.protocol import commands, model, op
3
3
 
4
4
 
5
5
  class ClearCommand(CommandABC):
@@ -13,5 +13,6 @@ class ClearCommand(CommandABC):
13
13
  def summary(self) -> str:
14
14
  return "Clear conversation history and free up context"
15
15
 
16
- async def run(self, raw: str, agent: Agent) -> CommandResult:
17
- return CommandResult(actions=[InputAction.clear()])
16
+ async def run(self, agent: Agent, user_input: model.UserInputPayload) -> CommandResult:
17
+ del user_input # unused
18
+ return CommandResult(operations=[op.ClearSessionOperation(session_id=agent.session.id)])
@@ -1,11 +1,10 @@
1
1
  from abc import ABC, abstractmethod
2
- from enum import Enum
3
2
  from typing import Protocol
4
3
 
5
4
  from pydantic import BaseModel
6
5
 
7
6
  from klaude_code.llm import LLMClientABC
8
- from klaude_code.protocol import commands, llm_param
7
+ from klaude_code.protocol import commands, llm_param, model, op
9
8
  from klaude_code.protocol import events as protocol_events
10
9
  from klaude_code.session.session import Session
11
10
 
@@ -34,40 +33,6 @@ class Agent(Protocol):
34
33
  def get_llm_client(self) -> LLMClientABC: ...
35
34
 
36
35
 
37
- class InputActionType(str, Enum):
38
- """Supported input action kinds."""
39
-
40
- RUN_AGENT = "run_agent"
41
- CHANGE_MODEL = "change_model"
42
- CLEAR = "clear"
43
-
44
-
45
- class InputAction(BaseModel):
46
- """Structured executor action derived from a user input."""
47
-
48
- type: InputActionType
49
- text: str = ""
50
- model_name: str | None = None
51
-
52
- @classmethod
53
- def run_agent(cls, text: str) -> "InputAction":
54
- """Create a RunAgent action preserving the provided text."""
55
-
56
- return cls(type=InputActionType.RUN_AGENT, text=text)
57
-
58
- @classmethod
59
- def change_model(cls, model_name: str) -> "InputAction":
60
- """Create a ChangeModel action for the provided model name."""
61
-
62
- return cls(type=InputActionType.CHANGE_MODEL, model_name=model_name)
63
-
64
- @classmethod
65
- def clear(cls) -> "InputAction":
66
- """Create a Clear action to reset the session."""
67
-
68
- return cls(type=InputActionType.CLEAR)
69
-
70
-
71
36
  class CommandResult(BaseModel):
72
37
  """Result of a command execution."""
73
38
 
@@ -75,7 +40,7 @@ class CommandResult(BaseModel):
75
40
  list[protocol_events.DeveloperMessageEvent | protocol_events.WelcomeEvent | protocol_events.ReplayHistoryEvent]
76
41
  | None
77
42
  ) = None # List of UI events to display immediately
78
- actions: list[InputAction] | None = None
43
+ operations: list[op.Operation] | None = None
79
44
 
80
45
 
81
46
  class CommandABC(ABC):
@@ -109,13 +74,13 @@ class CommandABC(ABC):
109
74
  return "additional instructions"
110
75
 
111
76
  @abstractmethod
112
- async def run(self, raw: str, agent: Agent) -> CommandResult:
77
+ async def run(self, agent: Agent, user_input: model.UserInputPayload) -> CommandResult:
113
78
  """
114
79
  Execute the command.
115
80
 
116
81
  Args:
117
- raw: The full command string as typed by user (e.g., "/help" or "/model gpt-4")
118
- session_id: Current session ID, may be None if no session initialized yet
82
+ agent: The agent instance
83
+ user_input: User input with text containing command arguments (without command name)
119
84
 
120
85
  Returns:
121
86
  CommandResult: Result of the command execution
@@ -45,8 +45,8 @@ class DebugCommand(CommandABC):
45
45
  def placeholder(self) -> str:
46
46
  return "filter types"
47
47
 
48
- async def run(self, raw: str, agent: Agent) -> CommandResult:
49
- raw = raw.strip()
48
+ async def run(self, agent: Agent, user_input: model.UserInputPayload) -> CommandResult:
49
+ raw = user_input.text.strip()
50
50
 
51
51
  # /debug (no args) - enable debug
52
52
  if not raw:
@@ -16,7 +16,8 @@ class DiffCommand(CommandABC):
16
16
  def summary(self) -> str:
17
17
  return "Show git diff"
18
18
 
19
- async def run(self, raw: str, agent: Agent) -> CommandResult:
19
+ async def run(self, agent: Agent, user_input: model.UserInputPayload) -> CommandResult:
20
+ del user_input # unused
20
21
  try:
21
22
  # Check if current directory is in a git repository
22
23
  git_check = subprocess.run(
@@ -1,11 +1,9 @@
1
1
  from __future__ import annotations
2
2
 
3
- import subprocess
4
3
  from pathlib import Path
5
4
 
6
5
  from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
7
- from klaude_code.protocol import commands, events, model
8
- from klaude_code.session.export import build_export_html, get_default_export_path
6
+ from klaude_code.protocol import commands, model, op
9
7
 
10
8
 
11
9
  class ExportCommand(CommandABC):
@@ -31,34 +29,18 @@ class ExportCommand(CommandABC):
31
29
  def is_interactive(self) -> bool:
32
30
  return False
33
31
 
34
- async def run(self, raw: str, agent: Agent) -> CommandResult:
35
- try:
36
- output_path = self._resolve_output_path(raw, agent)
37
- html_doc = self._build_html(agent)
38
- output_path.parent.mkdir(parents=True, exist_ok=True)
39
- output_path.write_text(html_doc, encoding="utf-8")
40
- self._open_file(output_path)
41
- event = events.DeveloperMessageEvent(
42
- session_id=agent.session.id,
43
- item=model.DeveloperMessageItem(
44
- content=f"Session exported and opened: {output_path}",
45
- command_output=model.CommandOutput(command_name=self.name),
46
- ),
47
- )
48
- return CommandResult(events=[event])
49
- except Exception as exc: # pragma: no cover - safeguard for unexpected errors
50
- import traceback
51
-
52
- event = events.DeveloperMessageEvent(
53
- session_id=agent.session.id,
54
- item=model.DeveloperMessageItem(
55
- content=f"Failed to export session: {exc}\n{traceback.format_exc()}",
56
- command_output=model.CommandOutput(command_name=self.name, is_error=True),
57
- ),
58
- )
59
- return CommandResult(events=[event])
60
-
61
- def _resolve_output_path(self, raw: str, agent: Agent) -> Path:
32
+ async def run(self, agent: Agent, user_input: model.UserInputPayload) -> CommandResult:
33
+ output_path = self._normalize_output_path(user_input.text, agent)
34
+ return CommandResult(
35
+ operations=[
36
+ op.ExportSessionOperation(
37
+ session_id=agent.session.id,
38
+ output_path=str(output_path) if output_path is not None else None,
39
+ )
40
+ ]
41
+ )
42
+
43
+ def _normalize_output_path(self, raw: str, agent: Agent) -> Path | None:
62
44
  trimmed = raw.strip()
63
45
  if trimmed:
64
46
  candidate = Path(trimmed).expanduser()
@@ -67,21 +49,4 @@ class ExportCommand(CommandABC):
67
49
  if candidate.suffix.lower() != ".html":
68
50
  candidate = candidate.with_suffix(".html")
69
51
  return candidate
70
- return get_default_export_path(agent.session)
71
-
72
- def _open_file(self, path: Path) -> None:
73
- try:
74
- subprocess.run(["open", str(path)], check=True)
75
- except FileNotFoundError as exc: # pragma: no cover - depends on platform
76
- msg = "`open` command not found; please open the HTML manually."
77
- raise RuntimeError(msg) from exc
78
- except subprocess.CalledProcessError as exc: # pragma: no cover - depends on platform
79
- msg = f"Failed to open HTML with `open`: {exc}"
80
- raise RuntimeError(msg) from exc
81
-
82
- def _build_html(self, agent: Agent) -> str:
83
- profile = agent.profile
84
- system_prompt = (profile.system_prompt if profile else "") or ""
85
- tools = profile.tools if profile else []
86
- model_name = profile.llm_client.model_name if profile else "unknown"
87
- return build_export_html(agent.session, system_prompt, tools, model_name)
52
+ return None
@@ -33,7 +33,8 @@ class ExportOnlineCommand(CommandABC):
33
33
  def is_interactive(self) -> bool:
34
34
  return False
35
35
 
36
- async def run(self, raw: str, agent: Agent) -> CommandResult:
36
+ async def run(self, agent: Agent, user_input: model.UserInputPayload) -> CommandResult:
37
+ del user_input # unused
37
38
  # Check if npx or surge is available
38
39
  surge_cmd = self._get_surge_command()
39
40
  if not surge_cmd:
@@ -13,7 +13,8 @@ class HelpCommand(CommandABC):
13
13
  def summary(self) -> str:
14
14
  return "Show help and available commands"
15
15
 
16
- async def run(self, raw: str, agent: Agent) -> CommandResult:
16
+ async def run(self, agent: Agent, user_input: model.UserInputPayload) -> CommandResult:
17
+ del user_input # unused
17
18
  lines: list[str] = [
18
19
  """
19
20
  Usage:
@@ -1,8 +1,8 @@
1
1
  import asyncio
2
2
 
3
- from klaude_code.command.command_abc import Agent, CommandABC, CommandResult, InputAction
3
+ from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
4
4
  from klaude_code.config.select_model import select_model_from_config
5
- from klaude_code.protocol import commands, events, model
5
+ from klaude_code.protocol import commands, events, model, op
6
6
 
7
7
 
8
8
  class ModelCommand(CommandABC):
@@ -28,8 +28,8 @@ class ModelCommand(CommandABC):
28
28
  def placeholder(self) -> str:
29
29
  return "model name"
30
30
 
31
- async def run(self, raw: str, agent: Agent) -> CommandResult:
32
- selected_model = await asyncio.to_thread(select_model_from_config, preferred=raw)
31
+ async def run(self, agent: Agent, user_input: model.UserInputPayload) -> CommandResult:
32
+ selected_model = await asyncio.to_thread(select_model_from_config, preferred=user_input.text)
33
33
 
34
34
  current_model = agent.profile.llm_client.model_name if agent.profile else None
35
35
  if selected_model is None or selected_model == current_model:
@@ -45,4 +45,6 @@ class ModelCommand(CommandABC):
45
45
  ]
46
46
  )
47
47
 
48
- return CommandResult(actions=[InputAction.change_model(selected_model)])
48
+ return CommandResult(
49
+ operations=[op.ChangeModelOperation(session_id=agent.session.id, model_name=selected_model)]
50
+ )
@@ -0,0 +1,18 @@
1
+ ---
2
+ description: Create a jj workspace before starting work to enable parallel Clauding
3
+ ---
4
+ <task>
5
+ $ARGUMENTS
6
+ </task>
7
+ <system>
8
+ You are now in jj-workspace mode. Before working on any task, you MUST first create a dedicated jj workspace. This allows multiple Claude sessions to work in parallel without conflicts.
9
+
10
+ If the <task> above is empty, inform the user that you are ready to work in jj-workspace mode and waiting for a task description. Once provided, follow the steps below.
11
+
12
+ When a task is provided, follow these steps:
13
+ 1. Generate a short, descriptive workspace name based on the task (e.g., `workspace-add-login` or `workspace-fix-typo`)
14
+ 2. Run `jj workspace add <workspace-name>` to create the workspace
15
+ 3. Change into the workspace directory: `cd <workspace-name>`
16
+ 4. Describe the change: `jj describe -m '<brief task description>'`
17
+ 5. Continue all subsequent work within this workspace directory
18
+ </system>
@@ -2,8 +2,8 @@ from importlib.resources import files
2
2
 
3
3
  import yaml
4
4
 
5
- from klaude_code.command.command_abc import Agent, CommandABC, CommandResult, InputAction
6
- from klaude_code.protocol import commands
5
+ from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
6
+ from klaude_code.protocol import commands, model, op
7
7
  from klaude_code.trace import log_debug
8
8
 
9
9
 
@@ -55,16 +55,23 @@ class PromptCommand(CommandABC):
55
55
  def support_addition_params(self) -> bool:
56
56
  return True
57
57
 
58
- async def run(self, raw: str, agent: Agent) -> CommandResult:
58
+ async def run(self, agent: Agent, user_input: model.UserInputPayload) -> CommandResult:
59
59
  self._ensure_loaded()
60
60
  template_content = self._content or ""
61
- user_input = raw.strip() or "<none>"
61
+ user_input_text = user_input.text.strip() or "<none>"
62
62
 
63
63
  if "$ARGUMENTS" in template_content:
64
- final_prompt = template_content.replace("$ARGUMENTS", user_input)
64
+ final_prompt = template_content.replace("$ARGUMENTS", user_input_text)
65
65
  else:
66
66
  final_prompt = template_content
67
- if user_input:
68
- final_prompt += f"\n\nAdditional Instructions:\n{user_input}"
69
-
70
- return CommandResult(actions=[InputAction.run_agent(final_prompt)])
67
+ if user_input_text:
68
+ final_prompt += f"\n\nAdditional Instructions:\n{user_input_text}"
69
+
70
+ return CommandResult(
71
+ operations=[
72
+ op.RunAgentOperation(
73
+ session_id=agent.session.id,
74
+ input=model.UserInputPayload(text=final_prompt, images=user_input.images),
75
+ )
76
+ ]
77
+ )
@@ -1,5 +1,5 @@
1
1
  from klaude_code.command.command_abc import Agent, CommandABC, CommandResult
2
- from klaude_code.protocol import commands, events
2
+ from klaude_code.protocol import commands, events, model
3
3
 
4
4
 
5
5
  class RefreshTerminalCommand(CommandABC):
@@ -17,7 +17,8 @@ class RefreshTerminalCommand(CommandABC):
17
17
  def is_interactive(self) -> bool:
18
18
  return True
19
19
 
20
- async def run(self, raw: str, agent: Agent) -> CommandResult:
20
+ async def run(self, agent: Agent, user_input: model.UserInputPayload) -> CommandResult:
21
+ del user_input # unused
21
22
  import os
22
23
 
23
24
  os.system("cls" if os.name == "nt" else "clear")
@@ -1,9 +1,9 @@
1
1
  from importlib.resources import files
2
2
  from typing import TYPE_CHECKING
3
3
 
4
- from klaude_code.command.command_abc import Agent, CommandResult, InputAction
4
+ from klaude_code.command.command_abc import Agent, CommandResult
5
5
  from klaude_code.command.prompt_command import PromptCommand
6
- from klaude_code.protocol import commands, events, model
6
+ from klaude_code.protocol import commands, events, model, op
7
7
  from klaude_code.trace import log_debug
8
8
 
9
9
  if TYPE_CHECKING:
@@ -110,11 +110,20 @@ def is_slash_command_name(name: str) -> bool:
110
110
  return _resolve_command_key(name) is not None
111
111
 
112
112
 
113
- async def dispatch_command(raw: str, agent: Agent) -> CommandResult:
113
+ async def dispatch_command(user_input: model.UserInputPayload, agent: Agent, *, submission_id: str) -> CommandResult:
114
114
  _ensure_commands_loaded()
115
115
  # Detect command name
116
+ raw = user_input.text
116
117
  if not raw.startswith("/"):
117
- return CommandResult(actions=[InputAction.run_agent(raw)])
118
+ return CommandResult(
119
+ operations=[
120
+ op.RunAgentOperation(
121
+ id=submission_id,
122
+ session_id=agent.session.id,
123
+ input=user_input,
124
+ )
125
+ ]
126
+ )
118
127
 
119
128
  splits = raw.split(" ", maxsplit=1)
120
129
  command_name_raw = splits[0][1:]
@@ -122,13 +131,29 @@ async def dispatch_command(raw: str, agent: Agent) -> CommandResult:
122
131
 
123
132
  command_key = _resolve_command_key(command_name_raw)
124
133
  if command_key is None:
125
- return CommandResult(actions=[InputAction.run_agent(raw)])
134
+ return CommandResult(
135
+ operations=[
136
+ op.RunAgentOperation(
137
+ id=submission_id,
138
+ session_id=agent.session.id,
139
+ input=user_input,
140
+ )
141
+ ]
142
+ )
126
143
 
127
144
  command = _COMMANDS[command_key]
128
145
  command_identifier: commands.CommandName | str = command.name
129
146
 
130
147
  try:
131
- return await command.run(rest, agent)
148
+ user_input_for_command = model.UserInputPayload(text=rest, images=user_input.images)
149
+ result = await command.run(agent, user_input_for_command)
150
+ ops = list(result.operations or [])
151
+ for operation in ops:
152
+ if isinstance(operation, op.RunAgentOperation):
153
+ operation.id = submission_id
154
+ if ops:
155
+ result.operations = ops
156
+ return result
132
157
  except Exception as e:
133
158
  command_output = (
134
159
  model.CommandOutput(command_name=command_identifier, is_error=True)
@@ -68,7 +68,8 @@ class ReleaseNotesCommand(CommandABC):
68
68
  def summary(self) -> str:
69
69
  return "Show the latest release notes"
70
70
 
71
- async def run(self, raw: str, agent: Agent) -> CommandResult:
71
+ async def run(self, agent: Agent, user_input: model.UserInputPayload) -> CommandResult:
72
+ del user_input # unused
72
73
  changelog = _read_changelog()
73
74
  content = _extract_releases(changelog, count=10)
74
75
 
@@ -132,7 +132,8 @@ class StatusCommand(CommandABC):
132
132
  def summary(self) -> str:
133
133
  return "Show session usage statistics"
134
134
 
135
- async def run(self, raw: str, agent: Agent) -> CommandResult:
135
+ async def run(self, agent: Agent, user_input: model.UserInputPayload) -> CommandResult:
136
+ del user_input # unused
136
137
  session = agent.session
137
138
  aggregated = accumulate_session_usage(session)
138
139
 
@@ -21,7 +21,8 @@ class TerminalSetupCommand(CommandABC):
21
21
  def is_interactive(self) -> bool:
22
22
  return False
23
23
 
24
- async def run(self, raw: str, agent: Agent) -> CommandResult:
24
+ async def run(self, agent: Agent, user_input: model.UserInputPayload) -> CommandResult:
25
+ del user_input # unused
25
26
  term_program = os.environ.get("TERM_PROGRAM", "").lower()
26
27
 
27
28
  try:
@@ -169,7 +169,8 @@ class ThinkingCommand(CommandABC):
169
169
  def is_interactive(self) -> bool:
170
170
  return True
171
171
 
172
- async def run(self, raw: str, agent: Agent) -> CommandResult:
172
+ async def run(self, agent: Agent, user_input: model.UserInputPayload) -> CommandResult:
173
+ del user_input # unused
173
174
  if not agent.profile:
174
175
  return self._no_change_result(agent, "No profile configured")
175
176