soothe-client-python 1.0.7__tar.gz → 1.0.8__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-1.0.7 → soothe_client_python-1.0.8}/PKG-INFO +1 -1
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/appkit/daemon_session.py +40 -3
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/stream_terminal.py +17 -0
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/.gitignore +0 -0
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/LICENSE +0 -0
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/README.md +0 -0
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/pyproject.toml +0 -0
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/__init__.py +0 -0
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/appkit/__init__.py +0 -0
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/appkit/attachments.py +0 -0
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/appkit/broadcaster.py +0 -0
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/appkit/card_projection.py +0 -0
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/appkit/chunk_filter.py +0 -0
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/appkit/classifier.py +0 -0
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/appkit/events.py +0 -0
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/appkit/loop_session_store.py +0 -0
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/appkit/managed_client.py +0 -0
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/appkit/observability.py +0 -0
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/appkit/pool.py +0 -0
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/appkit/query_gate.py +0 -0
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/appkit/thinking_step.py +0 -0
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/appkit/turn.py +0 -0
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/appkit/turn_boundary.py +0 -0
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/appkit/turn_runner.py +0 -0
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/command_client.py +0 -0
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/errors.py +0 -0
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/helpers.py +0 -0
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/intent_hints.py +0 -0
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/protocol_params.py +0 -0
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/py.typed +0 -0
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/schemas.py +0 -0
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/session.py +0 -0
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/turn_boundary.py +0 -0
- {soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/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: 1.0.
|
|
3
|
+
Version: 1.0.8
|
|
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
|
{soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/appkit/daemon_session.py
RENAMED
|
@@ -20,6 +20,7 @@ from soothe_client.appkit.observability import TurnEventStats
|
|
|
20
20
|
from soothe_client.session import bootstrap_loop_session, connect_websocket_with_retries
|
|
21
21
|
from soothe_client.stream_terminal import (
|
|
22
22
|
STREAM_END,
|
|
23
|
+
is_stream_end_cancel_reason,
|
|
23
24
|
is_turn_end_custom_data,
|
|
24
25
|
is_turn_progress_chunk,
|
|
25
26
|
)
|
|
@@ -81,6 +82,7 @@ class DaemonSession:
|
|
|
81
82
|
self.last_turn_end_state: str | None = None
|
|
82
83
|
self.last_turn_cancellation_seen: bool = False
|
|
83
84
|
self.last_turn_error_message: str | None = None
|
|
85
|
+
self.last_turn_stream_end_reason: str | None = None
|
|
84
86
|
# Last completed turn's seq floor (drop stale prior-turn frames).
|
|
85
87
|
# Scoped to the current peer seq namespace: reset on reconnect / loop
|
|
86
88
|
# change — daemon seq is per-process and restarts after daemon restart.
|
|
@@ -359,6 +361,7 @@ class DaemonSession:
|
|
|
359
361
|
self.last_turn_end_state = None
|
|
360
362
|
self.last_turn_cancellation_seen = False
|
|
361
363
|
self.last_turn_error_message = None
|
|
364
|
+
self.last_turn_stream_end_reason = None
|
|
362
365
|
inbound_dropped_baseline = getattr(self._client, "inbound_dropped", 0)
|
|
363
366
|
query_started = False
|
|
364
367
|
expected_loop_id = self._loop_id
|
|
@@ -555,9 +558,14 @@ class DaemonSession:
|
|
|
555
558
|
yield (namespace, mode, data)
|
|
556
559
|
if mode == "custom" and is_turn_end_custom_data(data):
|
|
557
560
|
custom_type = str(data.get("type", "")).strip()
|
|
558
|
-
|
|
559
|
-
"
|
|
560
|
-
|
|
561
|
+
if custom_type == STREAM_END:
|
|
562
|
+
reason = str(data.get("reason") or "").strip().lower()
|
|
563
|
+
self.last_turn_stream_end_reason = reason or None
|
|
564
|
+
if is_stream_end_cancel_reason(reason):
|
|
565
|
+
self.last_turn_cancellation_seen = True
|
|
566
|
+
self.last_turn_end_state = "stream_end"
|
|
567
|
+
else:
|
|
568
|
+
self.last_turn_end_state = "completed"
|
|
561
569
|
if ev_seq is not None:
|
|
562
570
|
self._last_turn_end_seq = max(self._last_turn_end_seq, ev_seq)
|
|
563
571
|
async for chunk in self._drain_stream_events_after_idle(
|
|
@@ -697,6 +705,35 @@ class DaemonSession:
|
|
|
697
705
|
values: dict[str, Any] = dict(raw) if isinstance(raw, dict) else {}
|
|
698
706
|
return SimpleNamespace(values=values)
|
|
699
707
|
|
|
708
|
+
async def aupdate_loop_state(
|
|
709
|
+
self,
|
|
710
|
+
loop_id: str,
|
|
711
|
+
values: dict[str, Any],
|
|
712
|
+
*,
|
|
713
|
+
timeout: float = 10.0,
|
|
714
|
+
as_node: str | None = None,
|
|
715
|
+
) -> dict[str, Any]:
|
|
716
|
+
"""Merge partial StrangeLoop state via daemon ``loop_state_update``.
|
|
717
|
+
|
|
718
|
+
Used by the TUI for interrupt cleanup and token-total persistence.
|
|
719
|
+
"""
|
|
720
|
+
lid = str(loop_id or "").strip()
|
|
721
|
+
if not lid or not isinstance(values, dict):
|
|
722
|
+
return {}
|
|
723
|
+
|
|
724
|
+
async with self._rpc_lock:
|
|
725
|
+
await self._ensure_rpc_connected()
|
|
726
|
+
try:
|
|
727
|
+
return await self._rpc_client.loop_state_update(
|
|
728
|
+
lid,
|
|
729
|
+
values,
|
|
730
|
+
as_node=as_node,
|
|
731
|
+
timeout=timeout,
|
|
732
|
+
)
|
|
733
|
+
except Exception:
|
|
734
|
+
logger.warning("loop_state_update failed for loop %s", lid[:16], exc_info=True)
|
|
735
|
+
return {}
|
|
736
|
+
|
|
700
737
|
async def fetch_execution_state(self, loop_id: str) -> SimpleNamespace:
|
|
701
738
|
"""Fetch execution-progress snapshot (plan, step_index, iteration, status).
|
|
702
739
|
|
{soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/stream_terminal.py
RENAMED
|
@@ -24,6 +24,16 @@ from soothe_sdk.core.events import (
|
|
|
24
24
|
|
|
25
25
|
TURN_END_CUSTOM_TYPES = frozenset({STREAM_END, STRANGE_LOOP_COMPLETED})
|
|
26
26
|
|
|
27
|
+
# ``soothe.stream.end`` reasons that mean the turn was cancelled (not a clean end).
|
|
28
|
+
STREAM_END_CANCEL_REASONS = frozenset(
|
|
29
|
+
{
|
|
30
|
+
"cancelled",
|
|
31
|
+
"canceled",
|
|
32
|
+
"user_cancelled",
|
|
33
|
+
"client_disconnect",
|
|
34
|
+
}
|
|
35
|
+
)
|
|
36
|
+
|
|
27
37
|
# Customs that prove this turn has real work (not mere intake plan.phase).
|
|
28
38
|
# Intake-only phases must not unlock turn-end — prior-goal stream.end can still
|
|
29
39
|
# arrive after status=running (loop 3e43).
|
|
@@ -64,6 +74,11 @@ def is_turn_end_custom_data(data: Any) -> TypeGuard[dict[str, Any]]:
|
|
|
64
74
|
return True
|
|
65
75
|
|
|
66
76
|
|
|
77
|
+
def is_stream_end_cancel_reason(reason: str | None) -> bool:
|
|
78
|
+
"""True when a ``stream.end`` reason is a cancel/disconnect (not clean end)."""
|
|
79
|
+
return str(reason or "").strip().lower() in STREAM_END_CANCEL_REASONS
|
|
80
|
+
|
|
81
|
+
|
|
67
82
|
def is_turn_progress_chunk(mode: str, data: Any) -> bool:
|
|
68
83
|
"""True when a chunk proves the active turn has non-intake progress.
|
|
69
84
|
|
|
@@ -113,8 +128,10 @@ def stale_pending_frame_label(event: dict[str, Any]) -> str | None:
|
|
|
113
128
|
__all__ = [
|
|
114
129
|
"STALE_TURN_PENDING_TYPES",
|
|
115
130
|
"STREAM_END",
|
|
131
|
+
"STREAM_END_CANCEL_REASONS",
|
|
116
132
|
"STRANGE_LOOP_COMPLETED",
|
|
117
133
|
"TURN_END_CUSTOM_TYPES",
|
|
134
|
+
"is_stream_end_cancel_reason",
|
|
118
135
|
"is_turn_end_custom_data",
|
|
119
136
|
"is_turn_progress_chunk",
|
|
120
137
|
"stale_pending_frame_label",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/appkit/__init__.py
RENAMED
|
File without changes
|
{soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/appkit/attachments.py
RENAMED
|
File without changes
|
{soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/appkit/broadcaster.py
RENAMED
|
File without changes
|
|
File without changes
|
{soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/appkit/chunk_filter.py
RENAMED
|
File without changes
|
{soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/appkit/classifier.py
RENAMED
|
File without changes
|
{soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/appkit/events.py
RENAMED
|
File without changes
|
|
File without changes
|
{soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/appkit/managed_client.py
RENAMED
|
File without changes
|
{soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/appkit/observability.py
RENAMED
|
File without changes
|
|
File without changes
|
{soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/appkit/query_gate.py
RENAMED
|
File without changes
|
{soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/appkit/thinking_step.py
RENAMED
|
File without changes
|
|
File without changes
|
{soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/appkit/turn_boundary.py
RENAMED
|
File without changes
|
{soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/appkit/turn_runner.py
RENAMED
|
File without changes
|
{soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/command_client.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/protocol_params.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{soothe_client_python-1.0.7 → soothe_client_python-1.0.8}/src/soothe_client/turn_boundary.py
RENAMED
|
File without changes
|
|
File without changes
|