soothe-cli 0.7.18__tar.gz → 0.8.1__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.7.18 → soothe_cli-0.8.1}/PKG-INFO +2 -1
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/pyproject.toml +2 -1
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/cli/commands/autopilot_cmd.py +2 -2
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/cli/commands/config_cmd.py +1 -1
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/cli/commands/cron_cmd.py +1 -1
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/cli/commands/loop_cmd.py +16 -4
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/cli/commands/run_cmd.py +9 -62
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/cli/commands/status_cmd.py +1 -1
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/cli/execution/daemon.py +6 -47
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/cli/execution/headless.py +1 -1
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/cli/main.py +1 -1
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/config/cli_config.py +1 -1
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/runtime/headless/processor.py +2 -2
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/runtime/headless/processor_state.py +1 -1
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/runtime/parse/tool_result.py +2 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/runtime/presentation/renderer_protocol.py +1 -1
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/runtime/state/step_router.py +52 -1
- soothe_cli-0.8.1/src/soothe_cli/runtime/transport/session.py +80 -0
- soothe_cli-0.8.1/src/soothe_cli/runtime/turn/pipeline.py +35 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/runtime/wire/message_text.py +1 -1
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/app/_app.py +54 -2
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/app/_execution.py +2 -13
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/app/_messages_mixin.py +8 -34
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/app/_model.py +3 -2
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/app/_startup.py +4 -4
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/command_registry.py +6 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/commands/__init__.py +2 -2
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/commands/command_router.py +8 -7
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/commands/slash_commands.py +9 -3
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/commands/subagent_routing.py +18 -6
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/config.py +1 -1
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/model_config.py +9 -3
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/skills/invocation.py +5 -2
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/spinner_labels.py +8 -1
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/textual_adapter.py +246 -16
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/theme.py +1 -1
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/tips.py +1 -1
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/tool_display.py +1 -1
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/update_check.py +1 -1
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/chat_input.py +1 -1
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/context_data.py +38 -31
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/context_viewer.py +7 -4
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/help_screen.py +3 -3
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/messages/cognition_goal_tree.py +1 -1
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/plan_quick_view_overlay.py +10 -3
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/status.py +25 -4
- soothe_cli-0.7.18/src/soothe_cli/runtime/transport/session.py +0 -757
- soothe_cli-0.7.18/src/soothe_cli/runtime/turn/pipeline.py +0 -404
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/.gitignore +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/README.md +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/__init__.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/cli/__init__.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/cli/commands/__init__.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/cli/execution/__init__.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/cli/execution/daemon_errors.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/cli/execution/headless_renderer.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/cli/execution/launcher.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/config/__init__.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/config/loader.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/config/logging_setup.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/runtime/__init__.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/runtime/parse/message_processing.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/runtime/parse/tool_call_resolution.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/runtime/policy/display_policy.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/runtime/policy/essential_events.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/runtime/policy/tui_trace_log.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/runtime/presentation/duration_format.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/runtime/presentation/engine.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/runtime/presentation/id_format.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/runtime/presentation/renderer_base.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/runtime/presentation/subagent_task_display.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/runtime/state/file_tracker.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/runtime/state/session_stats.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/runtime/state/stream_accumulator.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/runtime/state/transcript.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/runtime/token_events_debug.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/runtime/token_usage.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/runtime/turn/prepare.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/runtime/wire/chunk_filter.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/runtime/wire/messages.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/__init__.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/_cli_context.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/_env_vars.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/_version.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/app/__init__.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/app/_history.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/app/_module_init.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/app/_ui.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/app/app.tcss +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/binding.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/file_change_notify.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/file_change_renderers.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/hooks.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/input.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/markdown_theme.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/media_utils.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/path_utils.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/preview_limits.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/project_utils.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/sessions.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/shell_color.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/skills/__init__.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/skills/load.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/unicode_security.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/__init__.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/_links.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/autocomplete.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/clipboard.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/diff.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/editor.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/file_change_preview.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/history.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/loading.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/loop_selector.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/mcp_viewer.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/message_store.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/messages/__init__.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/messages/_helpers.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/messages/app.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/messages/assistant.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/messages/clarification.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/messages/cognition_reason.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/messages/cognition_step.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/messages/cognition_step_activity.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/messages/cognition_subagent.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/messages/diff_message.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/messages/error.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/messages/skill.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/messages/user.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/model_selector.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/notification_settings.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/router_profile_selector.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/theme_selector.py +0 -0
- {soothe_cli-0.7.18 → soothe_cli-0.8.1}/src/soothe_cli/tui/widgets/welcome.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: soothe-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.1
|
|
4
4
|
Summary: Soothe CLI client - communicates with daemon via WebSocket
|
|
5
5
|
Project-URL: Homepage, https://github.com/mirasoth/soothe
|
|
6
6
|
Project-URL: Documentation, https://soothe.readthedocs.io
|
|
@@ -22,6 +22,7 @@ Requires-Dist: pillow<12.0.0,>=10.0.0
|
|
|
22
22
|
Requires-Dist: python-dotenv<2.0.0,>=1.0.0
|
|
23
23
|
Requires-Dist: pyyaml<7.0.0,>=6.0.0
|
|
24
24
|
Requires-Dist: rich<15.0.0,>=13.0.0
|
|
25
|
+
Requires-Dist: soothe-client-python<1.0.0,>=0.5.25
|
|
25
26
|
Requires-Dist: soothe-sdk<1.0.0,>=0.5.25
|
|
26
27
|
Requires-Dist: textual<10.0.0,>=8.0.0
|
|
27
28
|
Requires-Dist: typer<2.0.0,>=0.9.0
|
|
@@ -23,7 +23,8 @@ classifiers = [
|
|
|
23
23
|
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
24
24
|
]
|
|
25
25
|
dependencies = [
|
|
26
|
-
"soothe-sdk>=0.5.25,<1.0.0", #
|
|
26
|
+
"soothe-sdk>=0.5.25,<1.0.0", # events/display/wire contracts
|
|
27
|
+
"soothe-client-python>=0.5.25,<1.0.0", # WebSocket transport client
|
|
27
28
|
"typer>=0.9.0,<2.0.0", # CLI framework
|
|
28
29
|
"textual>=8.0.0,<10.0.0", # TUI framework
|
|
29
30
|
"rich>=13.0.0,<15.0.0", # Console output
|
|
@@ -14,12 +14,12 @@ from collections import Counter
|
|
|
14
14
|
from pathlib import Path
|
|
15
15
|
|
|
16
16
|
import typer
|
|
17
|
-
from
|
|
17
|
+
from soothe_client import (
|
|
18
18
|
is_daemon_live,
|
|
19
19
|
websocket_url_from_config,
|
|
20
20
|
ws_command_client_from_config,
|
|
21
21
|
)
|
|
22
|
-
from soothe_sdk.
|
|
22
|
+
from soothe_sdk.wire.protocol import preview_first
|
|
23
23
|
|
|
24
24
|
app = typer.Typer(help="Autopilot mode — long-running autonomous agent control.")
|
|
25
25
|
|
|
@@ -10,7 +10,7 @@ from typing import Annotated, Any
|
|
|
10
10
|
import typer
|
|
11
11
|
from rich.console import Console
|
|
12
12
|
from rich.panel import Panel
|
|
13
|
-
from
|
|
13
|
+
from soothe_client import (
|
|
14
14
|
WebSocketClient,
|
|
15
15
|
request_daemon_config_reload,
|
|
16
16
|
websocket_url_from_config,
|
|
@@ -21,7 +21,7 @@ import typer
|
|
|
21
21
|
from rich.console import Console
|
|
22
22
|
from rich.panel import Panel
|
|
23
23
|
from rich.table import Table
|
|
24
|
-
from
|
|
24
|
+
from soothe_client import (
|
|
25
25
|
is_daemon_live,
|
|
26
26
|
websocket_url_from_config,
|
|
27
27
|
ws_command_client_from_config,
|
|
@@ -17,12 +17,14 @@ from rich.console import Console
|
|
|
17
17
|
from rich.panel import Panel
|
|
18
18
|
from rich.prompt import Prompt
|
|
19
19
|
from rich.table import Table
|
|
20
|
-
from
|
|
21
|
-
ProtocolError,
|
|
20
|
+
from soothe_client import (
|
|
22
21
|
WebSocketClient,
|
|
23
22
|
is_daemon_live,
|
|
24
23
|
websocket_url_from_config,
|
|
25
24
|
)
|
|
25
|
+
from soothe_sdk.client import (
|
|
26
|
+
ProtocolError,
|
|
27
|
+
)
|
|
26
28
|
|
|
27
29
|
from soothe_cli.runtime import load_config
|
|
28
30
|
|
|
@@ -79,13 +81,23 @@ async def _rpc(
|
|
|
79
81
|
client = WebSocketClient(url=ws_url)
|
|
80
82
|
try:
|
|
81
83
|
await client.connect()
|
|
84
|
+
await asyncio.wait_for(client.request_connection_init(), timeout=timeout)
|
|
85
|
+
await asyncio.wait_for(
|
|
86
|
+
client.wait_for_connection_ack(ack_timeout_s=timeout), timeout=timeout
|
|
87
|
+
)
|
|
82
88
|
if mode == "notify":
|
|
83
89
|
await asyncio.wait_for(client.notify(method, params or {}), timeout=timeout)
|
|
84
90
|
return {}
|
|
85
91
|
if mode == "subscribe":
|
|
86
|
-
sub_id = await asyncio.wait_for(
|
|
92
|
+
sub_id = await asyncio.wait_for(
|
|
93
|
+
client.subscribe(method, params or {}, timeout=timeout),
|
|
94
|
+
timeout=timeout,
|
|
95
|
+
)
|
|
87
96
|
return {"subscription_id": sub_id}
|
|
88
|
-
result = await asyncio.wait_for(
|
|
97
|
+
result = await asyncio.wait_for(
|
|
98
|
+
client.request(method, params or {}, timeout=timeout),
|
|
99
|
+
timeout=timeout,
|
|
100
|
+
)
|
|
89
101
|
return result if isinstance(result, dict) else {"result": result}
|
|
90
102
|
except TimeoutError:
|
|
91
103
|
return {"error": "Timed out waiting for daemon response"}
|
|
@@ -4,10 +4,9 @@ import logging
|
|
|
4
4
|
import sys
|
|
5
5
|
import time
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import Any
|
|
8
7
|
|
|
9
8
|
import typer
|
|
10
|
-
from soothe_sdk.
|
|
9
|
+
from soothe_sdk.paths import SOOTHE_HOME
|
|
11
10
|
from soothe_sdk.utils.logging import resolve_cli_log_level
|
|
12
11
|
|
|
13
12
|
from soothe_cli.cli.execution import run_headless, run_tui
|
|
@@ -37,7 +36,8 @@ def run_impl(
|
|
|
37
36
|
autonomous: Enable autonomous iteration mode
|
|
38
37
|
max_iterations: Max iterations for autonomous mode
|
|
39
38
|
tui_with_prompt: When True with a prompt, open the TUI instead of headless.
|
|
40
|
-
mcp_config:
|
|
39
|
+
mcp_config: Deprecated. MCP servers must be configured on the daemon;
|
|
40
|
+
passing this flag only emits a warning.
|
|
41
41
|
"""
|
|
42
42
|
startup_start = time.perf_counter()
|
|
43
43
|
use_headless: bool | None = None # Track execution mode for exit tip
|
|
@@ -45,9 +45,13 @@ def run_impl(
|
|
|
45
45
|
try:
|
|
46
46
|
cfg = load_config()
|
|
47
47
|
|
|
48
|
-
#
|
|
48
|
+
# MCP servers are owned by the daemon config; CLI cannot merge them post-split.
|
|
49
49
|
if mcp_config:
|
|
50
|
-
|
|
50
|
+
typer.echo(
|
|
51
|
+
"--mcp-config is ignored in the CLI; configure MCP servers on the daemon "
|
|
52
|
+
f"(~/.soothe/config). Requested file: {mcp_config}",
|
|
53
|
+
err=True,
|
|
54
|
+
)
|
|
51
55
|
log_level = resolve_cli_log_level(logging_level=cfg.logging_level)
|
|
52
56
|
log_file = Path(SOOTHE_HOME) / "logs" / "cli.log"
|
|
53
57
|
setup_logging(log_level, log_file=log_file)
|
|
@@ -123,60 +127,3 @@ def run_impl(
|
|
|
123
127
|
|
|
124
128
|
typer.echo(f"Error: {format_cli_error(e)}", err=True)
|
|
125
129
|
sys.exit(1)
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
def _merge_mcp_config(cfg: Any, mcp_config_path: str) -> None:
|
|
129
|
-
"""Load MCP server config from a JSON/YAML file and merge into SootheConfig.
|
|
130
|
-
|
|
131
|
-
Args:
|
|
132
|
-
cfg: SootheConfig instance.
|
|
133
|
-
mcp_config_path: Path to MCP config file (JSON or YAML).
|
|
134
|
-
"""
|
|
135
|
-
import json
|
|
136
|
-
|
|
137
|
-
path = Path(mcp_config_path).expanduser().resolve()
|
|
138
|
-
if not path.is_file():
|
|
139
|
-
typer.echo(f"--mcp-config: file not found: {path}", err=True)
|
|
140
|
-
return
|
|
141
|
-
|
|
142
|
-
try:
|
|
143
|
-
raw = path.read_text(encoding="utf-8")
|
|
144
|
-
if path.suffix in (".yml", ".yaml"):
|
|
145
|
-
try:
|
|
146
|
-
import yaml
|
|
147
|
-
|
|
148
|
-
data = yaml.safe_load(raw)
|
|
149
|
-
except ImportError:
|
|
150
|
-
typer.echo("--mcp-config: PyYAML not installed; use JSON format", err=True)
|
|
151
|
-
return
|
|
152
|
-
else:
|
|
153
|
-
data = json.loads(raw)
|
|
154
|
-
except Exception as e:
|
|
155
|
-
typer.echo(f"--mcp-config: failed to parse {path}: {e}", err=True)
|
|
156
|
-
return
|
|
157
|
-
|
|
158
|
-
servers = data.get("mcp_servers") or data.get("servers") or []
|
|
159
|
-
if not isinstance(servers, list):
|
|
160
|
-
typer.echo("--mcp-config: expected 'mcp_servers' or 'servers' list", err=True)
|
|
161
|
-
return
|
|
162
|
-
|
|
163
|
-
from soothe.config.models import MCPServerConfig
|
|
164
|
-
|
|
165
|
-
new_servers: list[MCPServerConfig] = []
|
|
166
|
-
for entry in servers:
|
|
167
|
-
if isinstance(entry, dict):
|
|
168
|
-
try:
|
|
169
|
-
new_servers.append(MCPServerConfig(**entry))
|
|
170
|
-
except Exception as e:
|
|
171
|
-
logger.warning("Skipping invalid MCP server entry: %s", e)
|
|
172
|
-
|
|
173
|
-
if new_servers:
|
|
174
|
-
existing = list(cfg.mcp_servers) if cfg.mcp_servers else []
|
|
175
|
-
existing_names = {s.name for s in existing}
|
|
176
|
-
for s in new_servers:
|
|
177
|
-
if s.name in existing_names:
|
|
178
|
-
logger.warning("--mcp-config: server '%s' already in config, skipping", s.name)
|
|
179
|
-
else:
|
|
180
|
-
existing.append(s)
|
|
181
|
-
cfg.mcp_servers = existing
|
|
182
|
-
logger.info("--mcp-config: merged %d MCP server(s)", len(new_servers))
|
|
@@ -12,13 +12,14 @@ import signal
|
|
|
12
12
|
from typing import Any
|
|
13
13
|
|
|
14
14
|
import typer
|
|
15
|
-
from
|
|
15
|
+
from soothe_client import (
|
|
16
16
|
WebSocketClient,
|
|
17
17
|
async_ws_command_client_from_config,
|
|
18
18
|
bootstrap_loop_session,
|
|
19
19
|
connect_websocket_with_retries,
|
|
20
20
|
websocket_url_from_config,
|
|
21
21
|
)
|
|
22
|
+
from soothe_client.appkit import is_loop_scoped_event, unwrap_next
|
|
22
23
|
|
|
23
24
|
from soothe_cli.cli.execution.daemon_errors import (
|
|
24
25
|
friendly_daemon_execution_error,
|
|
@@ -39,46 +40,6 @@ _HEADLESS_WORKER_LOST_RETRIES = 1
|
|
|
39
40
|
_CANCEL_SEND_TIMEOUT_S = 3.0
|
|
40
41
|
|
|
41
42
|
|
|
42
|
-
def _unwrap_next(event: dict[str, Any] | None) -> dict[str, Any] | None:
|
|
43
|
-
"""Unwrap a protocol-1 ``next`` envelope to its inner streaming frame.
|
|
44
|
-
|
|
45
|
-
Under protocol-1 (RFC-450 §9.3) the daemon wraps free-form streaming
|
|
46
|
-
frames (``event``/``command_response``/card replay) in a
|
|
47
|
-
``{proto, type:"next", payload:{namespace, mode, data}}`` envelope. This
|
|
48
|
-
helper returns the inner ``data`` dict (the legacy frame) so the headless
|
|
49
|
-
loop and EventProcessor can branch on the same fields as before.
|
|
50
|
-
``status``/``error``/``response``/``complete`` are sent raw and pass
|
|
51
|
-
through unchanged.
|
|
52
|
-
"""
|
|
53
|
-
if not isinstance(event, dict):
|
|
54
|
-
return event
|
|
55
|
-
if event.get("type") != "next":
|
|
56
|
-
return event
|
|
57
|
-
payload = event.get("payload")
|
|
58
|
-
if not isinstance(payload, dict):
|
|
59
|
-
return event
|
|
60
|
-
data = payload.get("data")
|
|
61
|
-
return data if isinstance(data, dict) else event
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
def _is_loop_scoped_event(event: dict[str, Any], *, active_loop_id: str) -> bool:
|
|
65
|
-
"""Return whether a daemon frame belongs to the active StrangeLoop session.
|
|
66
|
-
|
|
67
|
-
Unwraps protocol-1 ``next`` envelopes first, then checks ``loop_id`` on the
|
|
68
|
-
inner streaming frame. Non-scoped types (``response``, ``error``,
|
|
69
|
-
``complete``, etc.) are always considered in-scope.
|
|
70
|
-
"""
|
|
71
|
-
event_type = event.get("type", "")
|
|
72
|
-
if event_type == "next":
|
|
73
|
-
inner = _unwrap_next(event)
|
|
74
|
-
if isinstance(inner, dict):
|
|
75
|
-
event_type = inner.get("type", "")
|
|
76
|
-
return event_type not in {"status", "event"} or (inner.get("loop_id") == active_loop_id)
|
|
77
|
-
if event_type not in {"status", "event"}:
|
|
78
|
-
return True
|
|
79
|
-
return event.get("loop_id") == active_loop_id
|
|
80
|
-
|
|
81
|
-
|
|
82
43
|
def _emit_headless_error(message: str) -> None:
|
|
83
44
|
"""Write a user-facing error line to stderr (headless renderer convention)."""
|
|
84
45
|
typer.echo(f"ERROR: {message}", err=True)
|
|
@@ -120,8 +81,6 @@ async def _run_headless_session_once(
|
|
|
120
81
|
max_iterations: int | None = None,
|
|
121
82
|
) -> tuple[int, bool]:
|
|
122
83
|
"""Run one headless daemon session; return ``(exit_code, retry_on_worker_loss)``."""
|
|
123
|
-
from soothe_sdk.client import WebSocketClient
|
|
124
|
-
|
|
125
84
|
ws_url = websocket_url_from_config(cfg)
|
|
126
85
|
client = WebSocketClient(url=ws_url)
|
|
127
86
|
|
|
@@ -248,13 +207,13 @@ async def _run_headless_session_once(
|
|
|
248
207
|
break
|
|
249
208
|
|
|
250
209
|
event_type = event.get("type", "")
|
|
251
|
-
if not
|
|
210
|
+
if not is_loop_scoped_event(event, active_loop_id=active_loop_id):
|
|
252
211
|
continue
|
|
253
212
|
|
|
254
213
|
# Unwrap protocol-1 ``next`` envelopes to the inner streaming frame
|
|
255
214
|
# (RFC-450 §9.3). ``status``/``error`` arrive raw and pass through.
|
|
256
215
|
if event_type == "next":
|
|
257
|
-
inner =
|
|
216
|
+
inner = unwrap_next(event)
|
|
258
217
|
if isinstance(inner, dict):
|
|
259
218
|
event = inner
|
|
260
219
|
event_type = event.get("type", "")
|
|
@@ -288,12 +247,12 @@ async def _run_headless_session_once(
|
|
|
288
247
|
break
|
|
289
248
|
if not nxt:
|
|
290
249
|
break
|
|
291
|
-
if not
|
|
250
|
+
if not is_loop_scoped_event(nxt, active_loop_id=active_loop_id):
|
|
292
251
|
continue
|
|
293
252
|
# Unwrap ``next`` envelopes before handing to the
|
|
294
253
|
# processor so it sees the legacy frame shape.
|
|
295
254
|
if nxt.get("type") == "next":
|
|
296
|
-
inner =
|
|
255
|
+
inner = unwrap_next(nxt)
|
|
297
256
|
if isinstance(inner, dict):
|
|
298
257
|
nxt = inner
|
|
299
258
|
processor.process_event(nxt)
|
|
@@ -10,7 +10,7 @@ from pathlib import Path # noqa: E402
|
|
|
10
10
|
from typing import Annotated # noqa: E402
|
|
11
11
|
|
|
12
12
|
import typer # noqa: E402
|
|
13
|
-
from soothe_sdk.
|
|
13
|
+
from soothe_sdk.paths import SOOTHE_HOME # noqa: E402
|
|
14
14
|
|
|
15
15
|
from soothe_cli.config.cli_config import CLIConfig # noqa: E402
|
|
16
16
|
from soothe_cli.config.loader import set_runtime_config # noqa: E402
|
|
@@ -35,7 +35,7 @@ from soothe_cli.runtime.policy.tui_trace_log import log_tui_trace
|
|
|
35
35
|
from soothe_cli.runtime.presentation.engine import PresentationEngine
|
|
36
36
|
|
|
37
37
|
if TYPE_CHECKING:
|
|
38
|
-
from
|
|
38
|
+
from soothe_client.schemas import Plan
|
|
39
39
|
|
|
40
40
|
from soothe_cli.runtime.presentation.renderer_protocol import RendererProtocol
|
|
41
41
|
|
|
@@ -962,7 +962,7 @@ class EventProcessor:
|
|
|
962
962
|
|
|
963
963
|
def _handle_plan_created(self, data: dict[str, Any]) -> None:
|
|
964
964
|
"""Handle plan creation event."""
|
|
965
|
-
from
|
|
965
|
+
from soothe_client.schemas import Plan, PlanStep
|
|
966
966
|
|
|
967
967
|
steps = [
|
|
968
968
|
PlanStep(
|
|
@@ -11,7 +11,7 @@ from dataclasses import dataclass, field
|
|
|
11
11
|
from typing import TYPE_CHECKING, Any
|
|
12
12
|
|
|
13
13
|
if TYPE_CHECKING:
|
|
14
|
-
from
|
|
14
|
+
from soothe_client.schemas import Plan
|
|
15
15
|
|
|
16
16
|
from soothe_cli.runtime.state.stream_accumulator import StreamingTextAccumulator
|
|
17
17
|
|
|
@@ -5,9 +5,11 @@ from __future__ import annotations
|
|
|
5
5
|
from soothe_sdk.display.tool_result import (
|
|
6
6
|
extract_tool_result_payload,
|
|
7
7
|
infer_tool_output_suggests_error,
|
|
8
|
+
is_error_tool_result_text,
|
|
8
9
|
)
|
|
9
10
|
|
|
10
11
|
__all__ = [
|
|
11
12
|
"extract_tool_result_payload",
|
|
12
13
|
"infer_tool_output_suggests_error",
|
|
14
|
+
"is_error_tool_result_text",
|
|
13
15
|
]
|
|
@@ -24,6 +24,7 @@ from soothe_sdk.ux.task_namespace import (
|
|
|
24
24
|
resolve_task_parent_lookup,
|
|
25
25
|
resolve_task_scope_for_namespace,
|
|
26
26
|
row_key_for_subgraph_tool,
|
|
27
|
+
step_level_parent_task_call_id,
|
|
27
28
|
task_scope_step_id,
|
|
28
29
|
try_bind_namespace_from_tool_call_id,
|
|
29
30
|
)
|
|
@@ -93,6 +94,10 @@ class StepTaskRouter:
|
|
|
93
94
|
_spawns_by_task_id: dict[str, TaskScope] = field(default_factory=dict, repr=False)
|
|
94
95
|
_pending_unscoped_namespaces: deque[tuple[str, ...]] = field(default_factory=deque, repr=False)
|
|
95
96
|
_spawn_recorded: set[tuple[str, str]] = field(default_factory=set, repr=False)
|
|
97
|
+
_task_index_by_step_and_call: dict[tuple[str, str], int] = field(
|
|
98
|
+
default_factory=dict, repr=False
|
|
99
|
+
)
|
|
100
|
+
_next_task_index_by_step: dict[str, int] = field(default_factory=dict, repr=False)
|
|
96
101
|
_pending_main_tools: dict[str, PendingMainTool] = field(default_factory=dict, repr=False)
|
|
97
102
|
_pending_subgraph_tools: dict[tuple[tuple[str, ...], str], PendingSubgraphTool] = field(
|
|
98
103
|
default_factory=dict,
|
|
@@ -108,6 +113,8 @@ class StepTaskRouter:
|
|
|
108
113
|
self._spawns_by_task_id.clear()
|
|
109
114
|
self._pending_unscoped_namespaces.clear()
|
|
110
115
|
self._spawn_recorded.clear()
|
|
116
|
+
self._task_index_by_step_and_call.clear()
|
|
117
|
+
self._next_task_index_by_step.clear()
|
|
111
118
|
self._pending_main_tools.clear()
|
|
112
119
|
self._pending_subgraph_tools.clear()
|
|
113
120
|
|
|
@@ -220,6 +227,11 @@ class StepTaskRouter:
|
|
|
220
227
|
]
|
|
221
228
|
for tcid in drop_tcid:
|
|
222
229
|
self._spawns_by_task_id.pop(tcid, None)
|
|
230
|
+
# Cleanse per-step synthetic task-index state once the step is done.
|
|
231
|
+
self._next_task_index_by_step.pop(sid, None)
|
|
232
|
+
stale_index_keys = [key for key in self._task_index_by_step_and_call if key[0] == sid]
|
|
233
|
+
for key in stale_index_keys:
|
|
234
|
+
self._task_index_by_step_and_call.pop(key, None)
|
|
223
235
|
|
|
224
236
|
def step_id_for_tool(self, tool_call_id: str) -> str:
|
|
225
237
|
"""Return execute step id encoded in a unified root tool_call_id."""
|
|
@@ -287,7 +299,10 @@ class StepTaskRouter:
|
|
|
287
299
|
sid = str(step_id).strip()
|
|
288
300
|
if not sid:
|
|
289
301
|
return False
|
|
290
|
-
normalized_tcid =
|
|
302
|
+
normalized_tcid = self.normalize_task_delegation_id(
|
|
303
|
+
step_id=sid,
|
|
304
|
+
tool_call_id=tcid,
|
|
305
|
+
)
|
|
291
306
|
spawn_key = (sid, normalized_tcid)
|
|
292
307
|
if spawn_key in self._spawn_recorded:
|
|
293
308
|
return False
|
|
@@ -311,6 +326,42 @@ class StepTaskRouter:
|
|
|
311
326
|
)
|
|
312
327
|
return True
|
|
313
328
|
|
|
329
|
+
def normalize_task_delegation_id(self, *, step_id: str, tool_call_id: str) -> str:
|
|
330
|
+
"""Return canonical ``{step}:s:task:{idx}`` for main ``task`` delegations.
|
|
331
|
+
|
|
332
|
+
Some providers emit unified ids like ``{step}:s:call_<uuid>`` that do not
|
|
333
|
+
encode a task index. In that case allocate a stable per-step synthetic
|
|
334
|
+
index so task activity rows and standalone SubAgent cards stay 1:1.
|
|
335
|
+
"""
|
|
336
|
+
sid = str(step_id).strip()
|
|
337
|
+
tcid = str(tool_call_id).strip()
|
|
338
|
+
if not sid or not tcid:
|
|
339
|
+
return tcid
|
|
340
|
+
|
|
341
|
+
normalized = normalize_main_task_delegation_id(sid, tcid, tool_name="task")
|
|
342
|
+
_, type_code, _, tool_info = parse_unified_tool_call_id(normalized)
|
|
343
|
+
if type_code == "s" and isinstance(tool_info, str):
|
|
344
|
+
parts = tool_info.split(":")
|
|
345
|
+
if parts and parts[0] == "task":
|
|
346
|
+
tail = parts[-1] if parts else ""
|
|
347
|
+
if tail.isdigit():
|
|
348
|
+
idx = int(tail)
|
|
349
|
+
key = (sid, tcid)
|
|
350
|
+
self._task_index_by_step_and_call[key] = idx
|
|
351
|
+
next_idx = self._next_task_index_by_step.get(sid, 0)
|
|
352
|
+
if idx >= next_idx:
|
|
353
|
+
self._next_task_index_by_step[sid] = idx + 1
|
|
354
|
+
return normalized
|
|
355
|
+
|
|
356
|
+
key = (sid, tcid)
|
|
357
|
+
if key in self._task_index_by_step_and_call:
|
|
358
|
+
idx = self._task_index_by_step_and_call[key]
|
|
359
|
+
else:
|
|
360
|
+
idx = self._next_task_index_by_step.get(sid, 0)
|
|
361
|
+
self._task_index_by_step_and_call[key] = idx
|
|
362
|
+
self._next_task_index_by_step[sid] = idx + 1
|
|
363
|
+
return step_level_parent_task_call_id(sid, idx)
|
|
364
|
+
|
|
314
365
|
def resolve_task_scope(self, namespace: tuple[str, ...]) -> TaskScope | None:
|
|
315
366
|
"""Task scope for a stream namespace, if bound."""
|
|
316
367
|
return resolve_task_scope_for_namespace(self._namespace_bindings, namespace)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Daemon-backed session helpers for the Textual TUI.
|
|
2
|
+
|
|
3
|
+
Thin CLI wrapper over ``soothe_client.appkit.DaemonSession`` that derives
|
|
4
|
+
WebSocket URL / stream delivery from CLI config and uses the richer CLI early
|
|
5
|
+
chunk filter (LangChain-aware).
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from typing import Any
|
|
11
|
+
|
|
12
|
+
from soothe_client.appkit import DaemonSession
|
|
13
|
+
from soothe_client.helpers import websocket_url_from_config
|
|
14
|
+
|
|
15
|
+
# Re-exported for test monkeypatches / historical import sites.
|
|
16
|
+
from soothe_client.session import ( # noqa: F401
|
|
17
|
+
bootstrap_loop_session,
|
|
18
|
+
connect_websocket_with_retries,
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
from soothe_cli.runtime.state.session_stats import TurnEventStats
|
|
22
|
+
from soothe_cli.runtime.wire.chunk_filter import should_drop_stream_chunk_early
|
|
23
|
+
|
|
24
|
+
# Match headless daemon client post-idle drain window.
|
|
25
|
+
_POST_IDLE_DRAIN_DEADLINE_S = 0.5
|
|
26
|
+
|
|
27
|
+
# Brief close handshake on TUI exit — the daemon cleans up on disconnect anyway.
|
|
28
|
+
TUI_EXIT_HANDSHAKE_TIMEOUT_S = 0.3
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class TuiDaemonSession(DaemonSession):
|
|
32
|
+
"""Own the daemon websocket session used by the TUI."""
|
|
33
|
+
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
cfg: Any,
|
|
37
|
+
*,
|
|
38
|
+
workspace: str | None = None,
|
|
39
|
+
post_idle_drain_deadline: float = _POST_IDLE_DRAIN_DEADLINE_S,
|
|
40
|
+
) -> None:
|
|
41
|
+
self._cfg = cfg
|
|
42
|
+
super().__init__(
|
|
43
|
+
websocket_url_from_config(cfg),
|
|
44
|
+
workspace=workspace,
|
|
45
|
+
stream_delivery=self._resolve_stream_delivery_mode,
|
|
46
|
+
post_idle_drain_deadline=post_idle_drain_deadline,
|
|
47
|
+
early_drop_fn=should_drop_stream_chunk_early,
|
|
48
|
+
stats_factory=TurnEventStats,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
def _resolve_stream_delivery_mode(self) -> str:
|
|
52
|
+
"""Determine stream delivery mode from config (RFC-614).
|
|
53
|
+
|
|
54
|
+
Returns one of ``batch`` | ``adaptive`` | ``streaming``. CLI override
|
|
55
|
+
wins, then config; defaults to ``adaptive``.
|
|
56
|
+
"""
|
|
57
|
+
if (
|
|
58
|
+
self._cfg
|
|
59
|
+
and hasattr(self._cfg, "output_streaming_mode")
|
|
60
|
+
and self._cfg.output_streaming_mode
|
|
61
|
+
):
|
|
62
|
+
return str(self._cfg.output_streaming_mode)
|
|
63
|
+
|
|
64
|
+
if self._cfg and hasattr(self._cfg, "agent"):
|
|
65
|
+
streaming_cfg = self._cfg.agent.loop.output_streaming
|
|
66
|
+
return str(streaming_cfg.mode)
|
|
67
|
+
|
|
68
|
+
return "adaptive"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
# Historical alias used by some call sites / docs.
|
|
72
|
+
DaemonSession = TuiDaemonSession # noqa: F811
|
|
73
|
+
|
|
74
|
+
__all__ = [
|
|
75
|
+
"DaemonSession",
|
|
76
|
+
"TUI_EXIT_HANDSHAKE_TIMEOUT_S",
|
|
77
|
+
"TuiDaemonSession",
|
|
78
|
+
"bootstrap_loop_session",
|
|
79
|
+
"connect_websocket_with_retries",
|
|
80
|
+
]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Decouple daemon stream ingestion from chunk processing and UI application.
|
|
2
|
+
|
|
3
|
+
Canonical implementation lives in ``soothe_client.appkit.turn`` (RFC-629 Layer 1).
|
|
4
|
+
This module re-exports the public API for CLI import stability.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from soothe_client.appkit.turn import (
|
|
10
|
+
_DEFAULT_BATCH_DELAY_MS,
|
|
11
|
+
_DEFAULT_BATCH_SIZE,
|
|
12
|
+
_DEFAULT_INBOUND_MAXSIZE,
|
|
13
|
+
_DEFAULT_OUTBOUND_MAXSIZE,
|
|
14
|
+
PRIORITY_CRITICAL,
|
|
15
|
+
PRIORITY_HIGH,
|
|
16
|
+
PRIORITY_LOW,
|
|
17
|
+
PRIORITY_NORMAL,
|
|
18
|
+
TurnApplyBatcher,
|
|
19
|
+
TurnEventPipeline,
|
|
20
|
+
run_turn_pipeline,
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"PRIORITY_CRITICAL",
|
|
25
|
+
"PRIORITY_HIGH",
|
|
26
|
+
"PRIORITY_LOW",
|
|
27
|
+
"PRIORITY_NORMAL",
|
|
28
|
+
"TurnApplyBatcher",
|
|
29
|
+
"TurnEventPipeline",
|
|
30
|
+
"run_turn_pipeline",
|
|
31
|
+
"_DEFAULT_BATCH_SIZE",
|
|
32
|
+
"_DEFAULT_BATCH_DELAY_MS",
|
|
33
|
+
"_DEFAULT_INBOUND_MAXSIZE",
|
|
34
|
+
"_DEFAULT_OUTBOUND_MAXSIZE",
|
|
35
|
+
]
|