soothe-client-python 0.9.9__tar.gz → 0.10.0__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.
Files changed (33) hide show
  1. {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/PKG-INFO +6 -3
  2. {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/README.md +5 -2
  3. soothe_client_python-0.10.0/src/soothe_client/__init__.py +104 -0
  4. {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/__init__.py +9 -44
  5. {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/daemon_session.py +32 -8
  6. soothe_client_python-0.9.9/src/soothe_client/ws_command_client.py → soothe_client_python-0.10.0/src/soothe_client/command_client.py +117 -175
  7. {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/helpers.py +5 -1
  8. {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/protocol_params.py +5 -0
  9. {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/session.py +6 -9
  10. {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/stream_terminal.py +3 -3
  11. {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/turn_boundary.py +4 -3
  12. soothe_client_python-0.9.9/src/soothe_client/__init__.py +0 -194
  13. {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/.gitignore +0 -0
  14. {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/LICENSE +0 -0
  15. {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/pyproject.toml +0 -0
  16. {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/attachments.py +0 -0
  17. {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/broadcaster.py +0 -0
  18. {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/chunk_filter.py +0 -0
  19. {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/classifier.py +0 -0
  20. {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/events.py +0 -0
  21. {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/managed_client.py +0 -0
  22. {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/observability.py +0 -0
  23. {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/pool.py +0 -0
  24. {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/query_gate.py +0 -0
  25. {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/session_store.py +0 -0
  26. {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/thinking_step.py +0 -0
  27. {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/turn.py +0 -0
  28. {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/turn_runner.py +0 -0
  29. {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/errors.py +0 -0
  30. {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/intent_hints.py +0 -0
  31. {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/py.typed +0 -0
  32. {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/schemas.py +0 -0
  33. {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/websocket.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: soothe-client-python
3
- Version: 0.9.9
3
+ Version: 0.10.0
4
4
  Summary: WebSocket client + appkit for soothe-daemon (Python)
5
5
  Project-URL: Homepage, https://github.com/mirasoth/soothe-client-python
6
6
  Project-URL: Documentation, https://soothe.readthedocs.io
@@ -69,10 +69,13 @@ More patterns: [`examples/`](examples/) (hello → streaming → multi-turn →
69
69
 
70
70
  | Need | Use |
71
71
  |------|-----|
72
- | One conversation, stream replies | `DaemonSession` |
72
+ | One conversation, stream replies | `DaemonSession` (`soothe_client.appkit`) |
73
+ | Jobs / cron (async) | `AsyncCommandClient` |
74
+ | Jobs / cron (scripts / sync) | `CommandClient` |
73
75
  | Raw WebSocket / custom RPCs | `WebSocketClient` |
74
76
  | Many users / HTTP backend | `ConnectionPool` + `TurnRunner` |
75
- | Jobs / autopilot / cron | `WsCommandClient` |
77
+
78
+ Wire request param models: `soothe_client.protocol_params`.
76
79
 
77
80
  ## Develop
78
81
 
@@ -36,10 +36,13 @@ More patterns: [`examples/`](examples/) (hello → streaming → multi-turn →
36
36
 
37
37
  | Need | Use |
38
38
  |------|-----|
39
- | One conversation, stream replies | `DaemonSession` |
39
+ | One conversation, stream replies | `DaemonSession` (`soothe_client.appkit`) |
40
+ | Jobs / cron (async) | `AsyncCommandClient` |
41
+ | Jobs / cron (scripts / sync) | `CommandClient` |
40
42
  | Raw WebSocket / custom RPCs | `WebSocketClient` |
41
43
  | Many users / HTTP backend | `ConnectionPool` + `TurnRunner` |
42
- | Jobs / autopilot / cron | `WsCommandClient` |
44
+
45
+ Wire request param models: `soothe_client.protocol_params`.
43
46
 
44
47
  ## Develop
45
48
 
@@ -0,0 +1,104 @@
1
+ """Soothe WebSocket client for talking to a running soothe-daemon.
2
+
3
+ Public surface::
4
+
5
+ from soothe_client import WebSocketClient, is_daemon_live
6
+ from soothe_client import AsyncCommandClient, CommandClient
7
+ from soothe_client.appkit import DaemonSession
8
+
9
+ Wire request param models: ``soothe_client.protocol_params``.
10
+ Session / multi-user helpers: ``soothe_client.appkit``.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import importlib.metadata
16
+
17
+ from soothe_sdk.wire.codec import ProtocolError
18
+
19
+ from soothe_client.command_client import (
20
+ AsyncCommandClient,
21
+ CommandClient,
22
+ async_command_client_from_config,
23
+ command_client_from_config,
24
+ )
25
+ from soothe_client.errors import (
26
+ DaemonError,
27
+ DisconnectCause,
28
+ ReconnectError,
29
+ StaleLoopError,
30
+ disconnect_cause_name,
31
+ )
32
+ from soothe_client.helpers import (
33
+ check_daemon_status,
34
+ connected_websocket,
35
+ fetch_config_section,
36
+ fetch_loop_cards,
37
+ fetch_loop_history,
38
+ fetch_loop_messages,
39
+ fetch_skills_catalog,
40
+ is_daemon_live,
41
+ protocol1_rpc,
42
+ request_daemon_config_reload,
43
+ request_daemon_shutdown,
44
+ websocket_url_from_config,
45
+ )
46
+ from soothe_client.intent_hints import (
47
+ DEFAULT_DELIVERABLE_PHASES,
48
+ EMBED,
49
+ IMAGE_TO_TEXT,
50
+ OCR,
51
+ TEXT_COMPLETION,
52
+ validate_loop_input_intent_hint,
53
+ )
54
+ from soothe_client.session import (
55
+ bootstrap_loop_session,
56
+ connect_websocket_with_retries,
57
+ )
58
+ from soothe_client.websocket import WebSocketClient
59
+
60
+ try:
61
+ __version__ = importlib.metadata.version("soothe-client-python")
62
+ except importlib.metadata.PackageNotFoundError:
63
+ __version__ = "0.0.0"
64
+
65
+ __all__ = [
66
+ "__version__",
67
+ # Transport
68
+ "WebSocketClient",
69
+ "ProtocolError",
70
+ # Errors
71
+ "DaemonError",
72
+ "DisconnectCause",
73
+ "ReconnectError",
74
+ "StaleLoopError",
75
+ "disconnect_cause_name",
76
+ # Command clients
77
+ "AsyncCommandClient",
78
+ "CommandClient",
79
+ "command_client_from_config",
80
+ "async_command_client_from_config",
81
+ # Session bootstrap
82
+ "bootstrap_loop_session",
83
+ "connect_websocket_with_retries",
84
+ # Helpers
85
+ "websocket_url_from_config",
86
+ "connected_websocket",
87
+ "protocol1_rpc",
88
+ "check_daemon_status",
89
+ "is_daemon_live",
90
+ "request_daemon_config_reload",
91
+ "request_daemon_shutdown",
92
+ "fetch_skills_catalog",
93
+ "fetch_config_section",
94
+ "fetch_loop_history",
95
+ "fetch_loop_cards",
96
+ "fetch_loop_messages",
97
+ # Intent hints
98
+ "TEXT_COMPLETION",
99
+ "IMAGE_TO_TEXT",
100
+ "OCR",
101
+ "EMBED",
102
+ "DEFAULT_DELIVERABLE_PHASES",
103
+ "validate_loop_input_intent_hint",
104
+ ]
@@ -1,13 +1,16 @@
1
1
  """Application helpers built on WebSocketClient.
2
2
 
3
- Product-agnostic building blocks for agent UIs and backends:
3
+ Primary building blocks for agent UIs and backends:
4
4
 
5
5
  - ``DaemonSession`` — dual-socket loop session + streamed turns
6
6
  - ``ConnectionPool`` / ``TurnRunner`` — multi-session turn execution
7
7
  - ``QueryGate`` — one-in-flight query per session
8
- - ``EventClassifier`` / ``extract_thinking_step`` — stream → deliverable mapping
8
+ - ``EventClassifier`` — stream → deliverable mapping
9
9
  - ``SSEBroadcaster`` — drop-on-full fan-out to subscribers
10
10
  - ``SessionStore`` — persistence seam (Protocol)
11
+
12
+ Advanced stream/pipeline plumbing lives in submodules
13
+ (``events``, ``chunk_filter``, ``turn``, ``managed_client``).
11
14
  """
12
15
 
13
16
  from __future__ import annotations
@@ -18,7 +21,6 @@ from soothe_client.appkit.attachments import (
18
21
  compact_image_attachment,
19
22
  )
20
23
  from soothe_client.appkit.broadcaster import SSEBroadcaster, SSEEvent
21
- from soothe_client.appkit.chunk_filter import should_drop_stream_chunk_early
22
24
  from soothe_client.appkit.classifier import (
23
25
  EVENT_FINAL_REPORT,
24
26
  ChatEventResult,
@@ -26,17 +28,7 @@ from soothe_client.appkit.classifier import (
26
28
  ClassifierConfig,
27
29
  EventClassifier,
28
30
  )
29
- from soothe_client.appkit.daemon_session import DEFAULT_POST_IDLE_DRAIN_S, DaemonSession
30
- from soothe_client.appkit.events import is_loop_scoped_event, unwrap_next
31
- from soothe_client.appkit.managed_client import (
32
- BootstrapFunc,
33
- ClientFactory,
34
- ManagedClient,
35
- WebSocketManagedClient,
36
- default_bootstrap_func,
37
- default_client_factory,
38
- )
39
- from soothe_client.appkit.observability import TurnEventStats
31
+ from soothe_client.appkit.daemon_session import DaemonSession
40
32
  from soothe_client.appkit.pool import (
41
33
  ConnectionPool,
42
34
  ErrPoolExhausted,
@@ -50,15 +42,6 @@ from soothe_client.appkit.thinking_step import (
50
42
  DEFAULT_THINKING_STEP_EVENTS,
51
43
  extract_thinking_step,
52
44
  )
53
- from soothe_client.appkit.turn import (
54
- PRIORITY_CRITICAL,
55
- PRIORITY_HIGH,
56
- PRIORITY_LOW,
57
- PRIORITY_NORMAL,
58
- TurnApplyBatcher,
59
- TurnEventPipeline,
60
- run_turn_pipeline,
61
- )
62
45
  from soothe_client.appkit.turn_runner import (
63
46
  STREAM_CLOSE_FAIL,
64
47
  STREAM_CLOSE_SOFT_COMPLETE,
@@ -77,13 +60,13 @@ from soothe_client.appkit.turn_runner import (
77
60
  )
78
61
 
79
62
  __all__ = [
80
- "DEFAULT_POST_IDLE_DRAIN_S",
81
63
  "DEFAULT_THINKING_STEP_EVENTS",
82
64
  "STREAM_CLOSE_FAIL",
83
65
  "STREAM_CLOSE_SOFT_COMPLETE",
84
66
  "Attachment",
85
- "BootstrapFunc",
86
- "ClientFactory",
67
+ "ChatEventResult",
68
+ "ChatEventTerminal",
69
+ "ClassifierConfig",
87
70
  "CompactImageOptions",
88
71
  "ConnectionPool",
89
72
  "DaemonSession",
@@ -92,18 +75,10 @@ __all__ = [
92
75
  "ErrPoolExhausted",
93
76
  "ErrQueryBusy",
94
77
  "ErrQueryTimeout",
95
- "ChatEventResult",
96
- "ChatEventTerminal",
97
- "ClassifierConfig",
98
78
  "EventClassifier",
99
79
  "InputOpts",
100
- "ManagedClient",
101
80
  "OnComplete",
102
81
  "OnError",
103
- "PRIORITY_CRITICAL",
104
- "PRIORITY_HIGH",
105
- "PRIORITY_LOW",
106
- "PRIORITY_NORMAL",
107
82
  "PoolConfig",
108
83
  "PooledConn",
109
84
  "QueryGate",
@@ -114,22 +89,12 @@ __all__ = [
114
89
  "SessionStore",
115
90
  "StreamClosePolicy",
116
91
  "TimeoutPolicy",
117
- "TurnApplyBatcher",
118
92
  "TurnConfig",
119
- "TurnEventPipeline",
120
- "TurnEventStats",
121
93
  "TurnRunner",
122
- "WebSocketManagedClient",
123
94
  "compact_attachments",
124
95
  "compact_image_attachment",
125
- "default_bootstrap_func",
126
- "default_client_factory",
127
96
  "default_pool_config",
128
97
  "extract_thinking_step",
129
98
  "idle_timeout_for_turn",
130
99
  "input_message_for_loop",
131
- "is_loop_scoped_event",
132
- "run_turn_pipeline",
133
- "should_drop_stream_chunk_early",
134
- "unwrap_next",
135
100
  ]
@@ -37,7 +37,21 @@ StreamDeliveryResolver = Callable[[], str]
37
37
 
38
38
 
39
39
  class DaemonSession:
40
- """Daemon-backed loop session with stream + RPC sockets."""
40
+ """One conversation with a daemon loop: connect, send turns, stream chunks.
41
+
42
+ Owns a subscribed stream WebSocket plus an RPC sidecar so metadata calls
43
+ do not starve loop events. Typical usage::
44
+
45
+ session = DaemonSession("ws://127.0.0.1:8765")
46
+ await session.connect()
47
+ await session.send_turn("Hello")
48
+ async for _ns, mode, data in session.iter_turn_chunks():
49
+ ...
50
+ await session.close()
51
+
52
+ Advanced: ``early_drop_fn`` / ``stats_factory`` customize stream filtering
53
+ and metrics for product UIs; most apps leave the defaults.
54
+ """
41
55
 
42
56
  def __init__(
43
57
  self,
@@ -67,7 +81,7 @@ class DaemonSession:
67
81
  self.last_turn_end_state: str | None = None
68
82
  self.last_turn_cancellation_seen: bool = False
69
83
  self.last_turn_error_message: str | None = None
70
- # IG-659: last completed turn's seq floor (drop stale prior-turn frames).
84
+ # Last completed turn's seq floor (drop stale prior-turn frames).
71
85
  self._last_turn_end_seq: int = 0
72
86
  self._expected_turn_id: str | None = None
73
87
 
@@ -108,7 +122,11 @@ class DaemonSession:
108
122
  )
109
123
 
110
124
  async def connect(self, *, resume_loop_id: str | None = None) -> dict[str, Any]:
111
- """Connect and bootstrap a daemon loop session."""
125
+ """Open the stream socket and bootstrap (or resume) a loop session.
126
+
127
+ Returns:
128
+ Status event from the daemon (includes ``loop_id`` on success).
129
+ """
112
130
  await connect_websocket_with_retries(self._client)
113
131
  return await self._bootstrap_loop(resume_loop_id=resume_loop_id)
114
132
 
@@ -182,7 +200,7 @@ class DaemonSession:
182
200
  await self._bootstrap_loop(resume_loop_id=resume_loop_id)
183
201
 
184
202
  async def close(self, *, handshake_timeout: float = 2.0) -> None:
185
- """Close stream and RPC sockets (idempotent)."""
203
+ """Close stream and RPC sockets. Safe to call more than once."""
186
204
  if self._closed:
187
205
  return
188
206
  self._closed = True
@@ -219,7 +237,11 @@ class DaemonSession:
219
237
  clarification_answers: list[str] | None = None,
220
238
  intent_hint: str | None = None,
221
239
  ) -> None:
222
- """Send a new user turn to the daemon."""
240
+ """Send a user message on the active loop (start of a turn).
241
+
242
+ Call ``iter_turn_chunks`` afterward to consume the streamed reply.
243
+ Requires ``connect()`` (or another bootstrap) first.
244
+ """
223
245
  if not self._loop_id:
224
246
  raise RuntimeError("No active loop session")
225
247
  await self._client.send_input(
@@ -303,13 +325,15 @@ class DaemonSession:
303
325
  *,
304
326
  max_wait_s: float | None = None,
305
327
  ) -> AsyncIterator[tuple[tuple[Any, ...], str, Any]]:
306
- """Yield ``(namespace, mode, data)`` chunks for the active daemon turn.
328
+ """Yield ``(namespace, mode, data)`` stream chunks until the turn ends.
329
+
330
+ Handles idle timeout, post-idle drain, and turn boundary filtering.
331
+ Call after ``send_turn``.
307
332
 
308
333
  Args:
309
334
  max_wait_s: Optional absolute deadline for the whole turn. When set,
310
335
  raises ``TimeoutError`` if the daemon never emits a turn-end
311
- signal in time (idle after payload, stopped, stream.end, or
312
- strange_loop.completed).
336
+ signal in time.
313
337
  """
314
338
  self.turn_event_stats = self._new_turn_stats()
315
339
  self.last_turn_end_state = None
@@ -1,8 +1,6 @@
1
- """WebSocket command client for daemon command endpoints.
1
+ """Async and sync daemon command clients (jobs, autopilot, cron).
2
2
 
3
- Provides synchronous and async clients for sending commands over WebSocket
4
- and receiving responses. Replaces HTTP REST clients for autopilot, cron,
5
- and memory profiling operations.
3
+ ``AsyncCommandClient`` for asyncio; ``CommandClient`` for scripts/CLI.
6
4
  """
7
5
 
8
6
  from __future__ import annotations
@@ -141,6 +139,7 @@ _AUTOPilot_COMMANDS = {
141
139
  "list_goals": "autopilot_list_goals",
142
140
  "get_goal": "autopilot_get_goal",
143
141
  "cancel_goal": "autopilot_cancel_goal",
142
+ "cancel_all": "autopilot_cancel_all",
144
143
  "wake": "autopilot_wake",
145
144
  "dream": "autopilot_dream",
146
145
  "resume": "autopilot_resume",
@@ -161,20 +160,25 @@ _MEMORY_COMMANDS = {
161
160
  }
162
161
 
163
162
 
164
- class WsCommandClient:
165
- """Async WebSocket client for daemon command endpoints.
163
+ class AsyncCommandClient:
164
+ """Async client for one-shot daemon RPCs (jobs, autopilot, cron).
166
165
 
167
- Connects to daemon WebSocket, sends command messages, and waits for
168
- response messages with a request/response pattern.
166
+ Prefer this for asyncio apps. For scripts/CLI without an event loop, use
167
+ ``CommandClient``. For streaming agent turns, use ``DaemonSession`` instead.
169
168
 
170
- Usage:
171
- client = WsCommandClient(ws_url)
172
- result = await client.autopilot_status()
173
- result = await client.cron_add("in 1 hour remind me to deploy")
169
+ Job lifecycle uses ``job_*`` methods. Autopilot goal helpers use
170
+ ``autopilot_*``. ``job_cancel`` cancels a root job and its descendants;
171
+ ``autopilot_cancel_all`` cancels every open goal in one call.
172
+
173
+ Example::
174
+
175
+ client = AsyncCommandClient("ws://127.0.0.1:8765")
176
+ created = await client.job_create("Summarize the README")
177
+ await client.job_cancel(created["job_id"])
174
178
 
175
179
  Args:
176
- ws_url: WebSocket URL (e.g. ``ws://127.0.0.1:8765``).
177
- timeout: Command timeout in seconds.
180
+ ws_url: Daemon WebSocket URL (e.g. ``ws://127.0.0.1:8765``).
181
+ timeout: Per-command timeout in seconds.
178
182
  """
179
183
 
180
184
  def __init__(self, ws_url: str, *, timeout: float = 30.0) -> None:
@@ -259,35 +263,43 @@ class WsCommandClient:
259
263
  except Exception as exc:
260
264
  raise RuntimeError(f"Command failed: {exc}") from exc
261
265
 
262
- # Autopilot commands
266
+ # --- Autopilot goals -------------------------------------------------
263
267
 
264
268
  async def autopilot_status(self) -> dict[str, Any]:
265
- """Get autopilot status."""
269
+ """Return autopilot scheduler status (running / dreaming / pool)."""
266
270
  return await self._send_command("autopilot_status")
267
271
 
268
272
  async def autopilot_submit(
269
273
  self, description: str, *, priority: int = 50, workspace: str | None = None
270
274
  ) -> dict[str, Any]:
271
- """Submit a new autopilot task."""
275
+ """Submit a new autopilot goal (returns ``goal_id``)."""
272
276
  payload: dict[str, Any] = {"description": description, "priority": priority}
273
277
  if workspace:
274
278
  payload["workspace"] = workspace
275
279
  return await self._send_command("autopilot_submit", payload)
276
280
 
277
281
  async def autopilot_list_goals(self) -> dict[str, Any]:
278
- """List all goals."""
282
+ """List all goals (including non-root children)."""
279
283
  return await self._send_command("autopilot_list_goals")
280
284
 
281
285
  async def autopilot_get_goal(self, goal_id: str) -> dict[str, Any]:
282
- """Get goal details."""
286
+ """Fetch one goal by id."""
283
287
  return await self._send_command("autopilot_get_goal", {"goal_id": goal_id})
284
288
 
285
289
  async def autopilot_cancel_goal(self, goal_id: str) -> dict[str, Any]:
286
- """Cancel a goal."""
290
+ """Cancel a goal and its non-terminal descendants."""
287
291
  return await self._send_command("autopilot_cancel_goal", {"goal_id": goal_id})
288
292
 
293
+ async def autopilot_cancel_all(self) -> dict[str, Any]:
294
+ """Cancel every open (non-terminal) goal in one call.
295
+
296
+ Prefer ``job_cancel(job_id)`` when you know the job root. Use this when
297
+ you need a bulk stop of leftover pending/active goals.
298
+ """
299
+ return await self._send_command("autopilot_cancel_all")
300
+
289
301
  async def autopilot_wake(self) -> dict[str, Any]:
290
- """Exit dreaming mode."""
302
+ """Exit dreaming mode and resume scheduling."""
291
303
  return await self._send_command("autopilot_wake")
292
304
 
293
305
  async def autopilot_dream(self) -> dict[str, Any]:
@@ -295,26 +307,26 @@ class WsCommandClient:
295
307
  return await self._send_command("autopilot_dream")
296
308
 
297
309
  async def autopilot_resume(self, goal_id: str) -> dict[str, Any]:
298
- """Resume a suspended/blocked goal."""
310
+ """Resume a suspended or blocked goal."""
299
311
  return await self._send_command("autopilot_resume", {"goal_id": goal_id})
300
312
 
301
313
  async def autopilot_list_jobs(self) -> dict[str, Any]:
302
- """List root goals (jobs) only."""
314
+ """List root goals only (jobs). Prefer ``job_*`` for job control."""
303
315
  return await self._send_command("autopilot_list_jobs")
304
316
 
305
317
  async def autopilot_get_job(self, job_id: str) -> dict[str, Any]:
306
- """Get job status with DAG snapshot."""
318
+ """Get a root job with DAG snapshot. Prefer ``job_status`` / ``job_dag``."""
307
319
  return await self._send_command("autopilot_get_job", {"job_id": job_id})
308
320
 
309
321
  async def autopilot_subscribe(self) -> dict[str, Any]:
310
- """Subscribe to autopilot worker events."""
322
+ """Subscribe this connection to autopilot worker events."""
311
323
  return await self._send_command("autopilot_subscribe")
312
324
 
313
325
  async def autopilot_unsubscribe(self) -> dict[str, Any]:
314
326
  """Unsubscribe from autopilot worker events."""
315
327
  return await self._send_command("autopilot_unsubscribe")
316
328
 
317
- # canonical job commands (recommended)
329
+ # --- Jobs (preferred for job lifecycle) ------------------------------
318
330
 
319
331
  async def job_create(
320
332
  self,
@@ -325,18 +337,7 @@ class WsCommandClient:
325
337
  max_iterations: int | None = None,
326
338
  guidance: str | None = None,
327
339
  ) -> dict[str, Any]:
328
- """Create a new autopilot job.
329
-
330
- Args:
331
- goal: Job goal text (required).
332
- workspace: Optional workspace path.
333
- autonomous: Whether to run autonomously.
334
- max_iterations: Optional iteration limit.
335
- guidance: Optional initial guidance.
336
-
337
- Returns:
338
- Dict with job_id and status.
339
- """
340
+ """Create a background job (root goal). Returns ``job_id`` + status."""
340
341
  payload: dict[str, Any] = {"goal": goal}
341
342
  if workspace:
342
343
  payload["workspace"] = workspace
@@ -349,79 +350,35 @@ class WsCommandClient:
349
350
  return await self._send_command("job_create", payload)
350
351
 
351
352
  async def job_status(self, job_id: str) -> dict[str, Any]:
352
- """Get job status with goal counts and workers.
353
-
354
- Args:
355
- job_id: Job identifier.
356
-
357
- Returns:
358
- Dict with job_id, status, active_goals, completed_goals, workers.
359
- """
353
+ """Get job status with goal counts and workers."""
360
354
  return await self._send_command("job_status", {"job_id": job_id})
361
355
 
362
356
  async def job_pause(self, job_id: str) -> dict[str, Any]:
363
- """Pause a running job.
364
-
365
- Args:
366
- job_id: Job identifier.
367
-
368
- Returns:
369
- Dict with job_id and status="suspended".
370
- """
357
+ """Pause a running job (status becomes suspended)."""
371
358
  return await self._send_command("job_pause", {"job_id": job_id})
372
359
 
373
360
  async def job_resume(self, job_id: str) -> dict[str, Any]:
374
- """Resume a paused job.
375
-
376
- Args:
377
- job_id: Job identifier.
378
-
379
- Returns:
380
- Dict with job_id and status="pending".
381
- """
361
+ """Resume a paused job."""
382
362
  return await self._send_command("job_resume", {"job_id": job_id})
383
363
 
384
364
  async def job_cancel(self, job_id: str) -> dict[str, Any]:
385
- """Cancel a job.
386
-
387
- Args:
388
- job_id: Job identifier.
389
-
390
- Returns:
391
- Dict with job_id and status="cancelled".
392
- """
365
+ """Cancel a job root and all non-terminal descendants."""
393
366
  return await self._send_command("job_cancel", {"job_id": job_id})
394
367
 
395
368
  async def job_dag(self, job_id: str) -> dict[str, Any]:
396
- """Get job DAG snapshot for visualization.
397
-
398
- Args:
399
- job_id: Job identifier.
400
-
401
- Returns:
402
- Dict with job_id and dag (nodes/edges).
403
- """
369
+ """Get the job goal DAG (nodes/edges) for visualization."""
404
370
  return await self._send_command("job_dag", {"job_id": job_id})
405
371
 
406
372
  async def job_guidance(
407
373
  self, job_id: str, content: str, *, goal_id: str | None = None
408
374
  ) -> dict[str, Any]:
409
- """Send guidance to a job or specific goal.
410
-
411
- Args:
412
- job_id: Job identifier.
413
- content: Guidance text.
414
- goal_id: Optional specific goal to target.
415
-
416
- Returns:
417
- Dict with job_id, goal_id, absorbed.
418
- """
375
+ """Send guidance text to a job or a specific goal under it."""
419
376
  payload: dict[str, Any] = {"job_id": job_id, "content": content}
420
377
  if goal_id:
421
378
  payload["goal_id"] = goal_id
422
379
  return await self._send_command("job_guidance", payload)
423
380
 
424
- # Cron commands
381
+ # --- Cron ------------------------------------------------------------
425
382
 
426
383
  async def cron_add(self, text: str, *, priority: int | None = None) -> dict[str, Any]:
427
384
  """Submit a natural-language scheduled job."""
@@ -432,75 +389,62 @@ class WsCommandClient:
432
389
  return _normalize_cron_add_result(result)
433
390
 
434
391
  async def cron_list(self, *, status: str | None = None) -> dict[str, Any]:
435
- """List scheduled jobs.
436
-
437
- Sends the ``cron_list`` method, which the daemon routes to
438
- ``_handle_cron_list``. The former ``cron_list_jobs`` method name is
439
- deprecated; it sent a method the daemon did not handle.
440
-
441
- Args:
442
- status: Optional status filter.
443
-
444
- Returns:
445
- Dict with jobs list.
446
- """
392
+ """List scheduled cron jobs (optional ``status`` filter)."""
447
393
  payload: dict[str, Any] = {}
448
394
  if status:
449
395
  payload["status"] = status
450
396
  return await self._send_command("cron_list", payload)
451
397
 
452
398
  async def cron_show(self, job_id: str) -> dict[str, Any]:
453
- """Get job details."""
399
+ """Get details for one cron job."""
454
400
  result = await self._send_command("cron_show", {"job_id": job_id})
455
401
  return _normalize_cron_show_result(result)
456
402
 
457
403
  async def cron_cancel(self, job_id: str) -> dict[str, Any]:
458
- """Cancel a scheduled job."""
404
+ """Cancel a scheduled cron job."""
459
405
  return await self._send_command("cron_cancel", {"job_id": job_id})
460
406
 
461
- # Memory commands
407
+ # --- Memory (admin) --------------------------------------------------
462
408
 
463
409
  async def memory_stats(self, mode: str = "daemon") -> dict[str, Any]:
464
- """Query daemon memory profiling stats."""
410
+ """Query daemon memory profiling stats (admin)."""
465
411
  return await self._send_command("memory_stats", {"mode": mode})
466
412
 
467
413
 
468
- class SyncWsCommandClient:
469
- """Synchronous wrapper for WsCommandClient.
414
+ class CommandClient:
415
+ """Synchronous wrapper around ``AsyncCommandClient`` for scripts and CLI.
470
416
 
471
- Provides a synchronous interface for CLI commands that need to call
472
- daemon endpoints without async context.
417
+ Each method opens a short-lived WebSocket RPC (same behavior as async).
473
418
 
474
419
  Args:
475
- ws_url: WebSocket URL.
476
- timeout: Command timeout in seconds.
420
+ ws_url: Daemon WebSocket URL.
421
+ timeout: Per-command timeout in seconds.
477
422
  """
478
423
 
479
424
  def __init__(self, ws_url: str, *, timeout: float = 30.0) -> None:
480
- self._client = WsCommandClient(ws_url, timeout=timeout)
425
+ self._client = AsyncCommandClient(ws_url, timeout=timeout)
481
426
 
482
427
  def _run_async(self, coro: Any) -> Any:
483
- """Run async coroutine in sync context."""
428
+ """Run an async coroutine from sync code."""
484
429
  try:
485
430
  loop = asyncio.get_running_loop()
486
431
  except RuntimeError:
487
432
  loop = None
488
433
 
489
434
  if loop is not None:
490
- # Already in async context - create task
491
435
  return asyncio.ensure_future(coro)
492
- else:
493
- # Not in async context - run in new loop
494
- return asyncio.run(coro)
436
+ return asyncio.run(coro)
437
+
438
+ # --- Autopilot goals -------------------------------------------------
495
439
 
496
440
  def autopilot_status(self) -> dict[str, Any]:
497
- """Get autopilot status (sync)."""
441
+ """Return autopilot scheduler status."""
498
442
  return cast(dict[str, Any], self._run_async(self._client.autopilot_status()))
499
443
 
500
444
  def autopilot_submit(
501
445
  self, description: str, *, priority: int = 50, workspace: str | None = None
502
446
  ) -> dict[str, Any]:
503
- """Submit a new autopilot task (sync)."""
447
+ """Submit a new autopilot goal."""
504
448
  return cast(
505
449
  dict[str, Any],
506
450
  self._run_async(
@@ -509,47 +453,50 @@ class SyncWsCommandClient:
509
453
  )
510
454
 
511
455
  def autopilot_list_goals(self) -> dict[str, Any]:
512
- """List all goals (sync)."""
456
+ """List all goals."""
513
457
  return cast(dict[str, Any], self._run_async(self._client.autopilot_list_goals()))
514
458
 
515
459
  def autopilot_get_goal(self, goal_id: str) -> dict[str, Any]:
516
- """Get goal details (sync)."""
460
+ """Fetch one goal by id."""
517
461
  return cast(dict[str, Any], self._run_async(self._client.autopilot_get_goal(goal_id)))
518
462
 
519
463
  def autopilot_cancel_goal(self, goal_id: str) -> dict[str, Any]:
520
- """Cancel a goal (sync)."""
464
+ """Cancel a goal and its non-terminal descendants."""
521
465
  return cast(dict[str, Any], self._run_async(self._client.autopilot_cancel_goal(goal_id)))
522
466
 
467
+ def autopilot_cancel_all(self) -> dict[str, Any]:
468
+ """Cancel every open (non-terminal) goal."""
469
+ return cast(dict[str, Any], self._run_async(self._client.autopilot_cancel_all()))
470
+
523
471
  def autopilot_wake(self) -> dict[str, Any]:
524
- """Exit dreaming mode (sync)."""
472
+ """Exit dreaming mode."""
525
473
  return cast(dict[str, Any], self._run_async(self._client.autopilot_wake()))
526
474
 
527
475
  def autopilot_dream(self) -> dict[str, Any]:
528
- """Force dreaming mode (sync)."""
476
+ """Force dreaming mode."""
529
477
  return cast(dict[str, Any], self._run_async(self._client.autopilot_dream()))
530
478
 
531
479
  def autopilot_resume(self, goal_id: str) -> dict[str, Any]:
532
- """Resume a suspended/blocked goal (sync)."""
480
+ """Resume a suspended or blocked goal."""
533
481
  return cast(dict[str, Any], self._run_async(self._client.autopilot_resume(goal_id)))
534
482
 
535
483
  def autopilot_list_jobs(self) -> dict[str, Any]:
536
- """List root goals (jobs) only (sync)."""
484
+ """List root goals only (jobs)."""
537
485
  return cast(dict[str, Any], self._run_async(self._client.autopilot_list_jobs()))
538
486
 
539
487
  def autopilot_get_job(self, job_id: str) -> dict[str, Any]:
540
- """Get job status with DAG snapshot (sync)."""
488
+ """Get a root job with DAG snapshot."""
541
489
  return cast(dict[str, Any], self._run_async(self._client.autopilot_get_job(job_id)))
542
490
 
543
- def job_pause(self, job_id: str) -> dict[str, Any]:
544
- """Pause a running autopilot job (sync)."""
545
- return cast(dict[str, Any], self._run_async(self._client.job_pause(job_id)))
491
+ def autopilot_subscribe(self) -> dict[str, Any]:
492
+ """Subscribe to autopilot worker events."""
493
+ return cast(dict[str, Any], self._run_async(self._client.autopilot_subscribe()))
546
494
 
547
- def job_guidance(self, job_id: str, text: str, *, goal_id: str | None = None) -> dict[str, Any]:
548
- """Send guidance to an autopilot job or specific goal (sync)."""
549
- return cast(
550
- dict[str, Any],
551
- self._run_async(self._client.job_guidance(job_id, text, goal_id=goal_id)),
552
- )
495
+ def autopilot_unsubscribe(self) -> dict[str, Any]:
496
+ """Unsubscribe from autopilot worker events."""
497
+ return cast(dict[str, Any], self._run_async(self._client.autopilot_unsubscribe()))
498
+
499
+ # --- Jobs ------------------------------------------------------------
553
500
 
554
501
  def job_create(
555
502
  self,
@@ -560,7 +507,7 @@ class SyncWsCommandClient:
560
507
  max_iterations: int | None = None,
561
508
  guidance: str | None = None,
562
509
  ) -> dict[str, Any]:
563
- """Create a new autopilot job."""
510
+ """Create a background job (root goal)."""
564
511
  return cast(
565
512
  dict[str, Any],
566
513
  self._run_async(
@@ -578,76 +525,71 @@ class SyncWsCommandClient:
578
525
  """Get job status with goal counts and workers."""
579
526
  return cast(dict[str, Any], self._run_async(self._client.job_status(job_id)))
580
527
 
528
+ def job_pause(self, job_id: str) -> dict[str, Any]:
529
+ """Pause a running job."""
530
+ return cast(dict[str, Any], self._run_async(self._client.job_pause(job_id)))
531
+
581
532
  def job_resume(self, job_id: str) -> dict[str, Any]:
582
- """Resume a paused autopilot job."""
533
+ """Resume a paused job."""
583
534
  return cast(dict[str, Any], self._run_async(self._client.job_resume(job_id)))
584
535
 
585
536
  def job_cancel(self, job_id: str) -> dict[str, Any]:
586
- """Cancel an autopilot job."""
537
+ """Cancel a job root and all non-terminal descendants."""
587
538
  return cast(dict[str, Any], self._run_async(self._client.job_cancel(job_id)))
588
539
 
589
540
  def job_dag(self, job_id: str) -> dict[str, Any]:
590
- """Get job DAG snapshot for visualization."""
541
+ """Get the job goal DAG for visualization."""
591
542
  return cast(dict[str, Any], self._run_async(self._client.job_dag(job_id)))
592
543
 
593
- def autopilot_subscribe(self) -> dict[str, Any]:
594
- """Subscribe to autopilot worker events (sync)."""
595
- return cast(dict[str, Any], self._run_async(self._client.autopilot_subscribe()))
544
+ def job_guidance(
545
+ self, job_id: str, content: str, *, goal_id: str | None = None
546
+ ) -> dict[str, Any]:
547
+ """Send guidance text to a job or a specific goal under it."""
548
+ return cast(
549
+ dict[str, Any],
550
+ self._run_async(self._client.job_guidance(job_id, content, goal_id=goal_id)),
551
+ )
596
552
 
597
- def autopilot_unsubscribe(self) -> dict[str, Any]:
598
- """Unsubscribe from autopilot worker events (sync)."""
599
- return cast(dict[str, Any], self._run_async(self._client.autopilot_unsubscribe()))
553
+ # --- Cron ------------------------------------------------------------
600
554
 
601
555
  def cron_add(self, text: str, *, priority: int | None = None) -> dict[str, Any]:
602
- """Submit a natural-language scheduled job (sync)."""
556
+ """Submit a natural-language scheduled job."""
603
557
  return cast(dict[str, Any], self._run_async(self._client.cron_add(text, priority=priority)))
604
558
 
605
559
  def cron_list(self, *, status: str | None = None) -> dict[str, Any]:
606
- """List scheduled jobs (sync)."""
560
+ """List scheduled cron jobs."""
607
561
  return cast(dict[str, Any], self._run_async(self._client.cron_list(status=status)))
608
562
 
609
563
  def cron_show(self, job_id: str) -> dict[str, Any]:
610
- """Get job details (sync)."""
564
+ """Get details for one cron job."""
611
565
  return cast(dict[str, Any], self._run_async(self._client.cron_show(job_id)))
612
566
 
613
567
  def cron_cancel(self, job_id: str) -> dict[str, Any]:
614
- """Cancel a scheduled job (sync)."""
568
+ """Cancel a scheduled cron job."""
615
569
  return cast(dict[str, Any], self._run_async(self._client.cron_cancel(job_id)))
616
570
 
571
+ # --- Memory (admin) --------------------------------------------------
572
+
617
573
  def memory_stats(self, mode: str = "daemon") -> dict[str, Any]:
618
- """Query daemon memory profiling stats (sync)."""
574
+ """Query daemon memory profiling stats (admin)."""
619
575
  return cast(dict[str, Any], self._run_async(self._client.memory_stats(mode)))
620
576
 
621
577
 
622
- def ws_command_client_from_config(cfg: Any) -> SyncWsCommandClient:
623
- """Build a WebSocket command client from CLI or soothe config.
624
-
625
- Args:
626
- cfg: CLI, daemon, or soothe config exposing websocket host/port.
627
-
628
- Returns:
629
- SyncWsCommandClient instance.
630
- """
578
+ def command_client_from_config(cfg: Any) -> CommandClient:
579
+ """Build a sync command client from soothe/CLI config (host/port)."""
631
580
  ws_url = websocket_url_from_config(cfg)
632
- return SyncWsCommandClient(ws_url)
633
-
581
+ return CommandClient(ws_url)
634
582
 
635
- def async_ws_command_client_from_config(cfg: Any) -> WsCommandClient:
636
- """Build an async WebSocket command client from config.
637
583
 
638
- Args:
639
- cfg: CLI, daemon, or soothe config exposing websocket host/port.
640
-
641
- Returns:
642
- WsCommandClient instance.
643
- """
584
+ def async_command_client_from_config(cfg: Any) -> AsyncCommandClient:
585
+ """Build an async command client from soothe/CLI config (host/port)."""
644
586
  ws_url = websocket_url_from_config(cfg)
645
- return WsCommandClient(ws_url)
587
+ return AsyncCommandClient(ws_url)
646
588
 
647
589
 
648
590
  __all__ = [
649
- "WsCommandClient",
650
- "SyncWsCommandClient",
651
- "ws_command_client_from_config",
652
- "async_ws_command_client_from_config",
591
+ "AsyncCommandClient",
592
+ "CommandClient",
593
+ "command_client_from_config",
594
+ "async_command_client_from_config",
653
595
  ]
@@ -1,4 +1,8 @@
1
- """WebSocket helper functions for daemon communication."""
1
+ """Small WebSocket helpers for CLI and scripts (status, fetch, one-shot RPC).
2
+
3
+ For interactive turns use ``DaemonSession``. For jobs/cron use
4
+ ``AsyncCommandClient`` / ``CommandClient``.
5
+ """
2
6
 
3
7
  from __future__ import annotations
4
8
 
@@ -32,6 +32,7 @@ __all__ = [
32
32
  "AutopilotListGoalsParams",
33
33
  "AutopilotGetGoalParams",
34
34
  "AutopilotCancelGoalParams",
35
+ "AutopilotCancelAllParams",
35
36
  "AutopilotWakeParams",
36
37
  "AutopilotDreamParams",
37
38
  "AutopilotResumeParams",
@@ -363,6 +364,10 @@ class AutopilotCancelGoalParams(ParamsBase):
363
364
  goal_id: str = Field(..., min_length=1)
364
365
 
365
366
 
367
+ class AutopilotCancelAllParams(EmptyParams):
368
+ """Params for ``method=autopilot_cancel_all`` — no required fields."""
369
+
370
+
366
371
  class AutopilotWakeParams(EmptyParams):
367
372
  """Params for ``method=autopilot_wake`` — no required fields."""
368
373
 
@@ -1,4 +1,8 @@
1
- """Shared WebSocket session bootstrap for CLI headless and TUI."""
1
+ """Session bootstrap helpers for CLI, TUI, and appkit.
2
+
3
+ Prefer ``DaemonSession`` for application code. These helpers are the lower-level
4
+ connect + loop-subscribe steps used when you own the ``WebSocketClient``.
5
+ """
2
6
 
3
7
  from __future__ import annotations
4
8
 
@@ -22,14 +26,7 @@ _LEGACY_LOOP_AUTOPILOT_MODE = "solo"
22
26
 
23
27
 
24
28
  async def connect_websocket_with_retries(client: Any) -> None:
25
- """Connect to the daemon with bounded retries for cold-start races.
26
-
27
- Args:
28
- client: WebSocketClient instance.
29
-
30
- Raises:
31
- ConnectionError: If connection fails after all retries.
32
- """
29
+ """Connect a ``WebSocketClient`` with retries (daemon cold-start safe)."""
33
30
  last_error: OSError | ConnectionError | TimeoutError | None = None
34
31
  for attempt in range(_CONNECT_RETRY_COUNT):
35
32
  try:
@@ -1,7 +1,7 @@
1
- """Shared stream/turn terminal frame helpers for WebSocket and DaemonSession.
1
+ """Internal stream/turn terminal frame helpers.
2
2
 
3
- Keeps peel-at-turn-start and turn-end detection on one vocabulary so leftover
4
- prior-goal terminals cannot blank the next TUI query.
3
+ Used by ``WebSocketClient`` and ``DaemonSession`` so leftover prior-turn
4
+ terminals cannot blank the next query. Not part of the community public API.
5
5
  """
6
6
 
7
7
  from __future__ import annotations
@@ -1,7 +1,8 @@
1
- """Turn / stream boundary helpers (IG-659).
1
+ """Internal turn / stream boundary helpers.
2
2
 
3
- ``turn_id`` correlates a user turn across status, events, and terminals.
4
- ``seq`` is a daemon-assigned monotonic counter per loop for drop-stale filtering.
3
+ Used by ``DaemonSession`` and ``WebSocketClient`` for ``turn_id`` / ``seq``
4
+ filtering. Not part of the community public API import only if you are
5
+ extending stream handling.
5
6
  """
6
7
 
7
8
  from __future__ import annotations
@@ -1,194 +0,0 @@
1
- """Soothe WebSocket client for talking to a running soothe-daemon.
2
-
3
- Transport helpers (connect, loops, RPCs) live here. Higher-level session
4
- and multi-user turn tools live in ``soothe_client.appkit``.
5
- """
6
-
7
- from __future__ import annotations
8
-
9
- import importlib.metadata
10
-
11
- from soothe_sdk.core.types import VerbosityLevel
12
- from soothe_sdk.wire.codec import ProtocolError
13
-
14
- from soothe_client.errors import (
15
- DaemonError,
16
- DisconnectCause,
17
- ReconnectError,
18
- StaleLoopError,
19
- disconnect_cause_name,
20
- )
21
- from soothe_client.helpers import (
22
- check_daemon_status,
23
- connected_websocket,
24
- fetch_config_section,
25
- fetch_loop_cards,
26
- fetch_loop_history,
27
- fetch_loop_messages,
28
- fetch_skills_catalog,
29
- is_daemon_live,
30
- protocol1_rpc,
31
- request_daemon_config_reload,
32
- request_daemon_shutdown,
33
- websocket_url_from_config,
34
- )
35
- from soothe_client.intent_hints import (
36
- DEFAULT_DELIVERABLE_PHASES,
37
- EMBED,
38
- IMAGE_TO_TEXT,
39
- OCR,
40
- TEXT_COMPLETION,
41
- validate_loop_input_intent_hint,
42
- )
43
- from soothe_client.protocol_params import (
44
- AuthParams,
45
- AuthRefreshParams,
46
- AutopilotCancelGoalParams,
47
- AutopilotDreamParams,
48
- AutopilotGetGoalParams,
49
- AutopilotGetJobParams,
50
- AutopilotListGoalsParams,
51
- AutopilotListJobsParams,
52
- AutopilotResumeParams,
53
- AutopilotStatusParams,
54
- AutopilotSubmitParams,
55
- AutopilotSubscribeParams,
56
- AutopilotWakeParams,
57
- ConfigGetParams,
58
- ConfigReloadParams,
59
- CronAddParams,
60
- CronCancelParams,
61
- CronListParams,
62
- CronShowParams,
63
- DaemonShutdownParams,
64
- DaemonStatusParams,
65
- DisconnectParams,
66
- InvokeSkillParams,
67
- JobCancelParams,
68
- JobCreateParams,
69
- JobDagParams,
70
- JobGuidanceParams,
71
- JobPauseParams,
72
- JobResumeParams,
73
- JobStatusParams,
74
- LoopCardsFetchParams,
75
- LoopDeleteParams,
76
- LoopDetachParams,
77
- LoopGetParams,
78
- LoopInputParams,
79
- LoopListParams,
80
- LoopMessagesParams,
81
- LoopNewParams,
82
- LoopPruneParams,
83
- LoopReattachParams,
84
- LoopStateGetParams,
85
- LoopStateUpdateParams,
86
- LoopTreeParams,
87
- McpStatusParams,
88
- ModelsListParams,
89
- RpcCommandParams,
90
- SkillsListParams,
91
- SlashCommandParams,
92
- SubscribeParams,
93
- )
94
- from soothe_client.session import (
95
- bootstrap_loop_session,
96
- connect_websocket_with_retries,
97
- )
98
- from soothe_client.websocket import WebSocketClient
99
- from soothe_client.ws_command_client import (
100
- SyncWsCommandClient,
101
- WsCommandClient,
102
- async_ws_command_client_from_config,
103
- ws_command_client_from_config,
104
- )
105
-
106
- try:
107
- __version__ = importlib.metadata.version("soothe-client-python")
108
- except importlib.metadata.PackageNotFoundError:
109
- __version__ = "0.0.0"
110
-
111
- __all__ = [
112
- "__version__",
113
- "WebSocketClient",
114
- "VerbosityLevel",
115
- "ProtocolError",
116
- "DaemonError",
117
- "DisconnectCause",
118
- "ReconnectError",
119
- "StaleLoopError",
120
- "disconnect_cause_name",
121
- "WsCommandClient",
122
- "SyncWsCommandClient",
123
- "ws_command_client_from_config",
124
- "async_ws_command_client_from_config",
125
- "bootstrap_loop_session",
126
- "connect_websocket_with_retries",
127
- "websocket_url_from_config",
128
- "connected_websocket",
129
- "protocol1_rpc",
130
- "check_daemon_status",
131
- "is_daemon_live",
132
- "request_daemon_config_reload",
133
- "request_daemon_shutdown",
134
- "fetch_skills_catalog",
135
- "fetch_config_section",
136
- "fetch_loop_history",
137
- "fetch_loop_cards",
138
- "fetch_loop_messages",
139
- "TEXT_COMPLETION",
140
- "IMAGE_TO_TEXT",
141
- "OCR",
142
- "EMBED",
143
- "DEFAULT_DELIVERABLE_PHASES",
144
- "validate_loop_input_intent_hint",
145
- "LoopGetParams",
146
- "LoopListParams",
147
- "LoopTreeParams",
148
- "LoopPruneParams",
149
- "LoopDeleteParams",
150
- "LoopNewParams",
151
- "LoopReattachParams",
152
- "LoopInputParams",
153
- "LoopMessagesParams",
154
- "LoopStateGetParams",
155
- "LoopStateUpdateParams",
156
- "LoopCardsFetchParams",
157
- "LoopDetachParams",
158
- "SubscribeParams",
159
- "AutopilotSubscribeParams",
160
- "AutopilotStatusParams",
161
- "AutopilotSubmitParams",
162
- "AutopilotListGoalsParams",
163
- "AutopilotGetGoalParams",
164
- "AutopilotCancelGoalParams",
165
- "AutopilotWakeParams",
166
- "AutopilotDreamParams",
167
- "AutopilotResumeParams",
168
- "AutopilotListJobsParams",
169
- "AutopilotGetJobParams",
170
- "JobCreateParams",
171
- "JobStatusParams",
172
- "JobPauseParams",
173
- "JobResumeParams",
174
- "JobCancelParams",
175
- "JobDagParams",
176
- "JobGuidanceParams",
177
- "CronAddParams",
178
- "CronListParams",
179
- "CronShowParams",
180
- "CronCancelParams",
181
- "DaemonStatusParams",
182
- "DaemonShutdownParams",
183
- "ConfigGetParams",
184
- "ConfigReloadParams",
185
- "SkillsListParams",
186
- "ModelsListParams",
187
- "InvokeSkillParams",
188
- "McpStatusParams",
189
- "AuthParams",
190
- "AuthRefreshParams",
191
- "SlashCommandParams",
192
- "RpcCommandParams",
193
- "DisconnectParams",
194
- ]