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.
- {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/PKG-INFO +6 -3
- {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/README.md +5 -2
- soothe_client_python-0.10.0/src/soothe_client/__init__.py +104 -0
- {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/__init__.py +9 -44
- {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/daemon_session.py +32 -8
- 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
- {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/helpers.py +5 -1
- {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/protocol_params.py +5 -0
- {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/session.py +6 -9
- {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/stream_terminal.py +3 -3
- {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/turn_boundary.py +4 -3
- soothe_client_python-0.9.9/src/soothe_client/__init__.py +0 -194
- {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/.gitignore +0 -0
- {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/LICENSE +0 -0
- {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/pyproject.toml +0 -0
- {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/attachments.py +0 -0
- {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/broadcaster.py +0 -0
- {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/chunk_filter.py +0 -0
- {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/classifier.py +0 -0
- {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/events.py +0 -0
- {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/managed_client.py +0 -0
- {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/observability.py +0 -0
- {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/pool.py +0 -0
- {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/query_gate.py +0 -0
- {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/session_store.py +0 -0
- {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/thinking_step.py +0 -0
- {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/turn.py +0 -0
- {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/turn_runner.py +0 -0
- {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/errors.py +0 -0
- {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/intent_hints.py +0 -0
- {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/py.typed +0 -0
- {soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/schemas.py +0 -0
- {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.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
+
]
|
{soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/__init__.py
RENAMED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
"""Application helpers built on WebSocketClient.
|
|
2
2
|
|
|
3
|
-
|
|
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``
|
|
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
|
|
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
|
-
"
|
|
86
|
-
"
|
|
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
|
-
"""
|
|
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
|
-
#
|
|
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
|
-
"""
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
-
"""
|
|
1
|
+
"""Async and sync daemon command clients (jobs, autopilot, cron).
|
|
2
2
|
|
|
3
|
-
|
|
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
|
|
165
|
-
"""Async
|
|
163
|
+
class AsyncCommandClient:
|
|
164
|
+
"""Async client for one-shot daemon RPCs (jobs, autopilot, cron).
|
|
166
165
|
|
|
167
|
-
|
|
168
|
-
|
|
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
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
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:
|
|
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
|
|
266
|
+
# --- Autopilot goals -------------------------------------------------
|
|
263
267
|
|
|
264
268
|
async def autopilot_status(self) -> dict[str, Any]:
|
|
265
|
-
"""
|
|
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
|
|
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
|
-
"""
|
|
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
|
|
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)
|
|
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
|
|
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
|
-
#
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
469
|
-
"""Synchronous wrapper for
|
|
414
|
+
class CommandClient:
|
|
415
|
+
"""Synchronous wrapper around ``AsyncCommandClient`` for scripts and CLI.
|
|
470
416
|
|
|
471
|
-
|
|
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:
|
|
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 =
|
|
425
|
+
self._client = AsyncCommandClient(ws_url, timeout=timeout)
|
|
481
426
|
|
|
482
427
|
def _run_async(self, coro: Any) -> Any:
|
|
483
|
-
"""Run async coroutine
|
|
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
|
-
|
|
493
|
-
|
|
494
|
-
|
|
436
|
+
return asyncio.run(coro)
|
|
437
|
+
|
|
438
|
+
# --- Autopilot goals -------------------------------------------------
|
|
495
439
|
|
|
496
440
|
def autopilot_status(self) -> dict[str, Any]:
|
|
497
|
-
"""
|
|
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
|
|
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
|
|
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
|
-
"""
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
544
|
-
"""
|
|
545
|
-
return cast(dict[str, Any], self._run_async(self._client.
|
|
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
|
|
548
|
-
"""
|
|
549
|
-
return cast(
|
|
550
|
-
|
|
551
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
594
|
-
|
|
595
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
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
|
|
623
|
-
"""Build a
|
|
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
|
|
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
|
-
|
|
639
|
-
|
|
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
|
|
587
|
+
return AsyncCommandClient(ws_url)
|
|
646
588
|
|
|
647
589
|
|
|
648
590
|
__all__ = [
|
|
649
|
-
"
|
|
650
|
-
"
|
|
651
|
-
"
|
|
652
|
-
"
|
|
591
|
+
"AsyncCommandClient",
|
|
592
|
+
"CommandClient",
|
|
593
|
+
"command_client_from_config",
|
|
594
|
+
"async_command_client_from_config",
|
|
653
595
|
]
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
"""WebSocket
|
|
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
|
|
{soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/protocol_params.py
RENAMED
|
@@ -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
|
-
"""
|
|
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
|
|
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:
|
{soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/stream_terminal.py
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Internal stream/turn terminal frame helpers.
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
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
|
{soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/turn_boundary.py
RENAMED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Internal turn / stream boundary helpers.
|
|
2
2
|
|
|
3
|
-
``
|
|
4
|
-
|
|
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
|
-
]
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/attachments.py
RENAMED
|
File without changes
|
{soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/broadcaster.py
RENAMED
|
File without changes
|
{soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/chunk_filter.py
RENAMED
|
File without changes
|
{soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/classifier.py
RENAMED
|
File without changes
|
{soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/events.py
RENAMED
|
File without changes
|
|
File without changes
|
{soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/observability.py
RENAMED
|
File without changes
|
|
File without changes
|
{soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/query_gate.py
RENAMED
|
File without changes
|
{soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/session_store.py
RENAMED
|
File without changes
|
{soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/thinking_step.py
RENAMED
|
File without changes
|
|
File without changes
|
{soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/appkit/turn_runner.py
RENAMED
|
File without changes
|
|
File without changes
|
{soothe_client_python-0.9.9 → soothe_client_python-0.10.0}/src/soothe_client/intent_hints.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|