soothe-cli 0.6.9__tar.gz → 0.6.10__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.6.9 → soothe_cli-0.6.10}/PKG-INFO +1 -1
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/cli/commands/status_cmd.py +52 -67
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/runtime/parse/tool_call_resolution.py +34 -2
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/runtime/state/step_router.py +48 -9
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/textual_adapter.py +65 -32
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/tool_display.py +17 -3
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/widgets/loading.py +39 -78
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/widgets/messages.py +43 -1
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/.gitignore +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/README.md +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/pyproject.toml +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/__init__.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/cli/__init__.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/cli/commands/__init__.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/cli/commands/autopilot_cmd.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/cli/commands/loop_cmd.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/cli/commands/run_cmd.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/cli/execution/__init__.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/cli/execution/daemon.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/cli/execution/daemon_errors.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/cli/execution/headless.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/cli/execution/headless_renderer.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/cli/execution/launcher.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/cli/main.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/config/__init__.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/config/cli_config.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/config/loader.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/config/logging_setup.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/runtime/__init__.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/runtime/headless/processor.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/runtime/headless/processor_state.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/runtime/parse/_utils.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/runtime/parse/message_processing.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/runtime/parse/tool_message_format.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/runtime/parse/tool_result.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/runtime/policy/display_policy.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/runtime/policy/essential_events.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/runtime/policy/tui_trace_log.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/runtime/presentation/async_renderer_protocol.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/runtime/presentation/duration_format.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/runtime/presentation/engine.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/runtime/presentation/explore_task_display.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/runtime/presentation/renderer_base.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/runtime/presentation/renderer_protocol.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/runtime/state/file_tracker.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/runtime/state/session_stats.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/runtime/state/stream_accumulator.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/runtime/state/transcript.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/runtime/task_scope.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/runtime/transport/session.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/runtime/turn/pipeline.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/runtime/turn/prepare.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/runtime/wire/chunk_filter.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/runtime/wire/display_text.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/runtime/wire/message_text.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/runtime/wire/messages.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/__init__.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/_cli_context.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/_env_vars.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/_version.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/app/__init__.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/app/_app.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/app/_commands.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/app/_execution.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/app/_history.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/app/_messages_mixin.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/app/_model.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/app/_module_init.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/app/_startup.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/app/_ui.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/app/app.tcss +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/binding.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/command_registry.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/commands/__init__.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/commands/command_router.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/commands/slash_commands.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/commands/subagent_routing.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/config.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/file_change_notify.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/file_change_renderers.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/hooks.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/input.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/media_utils.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/model_config.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/path_utils.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/preview_limits.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/project_utils.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/sessions.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/skills/__init__.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/skills/invocation.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/skills/load.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/theme.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/tips.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/unicode_security.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/update_check.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/widgets/__init__.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/widgets/_links.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/widgets/autocomplete.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/widgets/autopilot_dashboard.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/widgets/autopilot_screen.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/widgets/chat_input.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/widgets/clipboard.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/widgets/diff.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/widgets/editor.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/widgets/file_change_preview.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/widgets/history.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/widgets/loop_selector.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/widgets/mcp_viewer.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/widgets/message_store.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/widgets/model_selector.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/widgets/notification_settings.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/widgets/status.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/widgets/theme_selector.py +0 -0
- {soothe_cli-0.6.9 → soothe_cli-0.6.10}/src/soothe_cli/tui/widgets/welcome.py +0 -0
|
@@ -74,39 +74,41 @@ async def _fetch_ready_state(ws_url: str, timeout: float = 5.0) -> dict[str, Any
|
|
|
74
74
|
return None
|
|
75
75
|
|
|
76
76
|
|
|
77
|
-
def
|
|
78
|
-
|
|
79
|
-
table = Table(title="Connection Settings")
|
|
80
|
-
table.add_column("Setting", style="cyan")
|
|
81
|
-
table.add_column("Value", style="green")
|
|
82
|
-
|
|
83
|
-
table.add_row("WebSocket URL", ws_url)
|
|
84
|
-
table.add_row("Daemon Host", config.daemon_host)
|
|
85
|
-
table.add_row("Daemon Port", str(config.daemon_port))
|
|
86
|
-
table.add_row("Soothe Home", str(config.soothe_home))
|
|
87
|
-
|
|
88
|
-
return table
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
def _render_daemon_table(
|
|
77
|
+
def _render_unified_status_table(
|
|
78
|
+
config: Any,
|
|
92
79
|
ws_url: str,
|
|
93
|
-
running: bool,
|
|
94
|
-
port_live: bool,
|
|
95
|
-
active_threads: int,
|
|
96
|
-
daemon_pid: int | None,
|
|
80
|
+
running: bool | None = None,
|
|
81
|
+
port_live: bool | None = None,
|
|
82
|
+
active_threads: int | None = None,
|
|
83
|
+
daemon_pid: int | None = None,
|
|
97
84
|
ready_state: dict[str, Any] | None = None,
|
|
85
|
+
daemon_live: bool = True,
|
|
98
86
|
) -> Table:
|
|
99
|
-
"""Render
|
|
100
|
-
|
|
87
|
+
"""Render unified status table without duplicated info.
|
|
88
|
+
|
|
89
|
+
Sections:
|
|
90
|
+
- Connection: WebSocket URL, Soothe Home
|
|
91
|
+
- Daemon: Running, Threads, PID (only when daemon is live)
|
|
92
|
+
"""
|
|
93
|
+
table = Table(title="Soothe Status")
|
|
94
|
+
table.add_column("Section", style="dim", width=12)
|
|
101
95
|
table.add_column("Setting", style="cyan")
|
|
102
96
|
table.add_column("Value", style="green")
|
|
103
97
|
|
|
104
|
-
|
|
105
|
-
table.add_row("
|
|
106
|
-
table.add_row("
|
|
107
|
-
|
|
98
|
+
# Connection section
|
|
99
|
+
table.add_row("Connection", "WebSocket URL", ws_url)
|
|
100
|
+
table.add_row("", "Soothe Home", str(config.soothe_home))
|
|
101
|
+
|
|
102
|
+
# Daemon section
|
|
103
|
+
if not daemon_live:
|
|
104
|
+
table.add_row("Daemon", "Status", "[red]Not running[/red]")
|
|
105
|
+
return table
|
|
106
|
+
|
|
107
|
+
table.add_row("Daemon", "Status", "[green]Running[/green]")
|
|
108
108
|
if daemon_pid:
|
|
109
|
-
table.add_row("
|
|
109
|
+
table.add_row("", "PID", str(daemon_pid))
|
|
110
|
+
if active_threads is not None:
|
|
111
|
+
table.add_row("", "Active Threads", str(active_threads))
|
|
110
112
|
|
|
111
113
|
if ready_state:
|
|
112
114
|
state = ready_state.get("state", "unknown")
|
|
@@ -118,9 +120,9 @@ def _render_daemon_table(
|
|
|
118
120
|
"warming": "blue",
|
|
119
121
|
"stopped": "dim",
|
|
120
122
|
}.get(state, "white")
|
|
121
|
-
table.add_row("Readiness", f"[{state_color}]{state}[/{state_color}]")
|
|
123
|
+
table.add_row("", "Readiness", f"[{state_color}]{state}[/{state_color}]")
|
|
122
124
|
if ready_state.get("message"):
|
|
123
|
-
table.add_row("Message", ready_state["message"])
|
|
125
|
+
table.add_row("", "Message", ready_state["message"])
|
|
124
126
|
|
|
125
127
|
return table
|
|
126
128
|
|
|
@@ -163,15 +165,9 @@ def daemon_status(
|
|
|
163
165
|
)
|
|
164
166
|
)
|
|
165
167
|
else:
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
"Status: [red]Not running[/red]\n"
|
|
170
|
-
"Hint: Start with 'soothed start'",
|
|
171
|
-
title="Daemon Status",
|
|
172
|
-
border_style="red",
|
|
173
|
-
)
|
|
174
|
-
)
|
|
168
|
+
table = _render_unified_status_table(config, ws_url, daemon_live=False)
|
|
169
|
+
console.print(table)
|
|
170
|
+
console.print("\n[dim]Hint: Start with 'soothed start'[/dim]")
|
|
175
171
|
sys.exit(1)
|
|
176
172
|
|
|
177
173
|
# Fetch detailed status
|
|
@@ -218,14 +214,14 @@ def daemon_status(
|
|
|
218
214
|
console.print_json(json.dumps(output))
|
|
219
215
|
return
|
|
220
216
|
|
|
221
|
-
# Render daemon status table
|
|
217
|
+
# Render unified daemon status table
|
|
222
218
|
running = status.get("running", True)
|
|
223
219
|
port_live = status.get("port_live", True)
|
|
224
220
|
active_threads = status.get("active_threads", 0)
|
|
225
221
|
daemon_pid = status.get("daemon_pid")
|
|
226
222
|
|
|
227
|
-
table =
|
|
228
|
-
ws_url, running, port_live, active_threads, daemon_pid, ready_state
|
|
223
|
+
table = _render_unified_status_table(
|
|
224
|
+
config, ws_url, running, port_live, active_threads, daemon_pid, ready_state
|
|
229
225
|
)
|
|
230
226
|
console.print(table)
|
|
231
227
|
|
|
@@ -261,7 +257,12 @@ def connection_status(
|
|
|
261
257
|
)
|
|
262
258
|
return
|
|
263
259
|
|
|
264
|
-
|
|
260
|
+
# Simple connection table
|
|
261
|
+
table = Table(title="Connection Settings")
|
|
262
|
+
table.add_column("Setting", style="cyan")
|
|
263
|
+
table.add_column("Value", style="green")
|
|
264
|
+
table.add_row("WebSocket URL", ws_url)
|
|
265
|
+
table.add_row("Soothe Home", str(config.soothe_home))
|
|
265
266
|
console.print(table)
|
|
266
267
|
|
|
267
268
|
|
|
@@ -313,20 +314,11 @@ def status_main(
|
|
|
313
314
|
console.print_json(json.dumps(output))
|
|
314
315
|
return
|
|
315
316
|
|
|
316
|
-
# Render
|
|
317
|
+
# Render unified status table
|
|
317
318
|
if not live:
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
f"Daemon Host: {config.daemon_host}\n"
|
|
322
|
-
f"Daemon Port: {config.daemon_port}\n"
|
|
323
|
-
f"Soothe Home: {config.soothe_home}\n\n"
|
|
324
|
-
"Daemon Status: [red]Not running[/red]\n"
|
|
325
|
-
"Hint: Start with 'soothed start'",
|
|
326
|
-
title="Soothe Status",
|
|
327
|
-
border_style="red",
|
|
328
|
-
)
|
|
329
|
-
)
|
|
319
|
+
table = _render_unified_status_table(config, ws_url, daemon_live=False)
|
|
320
|
+
console.print(table)
|
|
321
|
+
console.print("\n[dim]Hint: Start with 'soothed start'[/dim]")
|
|
330
322
|
sys.exit(1)
|
|
331
323
|
|
|
332
324
|
# Fetch detailed daemon status
|
|
@@ -335,29 +327,22 @@ def status_main(
|
|
|
335
327
|
if "error" in status:
|
|
336
328
|
console.print(
|
|
337
329
|
Panel(
|
|
338
|
-
f"WebSocket URL: {ws_url}\
|
|
339
|
-
|
|
340
|
-
f"Daemon Port: {config.daemon_port}\n"
|
|
341
|
-
f"Soothe Home: {config.soothe_home}\n\n"
|
|
342
|
-
f"Daemon Status: [red]Error[/red]\n"
|
|
343
|
-
f"Error: {status['error']}",
|
|
344
|
-
title="Soothe Status",
|
|
330
|
+
f"WebSocket URL: {ws_url}\nError: [red]{status['error']}[/red]",
|
|
331
|
+
title="Daemon Status",
|
|
345
332
|
border_style="red",
|
|
346
333
|
)
|
|
347
334
|
)
|
|
348
335
|
sys.exit(1)
|
|
349
336
|
|
|
350
|
-
# Render both tables
|
|
351
|
-
connection_table = _render_connection_table(config, ws_url)
|
|
352
|
-
console.print(connection_table)
|
|
353
|
-
|
|
354
337
|
running = status.get("running", True)
|
|
355
338
|
port_live = status.get("port_live", True)
|
|
356
339
|
active_threads = status.get("active_threads", 0)
|
|
357
340
|
daemon_pid = status.get("daemon_pid")
|
|
358
341
|
|
|
359
|
-
|
|
360
|
-
|
|
342
|
+
table = _render_unified_status_table(
|
|
343
|
+
config, ws_url, running, port_live, active_threads, daemon_pid
|
|
344
|
+
)
|
|
345
|
+
console.print(table)
|
|
361
346
|
|
|
362
347
|
|
|
363
348
|
__all__ = [
|
|
@@ -39,6 +39,18 @@ def tool_args_meaningful(raw: Any) -> bool:
|
|
|
39
39
|
return True
|
|
40
40
|
|
|
41
41
|
|
|
42
|
+
def is_execute_step_namespace(ns_key: tuple[str, ...]) -> bool:
|
|
43
|
+
"""True for CoreAgent execute root namespace (``execute:{run_id}``), not nested ``tools:`` subgraphs."""
|
|
44
|
+
if len(ns_key) != 1:
|
|
45
|
+
return False
|
|
46
|
+
return str(ns_key[0] or "").startswith("execute:")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def is_step_card_tool_scope(*, ns_key: tuple[str, ...]) -> bool:
|
|
50
|
+
"""True when tool activity belongs on the step card as main execute-graph tools."""
|
|
51
|
+
return ns_key == () or is_execute_step_namespace(ns_key)
|
|
52
|
+
|
|
53
|
+
|
|
42
54
|
def is_main_step_level_tool_call_id(tool_call_id: str) -> bool:
|
|
43
55
|
"""True for unified main-graph step tools (``{step}:s:{tool}:{n}``), not ``task`` rows."""
|
|
44
56
|
from soothe_sdk.ux.task_namespace import is_step_level_task_tool_id, parse_unified_tool_call_id
|
|
@@ -52,9 +64,23 @@ def is_main_step_level_tool_call_id(tool_call_id: str) -> bool:
|
|
|
52
64
|
return not is_step_level_task_tool_id(tcid)
|
|
53
65
|
|
|
54
66
|
|
|
67
|
+
def is_task_level_subgraph_tool_call_id(tool_call_id: str) -> bool:
|
|
68
|
+
"""True for unified subgraph tools (``{step}:t{n}:{tool}:{seq}``), not nested ``task`` rows."""
|
|
69
|
+
from soothe_sdk.ux.task_namespace import (
|
|
70
|
+
is_inner_subgraph_task_tool_id,
|
|
71
|
+
parse_unified_tool_call_id,
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
tcid = str(tool_call_id or "").strip()
|
|
75
|
+
if not tcid or is_inner_subgraph_task_tool_id(tcid):
|
|
76
|
+
return False
|
|
77
|
+
_, type_code, _, _ = parse_unified_tool_call_id(tcid)
|
|
78
|
+
return type_code == "t"
|
|
79
|
+
|
|
80
|
+
|
|
55
81
|
def should_ingest_tool_for_step_stats(
|
|
56
82
|
*,
|
|
57
|
-
|
|
83
|
+
is_step_card_scope: bool,
|
|
58
84
|
tool_name: str,
|
|
59
85
|
tool_call_id: str,
|
|
60
86
|
args_meaningful: bool,
|
|
@@ -70,7 +96,10 @@ def should_ingest_tool_for_step_stats(
|
|
|
70
96
|
return False
|
|
71
97
|
if args_meaningful:
|
|
72
98
|
return True
|
|
73
|
-
|
|
99
|
+
if is_step_card_scope and is_main_step_level_tool_call_id(tcid):
|
|
100
|
+
return True
|
|
101
|
+
# Subgraph explore tools often arrive with ``{"_subgraph_tool": true}`` before real args.
|
|
102
|
+
return not is_step_card_scope and is_task_level_subgraph_tool_call_id(tcid)
|
|
74
103
|
|
|
75
104
|
|
|
76
105
|
def _args_from_toolish_block(block: dict[str, Any]) -> dict[str, Any]:
|
|
@@ -465,7 +494,10 @@ def build_streaming_args_overlay(
|
|
|
465
494
|
__all__ = [
|
|
466
495
|
"ResolvedToolInvocation",
|
|
467
496
|
"build_streaming_args_overlay",
|
|
497
|
+
"is_execute_step_namespace",
|
|
468
498
|
"is_main_step_level_tool_call_id",
|
|
499
|
+
"is_step_card_tool_scope",
|
|
500
|
+
"is_task_level_subgraph_tool_call_id",
|
|
469
501
|
"is_toolish_display_block",
|
|
470
502
|
"materialize_ai_blocks_with_resolved_tools",
|
|
471
503
|
"merge_tool_display_args",
|
|
@@ -17,7 +17,7 @@ from typing import Any, TypeAlias
|
|
|
17
17
|
from soothe_sdk.ux.task_namespace import (
|
|
18
18
|
TaskScope,
|
|
19
19
|
is_inner_subgraph_task_tool_id,
|
|
20
|
-
|
|
20
|
+
normalize_main_task_delegation_id,
|
|
21
21
|
parse_unified_tool_call_id,
|
|
22
22
|
prune_bound_pending_namespaces,
|
|
23
23
|
register_task_spawn_for_step,
|
|
@@ -62,6 +62,21 @@ def _subgraph_pending_key(ns_key: tuple[str, ...], lookup_id: str) -> tuple[tupl
|
|
|
62
62
|
return (ns_key, str(lookup_id).strip())
|
|
63
63
|
|
|
64
64
|
|
|
65
|
+
def _is_task_metadata_subgraph_tool(item: PendingSubgraphTool) -> bool:
|
|
66
|
+
"""True when a buffered subgraph item is task metadata, not a user-facing tool row."""
|
|
67
|
+
if (item.tool_name or "").strip() == "task":
|
|
68
|
+
return True
|
|
69
|
+
for candidate in (item.lookup_id, item.display_key):
|
|
70
|
+
cid = str(candidate or "").strip()
|
|
71
|
+
if cid and is_inner_subgraph_task_tool_id(cid):
|
|
72
|
+
return True
|
|
73
|
+
args = item.args if isinstance(item.args, dict) else {}
|
|
74
|
+
subagent_type = str(args.get("subagent_type") or "").strip()
|
|
75
|
+
prompt = str(args.get("description") or args.get("prompt") or "").strip()
|
|
76
|
+
# Some providers emit opaque names (e.g. "tool-<id>") for task chunks.
|
|
77
|
+
return bool(subagent_type and prompt)
|
|
78
|
+
|
|
79
|
+
|
|
65
80
|
@dataclass
|
|
66
81
|
class StepTaskRouter:
|
|
67
82
|
"""High-performance per-turn router for steps, tools, and task namespaces.
|
|
@@ -123,7 +138,15 @@ class StepTaskRouter:
|
|
|
123
138
|
if existing is None:
|
|
124
139
|
self._pending_subgraph_tools[key] = item
|
|
125
140
|
return
|
|
126
|
-
|
|
141
|
+
# Prefer meaningful args over placeholder metadata like {"_subgraph_tool": true}.
|
|
142
|
+
from soothe_cli.runtime.parse.message_processing import extract_tool_args_dict
|
|
143
|
+
|
|
144
|
+
item_meaningful = extract_tool_args_dict(item.args or {})
|
|
145
|
+
existing_meaningful = extract_tool_args_dict(existing.args or {})
|
|
146
|
+
if len(item_meaningful) >= len(existing_meaningful):
|
|
147
|
+
args = item.args
|
|
148
|
+
else:
|
|
149
|
+
args = existing.args
|
|
127
150
|
raw = item.raw_args if len(item.raw_args) >= len(existing.raw_args) else existing.raw_args
|
|
128
151
|
self._pending_subgraph_tools[key] = PendingSubgraphTool(
|
|
129
152
|
ns_key=item.ns_key,
|
|
@@ -219,14 +242,12 @@ class StepTaskRouter:
|
|
|
219
242
|
if not tcid or is_inner_subgraph_task_tool_id(tcid):
|
|
220
243
|
return False
|
|
221
244
|
parsed_sid, type_code, _, _ = parse_unified_tool_call_id(tcid)
|
|
222
|
-
if type_code
|
|
223
|
-
return False
|
|
224
|
-
sid = parsed_sid if (parsed_sid and type_code == "s") else ""
|
|
245
|
+
sid = parsed_sid if (parsed_sid and type_code in ("s", "t")) else ""
|
|
225
246
|
if not sid:
|
|
226
247
|
sid = str(step_id).strip()
|
|
227
248
|
if not sid:
|
|
228
249
|
return False
|
|
229
|
-
normalized_tcid =
|
|
250
|
+
normalized_tcid = normalize_main_task_delegation_id(sid, tcid, tool_name="task")
|
|
230
251
|
spawn_key = (sid, normalized_tcid)
|
|
231
252
|
if spawn_key in self._spawn_recorded:
|
|
232
253
|
return False
|
|
@@ -375,7 +396,7 @@ class StepTaskRouter:
|
|
|
375
396
|
tool_to_step: dict[str, ParentWidget],
|
|
376
397
|
) -> bool:
|
|
377
398
|
"""Register one subgraph tool row on an already-resolved parent step card."""
|
|
378
|
-
if (item
|
|
399
|
+
if _is_task_metadata_subgraph_tool(item):
|
|
379
400
|
# Inner explore ``task`` chunks are not user-facing tool stats; ingesting
|
|
380
401
|
# them used to rewrite the main ``{step}:s:task:…`` delegation row args.
|
|
381
402
|
return True
|
|
@@ -399,13 +420,31 @@ class StepTaskRouter:
|
|
|
399
420
|
if has_row(row_id):
|
|
400
421
|
update = getattr(parent, "update_tool_args", None)
|
|
401
422
|
if callable(update):
|
|
402
|
-
|
|
423
|
+
resolved_args = dict(item.args or {})
|
|
424
|
+
# Placeholder args like {"_subgraph_tool": true} are not meaningful.
|
|
425
|
+
# Parse raw_args when resolved_args lacks real invocation kwargs.
|
|
426
|
+
from soothe_cli.runtime.parse.message_processing import extract_tool_args_dict
|
|
427
|
+
|
|
428
|
+
meaningful_args = extract_tool_args_dict(resolved_args)
|
|
429
|
+
if item.raw_args and not meaningful_args:
|
|
430
|
+
parsed = extract_tool_args_dict({"_raw": item.raw_args})
|
|
431
|
+
if parsed:
|
|
432
|
+
resolved_args = parsed
|
|
433
|
+
update(row_id, resolved_args)
|
|
403
434
|
else:
|
|
404
435
|
parent_task_id = str(scope[0]).strip()
|
|
436
|
+
resolved_args = dict(item.args or {})
|
|
437
|
+
from soothe_cli.runtime.parse.message_processing import extract_tool_args_dict
|
|
438
|
+
|
|
439
|
+
meaningful_args = extract_tool_args_dict(resolved_args)
|
|
440
|
+
if item.raw_args and not meaningful_args:
|
|
441
|
+
parsed = extract_tool_args_dict({"_raw": item.raw_args})
|
|
442
|
+
if parsed:
|
|
443
|
+
resolved_args = parsed
|
|
405
444
|
ingest(
|
|
406
445
|
row_id,
|
|
407
446
|
item.tool_name,
|
|
408
|
-
|
|
447
|
+
resolved_args,
|
|
409
448
|
raw_args=item.raw_args,
|
|
410
449
|
parent_tool_call_id=parent_task_id or None,
|
|
411
450
|
)
|
|
@@ -50,7 +50,7 @@ from soothe_sdk.ux.stream_tool_wire import STREAM_TOOL_CALL_UPDATE, TOOL_CALL_UP
|
|
|
50
50
|
from soothe_sdk.ux.task_namespace import (
|
|
51
51
|
TaskScope,
|
|
52
52
|
is_inner_subgraph_task_tool_id,
|
|
53
|
-
|
|
53
|
+
normalize_main_task_delegation_id,
|
|
54
54
|
parse_unified_tool_call_id,
|
|
55
55
|
row_key_for_subgraph_tool,
|
|
56
56
|
)
|
|
@@ -62,6 +62,8 @@ from soothe_cli.runtime.parse.message_processing import (
|
|
|
62
62
|
)
|
|
63
63
|
from soothe_cli.runtime.parse.tool_call_resolution import (
|
|
64
64
|
build_streaming_args_overlay,
|
|
65
|
+
is_execute_step_namespace,
|
|
66
|
+
is_step_card_tool_scope,
|
|
65
67
|
materialize_ai_blocks_with_resolved_tools,
|
|
66
68
|
merge_tool_display_args,
|
|
67
69
|
resolve_stream_tool_name,
|
|
@@ -521,7 +523,7 @@ def _ingest_main_task_tool_on_step_card(
|
|
|
521
523
|
adapter._tool_display_by_call_id,
|
|
522
524
|
)
|
|
523
525
|
return
|
|
524
|
-
norm_tcid =
|
|
526
|
+
norm_tcid = normalize_main_task_delegation_id(sid, tcid, tool_name="task")
|
|
525
527
|
step_w = _resolve_step_widget_for_tool(
|
|
526
528
|
adapter,
|
|
527
529
|
router,
|
|
@@ -597,10 +599,18 @@ def _fallback_ingest_subgraph_tool_on_step_card(
|
|
|
597
599
|
if step_w is None:
|
|
598
600
|
return False
|
|
599
601
|
row_id = lookup if type_code == "t" else (display or lookup)
|
|
602
|
+
resolved_args = dict(args or {})
|
|
603
|
+
# Placeholder args like {"_subgraph_tool": true} are not meaningful.
|
|
604
|
+
# Parse raw_args when resolved_args lacks real invocation kwargs.
|
|
605
|
+
meaningful_args = extract_tool_args_dict(resolved_args)
|
|
606
|
+
if raw_args and not meaningful_args:
|
|
607
|
+
parsed = extract_tool_args_dict({"_raw": raw_args})
|
|
608
|
+
if parsed:
|
|
609
|
+
resolved_args = parsed
|
|
600
610
|
if step_w.has_tool_call_row(row_id):
|
|
601
|
-
step_w.update_tool_args(row_id,
|
|
611
|
+
step_w.update_tool_args(row_id, resolved_args)
|
|
602
612
|
else:
|
|
603
|
-
step_w.add_tool_call(row_id, tool_name,
|
|
613
|
+
step_w.add_tool_call(row_id, tool_name, resolved_args, raw_args=raw_args)
|
|
604
614
|
adapter._tool_to_step[row_id] = step_w
|
|
605
615
|
adapter._tool_display_by_call_id[row_id] = step_w
|
|
606
616
|
return True
|
|
@@ -858,7 +868,7 @@ async def apply_tool_call_wire_update(
|
|
|
858
868
|
if str(data.get("type", "")) != STREAM_TOOL_CALL_UPDATE:
|
|
859
869
|
return False
|
|
860
870
|
|
|
861
|
-
if ns_key:
|
|
871
|
+
if ns_key and not is_execute_step_namespace(ns_key):
|
|
862
872
|
router.on_subgraph_namespace(ns_key)
|
|
863
873
|
|
|
864
874
|
tcid = str(data.get("tool_call_id", "")).strip()
|
|
@@ -866,62 +876,79 @@ async def apply_tool_call_wire_update(
|
|
|
866
876
|
return True
|
|
867
877
|
|
|
868
878
|
name = str(data.get("name") or "").strip() or "tool"
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
879
|
+
raw_args_field = data.get("args")
|
|
880
|
+
raw_args_stream = ""
|
|
881
|
+
if isinstance(raw_args_field, str):
|
|
882
|
+
raw_args_stream = raw_args_field
|
|
883
|
+
elif raw_args_field is not None:
|
|
884
|
+
try:
|
|
885
|
+
raw_args_stream = json.dumps(raw_args_field, separators=(",", ":"), default=str)
|
|
886
|
+
except (TypeError, ValueError):
|
|
887
|
+
raw_args_stream = str(raw_args_field)
|
|
888
|
+
display_args = extract_tool_args_dict(raw_args_field)
|
|
889
|
+
is_step_scope = is_step_card_tool_scope(ns_key=ns_key)
|
|
890
|
+
if not display_args and not should_ingest_tool_for_step_stats(
|
|
891
|
+
is_step_card_scope=is_step_scope,
|
|
875
892
|
tool_name=name,
|
|
876
893
|
tool_call_id=tcid,
|
|
877
894
|
args_meaningful=False,
|
|
878
895
|
):
|
|
879
896
|
return True
|
|
880
897
|
|
|
881
|
-
if ui_coalesce is not None and ui_coalesce.note_wire_apply(
|
|
898
|
+
if ui_coalesce is not None and ui_coalesce.note_wire_apply(
|
|
899
|
+
tcid, display_args or raw_args_field
|
|
900
|
+
):
|
|
882
901
|
return True
|
|
883
902
|
|
|
884
903
|
overlay = streaming_overlay if streaming_overlay is not None else {}
|
|
885
904
|
ts = router.resolve_task_scope(ns_key) if ns_key else None
|
|
886
905
|
merge_id, row_key = (
|
|
887
|
-
(tcid, tcid) if
|
|
906
|
+
(tcid, tcid) if is_step_scope else canonical_subgraph_tool_ids(ns_key, tcid, task_scope=ts)
|
|
888
907
|
)
|
|
889
908
|
|
|
909
|
+
overlay_payload = dict(display_args or {})
|
|
890
910
|
for key in {tcid, merge_id, row_key}:
|
|
891
911
|
if not key:
|
|
892
912
|
continue
|
|
893
|
-
|
|
913
|
+
if overlay_payload:
|
|
914
|
+
overlay[key] = dict(overlay_payload)
|
|
894
915
|
pending_tool_calls_lc[key] = {
|
|
895
916
|
"name": name,
|
|
896
|
-
"args_str":
|
|
917
|
+
"args_str": raw_args_stream
|
|
918
|
+
if raw_args_stream
|
|
919
|
+
else json.dumps(overlay_payload, separators=(",", ":")),
|
|
897
920
|
"is_complete_json": True,
|
|
898
921
|
"emitted": False,
|
|
899
|
-
"is_main":
|
|
922
|
+
"is_main": is_step_scope,
|
|
900
923
|
}
|
|
901
924
|
|
|
902
|
-
if ns_key:
|
|
925
|
+
if ns_key and not is_execute_step_namespace(ns_key):
|
|
903
926
|
alias_subgraph_pending_and_overlay(pending_tool_calls_lc, overlay, router, ns_key)
|
|
904
927
|
|
|
905
928
|
display_args = merge_tool_display_args(
|
|
906
929
|
merge_id or tcid,
|
|
907
|
-
block_args=
|
|
930
|
+
block_args=display_args,
|
|
908
931
|
streaming_overlay=overlay,
|
|
909
932
|
pending_tool_calls_lc=pending_tool_calls_lc,
|
|
910
933
|
tool_name=name,
|
|
911
934
|
)
|
|
912
935
|
|
|
913
|
-
if
|
|
936
|
+
if (
|
|
937
|
+
file_op_tracker is not None
|
|
938
|
+
and name in FILE_CHANGE_TOOLS
|
|
939
|
+
and tool_args_meaningful(display_args)
|
|
940
|
+
):
|
|
914
941
|
file_tcid = str(merge_id or tcid)
|
|
915
|
-
track_file_operation(file_op_tracker, name,
|
|
942
|
+
track_file_operation(file_op_tracker, name, display_args, file_tcid)
|
|
916
943
|
await mount_file_change_preview(
|
|
917
944
|
adapter,
|
|
918
945
|
tool_name=name,
|
|
919
|
-
args=
|
|
946
|
+
args=display_args,
|
|
920
947
|
tool_call_id=file_tcid,
|
|
921
948
|
assistant_id=adapter._file_preview_assistant_id,
|
|
922
949
|
)
|
|
923
950
|
|
|
924
|
-
if
|
|
951
|
+
if is_step_scope and name == "task":
|
|
925
952
|
if is_inner_subgraph_task_tool_id(tcid):
|
|
926
953
|
return True
|
|
927
954
|
parsed_sid, _, _, _ = parse_unified_tool_call_id(tcid)
|
|
@@ -935,7 +962,7 @@ async def apply_tool_call_wire_update(
|
|
|
935
962
|
)
|
|
936
963
|
return True
|
|
937
964
|
|
|
938
|
-
if
|
|
965
|
+
if is_step_scope:
|
|
939
966
|
parsed_sid, _, _, _ = parse_unified_tool_call_id(tcid)
|
|
940
967
|
bound_step_id = parsed_sid or router.step_id_for_tool(tcid)
|
|
941
968
|
step_w = _resolve_step_widget_for_tool(
|
|
@@ -945,10 +972,13 @@ async def apply_tool_call_wire_update(
|
|
|
945
972
|
ns_key=ns_key,
|
|
946
973
|
)
|
|
947
974
|
if step_w is not None and name != "task":
|
|
975
|
+
update_payload = dict(display_args or {})
|
|
976
|
+
if not update_payload and raw_args_stream:
|
|
977
|
+
update_payload = {"_raw": raw_args_stream}
|
|
948
978
|
if step_w.has_tool_call_row(tcid):
|
|
949
|
-
step_w.update_tool_args(tcid,
|
|
979
|
+
step_w.update_tool_args(tcid, update_payload)
|
|
950
980
|
else:
|
|
951
|
-
step_w.add_tool_call(tcid, name,
|
|
981
|
+
step_w.add_tool_call(tcid, name, update_payload, raw_args=raw_args_stream)
|
|
952
982
|
adapter._tool_to_step[tcid] = step_w
|
|
953
983
|
return True
|
|
954
984
|
|
|
@@ -960,6 +990,7 @@ async def apply_tool_call_wire_update(
|
|
|
960
990
|
display_key=display_key,
|
|
961
991
|
tool_name=name,
|
|
962
992
|
args=display_args,
|
|
993
|
+
raw_args=raw_args_stream,
|
|
963
994
|
step_cards=adapter._current_step_messages,
|
|
964
995
|
tool_to_step=adapter._tool_to_step,
|
|
965
996
|
tool_display_by_call_id=adapter._tool_display_by_call_id,
|
|
@@ -972,6 +1003,7 @@ async def apply_tool_call_wire_update(
|
|
|
972
1003
|
display_key=display_key,
|
|
973
1004
|
tool_name=name,
|
|
974
1005
|
args=display_args,
|
|
1006
|
+
raw_args=raw_args_stream,
|
|
975
1007
|
ns_key=ns_key,
|
|
976
1008
|
)
|
|
977
1009
|
return True
|
|
@@ -1736,6 +1768,7 @@ async def execute_task_textual(
|
|
|
1736
1768
|
# namespaces. Assistant *text* from subgraphs is suppressed (avoid duplicate
|
|
1737
1769
|
# prose with main). Tool stats attach to step cards on the main graph only.
|
|
1738
1770
|
is_main_agent = ns_key == ()
|
|
1771
|
+
is_step_scope = is_step_card_tool_scope(ns_key=ns_key)
|
|
1739
1772
|
suppress_subgraph_assistant_text = not is_main_agent
|
|
1740
1773
|
suppress_main_agent_assistant_text = False
|
|
1741
1774
|
|
|
@@ -1765,7 +1798,7 @@ async def execute_task_textual(
|
|
|
1765
1798
|
message, metadata = data
|
|
1766
1799
|
message = _normalize_lc_stream_message(message)
|
|
1767
1800
|
|
|
1768
|
-
if ns_key:
|
|
1801
|
+
if ns_key and not is_execute_step_namespace(ns_key):
|
|
1769
1802
|
router.on_subgraph_namespace(ns_key)
|
|
1770
1803
|
|
|
1771
1804
|
# Filter out summarization model output, but keep UI feedback.
|
|
@@ -2285,7 +2318,7 @@ async def execute_task_textual(
|
|
|
2285
2318
|
parsed_args = extract_tool_args_dict(parsed_args)
|
|
2286
2319
|
|
|
2287
2320
|
merge_lookup_id = lookup_id
|
|
2288
|
-
if lookup_id and not
|
|
2321
|
+
if lookup_id and not is_step_scope:
|
|
2289
2322
|
ts_merge = router.resolve_task_scope(ns_key)
|
|
2290
2323
|
merge_lookup_id, _rk = canonical_subgraph_tool_ids(
|
|
2291
2324
|
ns_key, str(lookup_id), task_scope=ts_merge
|
|
@@ -2327,7 +2360,7 @@ async def execute_task_textual(
|
|
|
2327
2360
|
|
|
2328
2361
|
args_meaningful = tool_args_meaningful(parsed_args)
|
|
2329
2362
|
ingest_for_stats = should_ingest_tool_for_step_stats(
|
|
2330
|
-
|
|
2363
|
+
is_step_card_scope=is_step_scope,
|
|
2331
2364
|
tool_name=str(buffer_name or ""),
|
|
2332
2365
|
tool_call_id=str(lookup_id or ""),
|
|
2333
2366
|
args_meaningful=args_meaningful,
|
|
@@ -2339,7 +2372,7 @@ async def execute_task_textual(
|
|
|
2339
2372
|
if lookup_id and buffer_name and ingest_for_stats:
|
|
2340
2373
|
if buffer_name in FILE_CHANGE_TOOLS and args_meaningful:
|
|
2341
2374
|
file_tcid = str(lookup_id)
|
|
2342
|
-
if not
|
|
2375
|
+
if not is_step_scope:
|
|
2343
2376
|
ts_file = router.resolve_task_scope(ns_key)
|
|
2344
2377
|
file_tcid, _fk = canonical_subgraph_tool_ids(
|
|
2345
2378
|
ns_key, file_tcid, task_scope=ts_file
|
|
@@ -2359,7 +2392,7 @@ async def execute_task_textual(
|
|
|
2359
2392
|
assistant_id=assistant_id,
|
|
2360
2393
|
)
|
|
2361
2394
|
|
|
2362
|
-
if
|
|
2395
|
+
if is_step_scope and buffer_name == "task":
|
|
2363
2396
|
if not is_inner_subgraph_task_tool_id(str(lookup_id)):
|
|
2364
2397
|
parsed_step_id, _, _, _ = parse_unified_tool_call_id(
|
|
2365
2398
|
str(lookup_id)
|
|
@@ -2374,7 +2407,7 @@ async def execute_task_textual(
|
|
|
2374
2407
|
parsed_args,
|
|
2375
2408
|
bound_step_id=bound_step_id,
|
|
2376
2409
|
)
|
|
2377
|
-
elif
|
|
2410
|
+
elif is_step_scope and buffer_name != "task":
|
|
2378
2411
|
parsed_sid, _, _, _ = parse_unified_tool_call_id(
|
|
2379
2412
|
str(lookup_id)
|
|
2380
2413
|
)
|
|
@@ -2410,7 +2443,7 @@ async def execute_task_textual(
|
|
|
2410
2443
|
parsed_args,
|
|
2411
2444
|
raw_args=raw_args_stream,
|
|
2412
2445
|
)
|
|
2413
|
-
elif not
|
|
2446
|
+
elif not is_step_scope:
|
|
2414
2447
|
ts_disp = router.resolve_task_scope(ns_key)
|
|
2415
2448
|
_merge_disp, display_key = canonical_subgraph_tool_ids(
|
|
2416
2449
|
ns_key, str(lookup_id), task_scope=ts_disp
|
|
@@ -10,13 +10,16 @@ from soothe_sdk.tools.metadata import get_tool_meta
|
|
|
10
10
|
from soothe_sdk.utils import get_tool_display_name
|
|
11
11
|
from soothe_sdk.utils.formatting import convert_and_abbreviate_path
|
|
12
12
|
|
|
13
|
-
from soothe_cli.runtime.parse.message_processing import
|
|
13
|
+
from soothe_cli.runtime.parse.message_processing import (
|
|
14
|
+
_normalize_tool_name_for_arg_map,
|
|
15
|
+
extract_tool_args_dict,
|
|
16
|
+
)
|
|
14
17
|
from soothe_cli.runtime.presentation.duration_format import format_duration_ms
|
|
15
18
|
|
|
16
19
|
_ARG_PREVIEW_MAX_CHARS = 80
|
|
17
20
|
_EDIT_STRING_PREVIEW_MAX_CHARS = 30
|
|
18
21
|
_EDIT_STRING_ARG_KEYS = frozenset({"old_string", "new_string"})
|
|
19
|
-
_SKIP_ARG_KEYS = frozenset({"_raw"})
|
|
22
|
+
_SKIP_ARG_KEYS = frozenset({"_raw", "_subgraph_tool", "value"})
|
|
20
23
|
|
|
21
24
|
|
|
22
25
|
def compact_arg_text(text: str) -> str:
|
|
@@ -72,7 +75,18 @@ def _ordered_arg_keys(tool_name: str, clean: dict[str, Any]) -> list[str]:
|
|
|
72
75
|
|
|
73
76
|
def _args_preview(tool_name: str, args: dict[str, Any]) -> str:
|
|
74
77
|
"""Comma-separated arg summary: primary value bare, extras as ``key=value``."""
|
|
75
|
-
|
|
78
|
+
normalized = extract_tool_args_dict(args or {})
|
|
79
|
+
if not normalized and isinstance(args, dict):
|
|
80
|
+
raw_value = args.get("value")
|
|
81
|
+
if isinstance(raw_value, str) and raw_value.strip():
|
|
82
|
+
try:
|
|
83
|
+
loaded = json.loads(raw_value)
|
|
84
|
+
except (TypeError, ValueError):
|
|
85
|
+
loaded = None
|
|
86
|
+
if isinstance(loaded, dict):
|
|
87
|
+
normalized = loaded
|
|
88
|
+
source_args = normalized if normalized else (args or {})
|
|
89
|
+
clean = {k: v for k, v in source_args.items() if k not in _SKIP_ARG_KEYS}
|
|
76
90
|
if not clean:
|
|
77
91
|
return ""
|
|
78
92
|
segments: list[str] = []
|