soothe-cli 0.3.4__tar.gz → 0.3.5__tar.gz
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.
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/PKG-INFO +3 -3
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/pyproject.toml +2 -2
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/cli/commands/config_cmd.py +6 -1
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/cli/stream/display_line.py +0 -6
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/cli/stream/formatter.py +0 -7
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/shared/message_processing.py +65 -22
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/shared/tool_call_resolution.py +20 -4
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/_version.py +2 -2
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/app.py +419 -18
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/daemon_session.py +57 -47
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/model_config.py +38 -6
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/sessions.py +145 -5
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/textual_adapter.py +27 -14
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/tool_display.py +58 -16
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/widgets/chat_input.py +2 -2
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/widgets/messages.py +2 -1
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/widgets/thread_selector.py +21 -4
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/.gitignore +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/README.md +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/__init__.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/cli/__init__.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/cli/commands/__init__.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/cli/commands/autopilot_cmd.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/cli/commands/run_cmd.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/cli/commands/status_cmd.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/cli/commands/subagent_names.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/cli/commands/thread_cmd.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/cli/execution/__init__.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/cli/execution/daemon.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/cli/execution/headless.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/cli/execution/launcher.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/cli/main.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/cli/renderer.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/cli/stream/__init__.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/cli/stream/context.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/cli/stream/pipeline.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/cli/utils.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/config/__init__.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/config/cli_config.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/plan/__init__.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/plan/rich_tree.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/shared/__init__.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/shared/command_router.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/shared/config_loader.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/shared/display_policy.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/shared/essential_events.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/shared/event_processor.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/shared/presentation_engine.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/shared/processor_state.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/shared/renderer_protocol.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/shared/rendering.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/shared/slash_commands.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/shared/subagent_routing.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/shared/suppression_state.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/shared/tool_card_payload.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/shared/tool_formatters/__init__.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/shared/tool_formatters/base.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/shared/tool_formatters/execution.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/shared/tool_formatters/fallback.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/shared/tool_formatters/file_ops.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/shared/tool_formatters/goal_formatter.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/shared/tool_formatters/media.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/shared/tool_formatters/structured.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/shared/tool_formatters/web.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/shared/tool_message_format.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/shared/tool_output_formatter.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/shared/tui_trace_log.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/__init__.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/_ask_user_types.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/_cli_context.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/_env_vars.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/_session_stats.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/app.tcss +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/command_registry.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/config.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/file_ops.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/formatting.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/hooks.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/input.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/media_utils.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/output.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/project_utils.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/skills/__init__.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/skills/invocation.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/skills/load.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/theme.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/unicode_security.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/update_check.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/widgets/__init__.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/widgets/_links.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/widgets/approval.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/widgets/ask_user.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/widgets/autocomplete.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/widgets/autopilot_dashboard.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/widgets/autopilot_screen.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/widgets/clipboard.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/widgets/diff.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/widgets/editor.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/widgets/history.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/widgets/loading.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/widgets/mcp_viewer.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/widgets/message_store.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/widgets/model_selector.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/widgets/notification_settings.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/widgets/status.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/widgets/theme_selector.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/widgets/tool_renderers.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/widgets/tool_widgets.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/widgets/tools.py +0 -0
- {soothe_cli-0.3.4 → soothe_cli-0.3.5}/src/soothe_cli/tui/widgets/welcome.py +0 -0
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: soothe-cli
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.5
|
|
4
4
|
Summary: Soothe CLI client - communicates with daemon via WebSocket
|
|
5
|
-
Project-URL: Homepage, https://github.com/
|
|
5
|
+
Project-URL: Homepage, https://github.com/OpenSoothe/soothe
|
|
6
6
|
Project-URL: Documentation, https://soothe.readthedocs.io
|
|
7
|
-
Project-URL: Repository, https://github.com/
|
|
7
|
+
Project-URL: Repository, https://github.com/OpenSoothe/soothe
|
|
8
8
|
License: MIT
|
|
9
9
|
Keywords: cli,client,soothe,tui,websocket
|
|
10
10
|
Classifier: Development Status :: 3 - Alpha
|
|
@@ -45,9 +45,9 @@ dev = [
|
|
|
45
45
|
soothe = "soothe_cli.cli.main:app"
|
|
46
46
|
|
|
47
47
|
[project.urls]
|
|
48
|
-
Homepage = "https://github.com/
|
|
48
|
+
Homepage = "https://github.com/OpenSoothe/soothe"
|
|
49
49
|
Documentation = "https://soothe.readthedocs.io"
|
|
50
|
-
Repository = "https://github.com/
|
|
50
|
+
Repository = "https://github.com/OpenSoothe/soothe"
|
|
51
51
|
|
|
52
52
|
[tool.hatch.build.targets.wheel]
|
|
53
53
|
packages = ["src/soothe_cli"]
|
|
@@ -189,9 +189,14 @@ def config_init(
|
|
|
189
189
|
target.write_text("# Soothe configuration\n# See docs/user_guide.md for options\n")
|
|
190
190
|
typer.echo(f"Created minimal {target}")
|
|
191
191
|
|
|
192
|
-
for subdir in ("runs", "generated_agents", "logs"):
|
|
192
|
+
for subdir in ("runs", "generated_agents", "logs", "data"):
|
|
193
193
|
(home / subdir).mkdir(parents=True, exist_ok=True)
|
|
194
194
|
|
|
195
|
+
# Migrate runtime data files from root to data/ subdirectory
|
|
196
|
+
from soothe_sdk.client.config import migrate_data_to_subdir
|
|
197
|
+
|
|
198
|
+
migrate_data_to_subdir()
|
|
199
|
+
|
|
195
200
|
typer.echo(f"Soothe home initialized at {home}")
|
|
196
201
|
|
|
197
202
|
|
|
@@ -19,7 +19,6 @@ class DisplayLine:
|
|
|
19
19
|
status: Optional status suffix ("running" for parallel tools).
|
|
20
20
|
duration_ms: Optional duration in milliseconds.
|
|
21
21
|
source_prefix: Optional source identifier for debug mode (e.g., "[main]", "[subagent:research]").
|
|
22
|
-
newline_before: Add newline separator before this line for improved readability.
|
|
23
22
|
"""
|
|
24
23
|
|
|
25
24
|
level: int
|
|
@@ -29,7 +28,6 @@ class DisplayLine:
|
|
|
29
28
|
status: str | None = None
|
|
30
29
|
duration_ms: int | None = None
|
|
31
30
|
source_prefix: str | None = None
|
|
32
|
-
newline_before: bool = False
|
|
33
31
|
|
|
34
32
|
def format(self) -> str:
|
|
35
33
|
"""Format the display line as a string.
|
|
@@ -39,10 +37,6 @@ class DisplayLine:
|
|
|
39
37
|
"""
|
|
40
38
|
parts = []
|
|
41
39
|
|
|
42
|
-
# Add newline separator before if requested (for improved readability)
|
|
43
|
-
if self.newline_before:
|
|
44
|
-
parts.append("\n")
|
|
45
|
-
|
|
46
40
|
# Add source prefix first if present (debug mode)
|
|
47
41
|
if self.source_prefix:
|
|
48
42
|
parts.append(self.source_prefix)
|
|
@@ -102,7 +102,6 @@ def format_goal_header(
|
|
|
102
102
|
icon="●",
|
|
103
103
|
indent=indent_for_level(1),
|
|
104
104
|
source_prefix=_derive_source_prefix(namespace, verbosity_tier),
|
|
105
|
-
newline_before=True, # Add separator for goal start
|
|
106
105
|
)
|
|
107
106
|
|
|
108
107
|
|
|
@@ -133,7 +132,6 @@ def format_step_header(
|
|
|
133
132
|
icon="○", # Hollow circle for in-progress step
|
|
134
133
|
indent=indent_for_level(2),
|
|
135
134
|
source_prefix=_derive_source_prefix(namespace, verbosity_tier),
|
|
136
|
-
newline_before=True, # Add separator for step start
|
|
137
135
|
)
|
|
138
136
|
|
|
139
137
|
|
|
@@ -226,7 +224,6 @@ def format_subagent_milestone(
|
|
|
226
224
|
icon="✓",
|
|
227
225
|
indent=indent_for_level(3),
|
|
228
226
|
source_prefix=_derive_source_prefix(namespace, verbosity_tier),
|
|
229
|
-
newline_before=True, # Add separator before subagent milestone
|
|
230
227
|
)
|
|
231
228
|
|
|
232
229
|
|
|
@@ -276,7 +273,6 @@ def format_plan_phase_reasoning(
|
|
|
276
273
|
icon="•",
|
|
277
274
|
indent=indent_for_level(3),
|
|
278
275
|
source_prefix=_derive_source_prefix(namespace, verbosity_tier),
|
|
279
|
-
newline_before=True,
|
|
280
276
|
)
|
|
281
277
|
|
|
282
278
|
|
|
@@ -307,7 +303,6 @@ def format_reasoning(
|
|
|
307
303
|
icon="•",
|
|
308
304
|
indent=indent_for_level(3),
|
|
309
305
|
source_prefix=_derive_source_prefix(namespace, verbosity_tier),
|
|
310
|
-
newline_before=True, # Add separator for reasoning display
|
|
311
306
|
)
|
|
312
307
|
|
|
313
308
|
|
|
@@ -349,7 +344,6 @@ def format_judgement(
|
|
|
349
344
|
icon=action_icon,
|
|
350
345
|
indent=indent_for_level(2),
|
|
351
346
|
source_prefix=_derive_source_prefix(namespace, verbosity_tier),
|
|
352
|
-
newline_before=True, # Add separator before judgement indicator
|
|
353
347
|
)
|
|
354
348
|
|
|
355
349
|
|
|
@@ -452,7 +446,6 @@ def format_goal_done(
|
|
|
452
446
|
indent=indent_for_level(1),
|
|
453
447
|
duration_ms=duration_ms,
|
|
454
448
|
source_prefix=_derive_source_prefix(namespace, verbosity_tier),
|
|
455
|
-
newline_before=True, # Add separator before final report
|
|
456
449
|
)
|
|
457
450
|
|
|
458
451
|
|
|
@@ -49,22 +49,33 @@ def accumulate_tool_call_chunks(
|
|
|
49
49
|
if tc_name and tc_id and tc_id not in pending_tool_calls:
|
|
50
50
|
if isinstance(tc_args, str):
|
|
51
51
|
args_str = tc_args
|
|
52
|
+
is_complete = False # String may be partial JSON
|
|
52
53
|
elif isinstance(tc_args, dict) and tc_args:
|
|
53
54
|
args_str = json.dumps(tc_args)
|
|
55
|
+
is_complete = True # Dict yields complete JSON
|
|
54
56
|
else:
|
|
55
57
|
args_str = ""
|
|
58
|
+
is_complete = False # Empty or missing args
|
|
56
59
|
pending_tool_calls[tc_id] = {
|
|
57
60
|
"name": tc_name,
|
|
58
61
|
"args_str": args_str,
|
|
62
|
+
"is_complete_json": is_complete,
|
|
59
63
|
"emitted": False,
|
|
60
64
|
"is_main": is_main,
|
|
61
65
|
}
|
|
62
|
-
# Some providers send final args as a dict on a later chunk
|
|
66
|
+
# Some providers send final args as a dict on a later chunk (replace previous)
|
|
63
67
|
elif tc_id and tc_id in pending_tool_calls and isinstance(tc_args, dict) and tc_args:
|
|
64
68
|
pending_tool_calls[tc_id]["args_str"] = json.dumps(tc_args)
|
|
69
|
+
pending_tool_calls[tc_id]["is_complete_json"] = True
|
|
65
70
|
# Subsequent chunks: accumulate partial JSON strings for this tool call id
|
|
66
71
|
elif tc_id and tc_id in pending_tool_calls and isinstance(tc_args, str) and tc_args:
|
|
67
|
-
|
|
72
|
+
# If args_str already contains complete JSON, provider refined args → restart
|
|
73
|
+
if pending_tool_calls[tc_id].get("is_complete_json"):
|
|
74
|
+
pending_tool_calls[tc_id]["args_str"] = tc_args
|
|
75
|
+
pending_tool_calls[tc_id]["is_complete_json"] = False
|
|
76
|
+
else:
|
|
77
|
+
# Normal partial accumulation
|
|
78
|
+
pending_tool_calls[tc_id]["args_str"] += tc_args
|
|
68
79
|
elif tc_args and isinstance(tc_args, str) and tc_args:
|
|
69
80
|
# Legacy: chunks missing ``id`` — attach to the first non-emitted pending call
|
|
70
81
|
for pending in pending_tool_calls.values():
|
|
@@ -279,7 +290,7 @@ def tool_calls_have_any_arg_dict(tc_list: list[Any]) -> bool:
|
|
|
279
290
|
# Maps tool name to list of argument keys to display (supports multiple args)
|
|
280
291
|
_ARG_DISPLAY_MAP: dict[str, list[str]] = {
|
|
281
292
|
# File operations — deepagents uses ``file_path`` for read/write/edit (see IG-053)
|
|
282
|
-
"read_file": ["file_path", "path", "path_name"],
|
|
293
|
+
"read_file": ["file_path", "path", "path_name", "target_file", "filename", "relative_path"],
|
|
283
294
|
"write_file": ["file_path", "path"],
|
|
284
295
|
"delete_file": ["file_path", "path"],
|
|
285
296
|
"file_info": ["path", "file_path"],
|
|
@@ -312,6 +323,8 @@ _ARG_DISPLAY_MAP: dict[str, list[str]] = {
|
|
|
312
323
|
"create_goal": ["description"],
|
|
313
324
|
"complete_goal": ["goal_id"],
|
|
314
325
|
"fail_goal": ["goal_id"],
|
|
326
|
+
# Subagent delegation (deepagents ``task`` tool)
|
|
327
|
+
"task": ["subagent_type", "description", "prompt"],
|
|
315
328
|
}
|
|
316
329
|
|
|
317
330
|
|
|
@@ -323,6 +336,43 @@ def _normalize_tool_name_for_arg_map(tool_name: str) -> str:
|
|
|
323
336
|
return re.sub(r"(?<!^)(?=[A-Z])", "_", tool_name).lower()
|
|
324
337
|
|
|
325
338
|
|
|
339
|
+
_ARG_SUMMARY_SKIP_KEYS: frozenset[str] = frozenset({"_raw", "_internal", "raw_args_str"})
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
def _compact_tool_args_display_values(
|
|
343
|
+
args: dict[str, Any],
|
|
344
|
+
*,
|
|
345
|
+
max_value_length: int = 40,
|
|
346
|
+
max_items: int = 3,
|
|
347
|
+
) -> str:
|
|
348
|
+
"""Build comma-separated display values from the first tool parameters (values only)."""
|
|
349
|
+
from soothe_sdk.utils import convert_and_abbreviate_path
|
|
350
|
+
from soothe_sdk.utils.parsing import is_path_argument as _path_arg_name_pattern
|
|
351
|
+
|
|
352
|
+
def _is_path_arg_name(key: str) -> bool:
|
|
353
|
+
return _path_arg_name_pattern.match(key) is not None
|
|
354
|
+
|
|
355
|
+
def _display_path_value(raw: str) -> str:
|
|
356
|
+
out = convert_and_abbreviate_path(raw)
|
|
357
|
+
if len(out) > max_value_length:
|
|
358
|
+
return out[: max_value_length - 3] + "..."
|
|
359
|
+
return out
|
|
360
|
+
|
|
361
|
+
parts: list[str] = []
|
|
362
|
+
for k, v in args.items():
|
|
363
|
+
if len(parts) >= max_items:
|
|
364
|
+
break
|
|
365
|
+
if k in _ARG_SUMMARY_SKIP_KEYS:
|
|
366
|
+
continue
|
|
367
|
+
s = str(v)
|
|
368
|
+
if _is_path_arg_name(k):
|
|
369
|
+
s = _display_path_value(s)
|
|
370
|
+
elif len(s) > max_value_length:
|
|
371
|
+
s = s[: max_value_length - 3] + "..."
|
|
372
|
+
parts.append(s)
|
|
373
|
+
return ", ".join(parts)
|
|
374
|
+
|
|
375
|
+
|
|
326
376
|
def format_tool_call_args(tool_name: str, tool_call: dict[str, Any]) -> str:
|
|
327
377
|
"""Format key tool arguments for display (see IG-053).
|
|
328
378
|
|
|
@@ -340,8 +390,10 @@ def format_tool_call_args(tool_name: str, tool_call: dict[str, Any]) -> str:
|
|
|
340
390
|
Returns:
|
|
341
391
|
Formatted argument string like "file_name.md" or "/Users/dev/.../file.md, pattern"
|
|
342
392
|
(without outer parentheses - caller adds them).
|
|
343
|
-
Returns "..." when args are empty but tool is known.
|
|
344
|
-
Returns ""
|
|
393
|
+
Returns "..." when args are empty but tool is known (or placeholders while streaming).
|
|
394
|
+
Returns "…" when the tool is not in the display map and there are no usable args,
|
|
395
|
+
or when parsed args exist but only contain internal/skip keys.
|
|
396
|
+
For unmapped tools with parameters, returns a compact comma-separated value summary.
|
|
345
397
|
|
|
346
398
|
Examples:
|
|
347
399
|
>>> format_tool_call_args("read_file", {"args": {"path": "config.yml"}})
|
|
@@ -399,10 +451,13 @@ def format_tool_call_args(tool_name: str, tool_call: dict[str, Any]) -> str:
|
|
|
399
451
|
val = val[: max_value_length - 3] + "..."
|
|
400
452
|
return val
|
|
401
453
|
return "..."
|
|
402
|
-
return ""
|
|
454
|
+
return "…"
|
|
403
455
|
|
|
404
456
|
if not key_args:
|
|
405
|
-
|
|
457
|
+
if args:
|
|
458
|
+
compact = _compact_tool_args_display_values(args, max_value_length=max_value_length)
|
|
459
|
+
return compact if compact else "…"
|
|
460
|
+
return "…"
|
|
406
461
|
|
|
407
462
|
# Extract values for all configured argument keys
|
|
408
463
|
values = []
|
|
@@ -420,21 +475,9 @@ def format_tool_call_args(tool_name: str, tool_call: dict[str, Any]) -> str:
|
|
|
420
475
|
if not values:
|
|
421
476
|
# Model may use different arg names than _ARG_DISPLAY_MAP; still show something useful.
|
|
422
477
|
if args:
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
for k, v in list(args.items())[:3]:
|
|
427
|
-
if k in skip_keys:
|
|
428
|
-
continue
|
|
429
|
-
s = str(v)
|
|
430
|
-
# Convert and abbreviate path arguments
|
|
431
|
-
if _is_path_arg_name(k):
|
|
432
|
-
s = _display_path_value(s)
|
|
433
|
-
elif len(s) > max_value_length:
|
|
434
|
-
s = s[: max_value_length - 3] + "..."
|
|
435
|
-
parts.append(s)
|
|
436
|
-
if parts:
|
|
437
|
-
return ", ".join(parts)
|
|
478
|
+
compact = _compact_tool_args_display_values(args, max_value_length=max_value_length)
|
|
479
|
+
if compact:
|
|
480
|
+
return compact
|
|
438
481
|
# All args were internal keys, check for raw_args_str
|
|
439
482
|
raw = args.get("_raw") or args.get("raw_args_str", "")
|
|
440
483
|
if raw:
|
|
@@ -12,6 +12,7 @@ duplicate precedence rules across merge/backfill helpers.
|
|
|
12
12
|
|
|
13
13
|
from __future__ import annotations
|
|
14
14
|
|
|
15
|
+
import logging
|
|
15
16
|
from collections.abc import Mapping
|
|
16
17
|
from dataclasses import dataclass
|
|
17
18
|
from typing import Any
|
|
@@ -22,6 +23,8 @@ from soothe_cli.shared.message_processing import (
|
|
|
22
23
|
try_parse_pending_tool_call_args,
|
|
23
24
|
)
|
|
24
25
|
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
25
28
|
|
|
26
29
|
def infer_tool_name_from_call_id(tool_call_id: str) -> str | None:
|
|
27
30
|
"""Recover a real tool name from common ``functions.<name>:<idx>`` id shapes.
|
|
@@ -243,7 +246,13 @@ def build_streaming_args_overlay(
|
|
|
243
246
|
message: Any,
|
|
244
247
|
pending_tool_calls_lc: dict[str, dict[str, Any]],
|
|
245
248
|
) -> dict[str, dict[str, Any]]:
|
|
246
|
-
"""Map ``tool_call_id`` → parsed args dict from ``tool_call_chunks`` accumulation.
|
|
249
|
+
"""Map ``tool_call_id`` → parsed args dict from ``tool_call_chunks`` accumulation.
|
|
250
|
+
|
|
251
|
+
Updates the overlay on every chunk whenever JSON parses successfully. A prior
|
|
252
|
+
version stopped after the first parse (``tui_stream_mounted``), which froze the
|
|
253
|
+
overlay when ``args_str`` grew across chunks so the TUI kept ``read_file(…)``
|
|
254
|
+
headers even after the path arrived in the accumulated string.
|
|
255
|
+
"""
|
|
247
256
|
from langchain_core.messages import AIMessageChunk
|
|
248
257
|
|
|
249
258
|
overlay: dict[str, dict[str, Any]] = {}
|
|
@@ -252,8 +261,6 @@ def build_streaming_args_overlay(
|
|
|
252
261
|
)
|
|
253
262
|
|
|
254
263
|
for tc_id, pend in list(pending_tool_calls_lc.items()):
|
|
255
|
-
if pend.get("tui_stream_mounted"):
|
|
256
|
-
continue
|
|
257
264
|
parsed = try_parse_pending_tool_call_args(pend)
|
|
258
265
|
if parsed is None:
|
|
259
266
|
continue
|
|
@@ -262,9 +269,18 @@ def build_streaming_args_overlay(
|
|
|
262
269
|
continue
|
|
263
270
|
if not parsed and not streaming_final:
|
|
264
271
|
continue
|
|
265
|
-
pend["tui_stream_mounted"] = True
|
|
266
272
|
str_id = str(tc_id)
|
|
267
273
|
overlay[str_id] = parsed
|
|
274
|
+
if logger.isEnabledFor(logging.DEBUG):
|
|
275
|
+
args_preview = str(parsed)[:200]
|
|
276
|
+
logger.debug(
|
|
277
|
+
"tool_stream_overlay id=%s name=%s keys=%s streaming_final=%s preview=%s",
|
|
278
|
+
str_id,
|
|
279
|
+
name,
|
|
280
|
+
sorted(parsed.keys()) if isinstance(parsed, dict) else "?",
|
|
281
|
+
streaming_final,
|
|
282
|
+
args_preview,
|
|
283
|
+
)
|
|
268
284
|
return overlay
|
|
269
285
|
|
|
270
286
|
|
|
@@ -8,13 +8,13 @@ except PackageNotFoundError:
|
|
|
8
8
|
# Fallback for development/editable installs
|
|
9
9
|
__version__ = "0.0.0"
|
|
10
10
|
|
|
11
|
-
DOCS_URL = "https://github.com/
|
|
11
|
+
DOCS_URL = "https://github.com/OpenSoothe/soothe/docs"
|
|
12
12
|
"""URL for Soothe documentation."""
|
|
13
13
|
|
|
14
14
|
PYPI_URL = "https://pypi.org/pypi/soothe/json"
|
|
15
15
|
"""PyPI JSON API endpoint for version checks."""
|
|
16
16
|
|
|
17
|
-
CHANGELOG_URL = "https://github.com/
|
|
17
|
+
CHANGELOG_URL = "https://github.com/OpenSoothe/soothe/blob/main/CHANGELOG.md"
|
|
18
18
|
"""URL for the full changelog."""
|
|
19
19
|
|
|
20
20
|
USER_AGENT = f"soothe/{__version__} update-check"
|