muse-code-sdk 1.3.0__py3-none-any.whl
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.
- muse_code/__init__.py +129 -0
- muse_code/connection/__init__.py +49 -0
- muse_code/connection/connection.py +905 -0
- muse_code/connection/discovery.py +55 -0
- muse_code/connection/spawn.py +1067 -0
- muse_code/connection/validation.py +149 -0
- muse_code/errors.py +199 -0
- muse_code/facade/__init__.py +89 -0
- muse_code/facade/approval.py +279 -0
- muse_code/facade/client.py +444 -0
- muse_code/facade/discarded.py +45 -0
- muse_code/facade/gap_fill.py +483 -0
- muse_code/facade/host_death.py +254 -0
- muse_code/facade/session.py +977 -0
- muse_code/facade/turn_handle.py +440 -0
- muse_code/facade/turn_submit.py +289 -0
- muse_code/fold/__init__.py +82 -0
- muse_code/fold/item_store.py +268 -0
- muse_code/fold/session_fold.py +785 -0
- muse_code/fold/state_store.py +141 -0
- muse_code/pending/__init__.py +53 -0
- muse_code/pending/pending_command_set.py +842 -0
- muse_code/py.typed +0 -0
- muse_code/replay.py +144 -0
- muse_code/sync_facade.py +493 -0
- muse_code_sdk-1.3.0.dist-info/METADATA +47 -0
- muse_code_sdk-1.3.0.dist-info/RECORD +30 -0
- muse_code_sdk-1.3.0.dist-info/WHEEL +5 -0
- muse_code_sdk-1.3.0.dist-info/licenses/LICENSE +21 -0
- muse_code_sdk-1.3.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"""The session-state half of the SS4 client fold (spec 638 FR-638-007
|
|
2
|
+
carrying spec 14990 FR-007, INV-005).
|
|
3
|
+
|
|
4
|
+
State events are NOT items: they carry replace-wholesale session facts,
|
|
5
|
+
cursor-ordered, latest-wins (tdd SS4.6). An explicit ``None`` is a fact — it
|
|
6
|
+
clears the family — never "unchanged" (the ``session/goalChanged`` and
|
|
7
|
+
``session/branchChanged`` rules). ``session/tokenUsage`` is the one
|
|
8
|
+
accumulate-only family; its running totals arrive server-computed in
|
|
9
|
+
``cumulative``, so the client stores rather than sums (tdd SS4.6.5).
|
|
10
|
+
|
|
11
|
+
The family KEY is the notification method name — protocol vocabulary treated
|
|
12
|
+
as an opaque string, so a family added additively (tdd SS1.5.4) folds without
|
|
13
|
+
a code change.
|
|
14
|
+
|
|
15
|
+
Absent versus cleared, the Python spelling: ``get`` answers ``None`` for
|
|
16
|
+
both, and ``has`` distinguishes — ``has(family)`` is ``False`` when no fact
|
|
17
|
+
has ever landed and ``True`` when a fact (including an explicit clear)
|
|
18
|
+
holds. Absent is never fabricated (tdd SS4.9.1). (The TS store spells the
|
|
19
|
+
same three states as ``undefined`` / ``null`` / value.)
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
from __future__ import annotations
|
|
23
|
+
|
|
24
|
+
from dataclasses import dataclass
|
|
25
|
+
from typing import Iterable
|
|
26
|
+
|
|
27
|
+
StateValue = object | None
|
|
28
|
+
"""A stored family value. ``None`` is a real value: the family was cleared."""
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclass(frozen=True)
|
|
32
|
+
class StateApplyOutcome:
|
|
33
|
+
"""What one state event changed.
|
|
34
|
+
|
|
35
|
+
Attributes:
|
|
36
|
+
family: The family it targeted.
|
|
37
|
+
previous: The value it replaced (``None`` when absent or cleared;
|
|
38
|
+
``had_previous`` distinguishes).
|
|
39
|
+
had_previous: Whether the family held any fact before this event.
|
|
40
|
+
current: The value now stored.
|
|
41
|
+
applied: ``False`` when the incoming value was refused as an
|
|
42
|
+
exact-cursor replay.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
family: str
|
|
46
|
+
previous: StateValue
|
|
47
|
+
had_previous: bool
|
|
48
|
+
current: StateValue
|
|
49
|
+
applied: bool
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
class SessionStateStore:
|
|
53
|
+
"""Last-write-wins per family, in arrival order.
|
|
54
|
+
|
|
55
|
+
Cursors are opaque strings and MUST NOT be parsed or ordered (tdd SS4.1),
|
|
56
|
+
so ordering here is by arrival: the server emits view events in cursor
|
|
57
|
+
order on every connection, and a page's events are ascending. The one
|
|
58
|
+
cursor use is EXACT-EQUALITY replay de-duplication, and it remembers only
|
|
59
|
+
the family's LATEST cursor — it refuses only a back-to-back replay of the
|
|
60
|
+
family's most recent event. The SS4.8 splice caller must skip
|
|
61
|
+
already-applied page events itself, which it does by construction: the
|
|
62
|
+
splice pages forward from ``after`` and discards paged events at cursors
|
|
63
|
+
>= ``next`` as duplicates of the live buffer. No relational comparison
|
|
64
|
+
exists: string order diverges from cursor order at every digit rollover
|
|
65
|
+
("v:s:10" < "v:s:9" as strings), which would drop genuinely newer events.
|
|
66
|
+
"""
|
|
67
|
+
|
|
68
|
+
def __init__(self) -> None:
|
|
69
|
+
"""Create an empty store."""
|
|
70
|
+
self._values: dict[str, StateValue] = {}
|
|
71
|
+
self._cursors: dict[str, str] = {}
|
|
72
|
+
|
|
73
|
+
def apply(
|
|
74
|
+
self, family: str, value: StateValue, cursor: str | None = None
|
|
75
|
+
) -> StateApplyOutcome:
|
|
76
|
+
"""Apply a state event.
|
|
77
|
+
|
|
78
|
+
Args:
|
|
79
|
+
family: The notification method name (opaque vocabulary).
|
|
80
|
+
value: The event's params object, or ``None`` for a clear.
|
|
81
|
+
cursor: The event's ``viewCursor``; when omitted (a snapshot
|
|
82
|
+
seed, which carries one cursor for the whole state) the value
|
|
83
|
+
is taken unconditionally.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
What changed, and whether the event applied.
|
|
87
|
+
"""
|
|
88
|
+
had_previous = family in self._values
|
|
89
|
+
previous = self._values.get(family)
|
|
90
|
+
if cursor is not None:
|
|
91
|
+
seen = self._cursors.get(family)
|
|
92
|
+
# An equal cursor is a replay of the same event (idempotent):
|
|
93
|
+
# refuse. That is the ONLY cursor comparison — cursors are opaque
|
|
94
|
+
# (SS4.1) and any relational order mis-sorts at a digit rollover;
|
|
95
|
+
# arrival order carries the LWW truth.
|
|
96
|
+
if seen is not None and cursor == seen:
|
|
97
|
+
return StateApplyOutcome(
|
|
98
|
+
family=family,
|
|
99
|
+
previous=previous,
|
|
100
|
+
had_previous=had_previous,
|
|
101
|
+
current=previous,
|
|
102
|
+
applied=False,
|
|
103
|
+
)
|
|
104
|
+
self._cursors[family] = cursor
|
|
105
|
+
self._values[family] = value
|
|
106
|
+
return StateApplyOutcome(
|
|
107
|
+
family=family,
|
|
108
|
+
previous=previous,
|
|
109
|
+
had_previous=had_previous,
|
|
110
|
+
current=value,
|
|
111
|
+
applied=True,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
def get(self, family: str) -> StateValue:
|
|
115
|
+
"""Read a family: the held value, or ``None`` when absent or cleared.
|
|
116
|
+
|
|
117
|
+
Pair with :meth:`has` to distinguish never-landed from cleared
|
|
118
|
+
(module docstring, "Absent versus cleared").
|
|
119
|
+
"""
|
|
120
|
+
return self._values.get(family)
|
|
121
|
+
|
|
122
|
+
def has(self, family: str) -> bool:
|
|
123
|
+
"""Whether the family holds any fact (an explicit clear counts)."""
|
|
124
|
+
return family in self._values
|
|
125
|
+
|
|
126
|
+
def families(self) -> list[str]:
|
|
127
|
+
"""Every family that holds a value, in insertion order."""
|
|
128
|
+
return list(self._values)
|
|
129
|
+
|
|
130
|
+
def seed(
|
|
131
|
+
self,
|
|
132
|
+
entries: Iterable[tuple[str, StateValue]],
|
|
133
|
+
cursor: str | None = None,
|
|
134
|
+
) -> None:
|
|
135
|
+
"""Seed from a snapshot's state block: authoritative, replaces wholesale."""
|
|
136
|
+
self._values.clear()
|
|
137
|
+
self._cursors.clear()
|
|
138
|
+
for family, value in entries:
|
|
139
|
+
self._values[family] = value
|
|
140
|
+
if cursor is not None:
|
|
141
|
+
self._cursors[family] = cursor
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"""The SS4.13 ``PendingCommand`` fold (client-local; spec 638 FR-638-008)."""
|
|
2
|
+
|
|
3
|
+
from muse_code.pending.pending_command_set import (
|
|
4
|
+
COMMAND_REJECTED_CODE,
|
|
5
|
+
COMMAND_REJECTED_KIND,
|
|
6
|
+
AbandonedRetirement,
|
|
7
|
+
CommandErrorResponse,
|
|
8
|
+
Held,
|
|
9
|
+
MaterializedRetirement,
|
|
10
|
+
PendingCommandAck,
|
|
11
|
+
PendingCommandEntry,
|
|
12
|
+
PendingCommandSet,
|
|
13
|
+
PendingDisposition,
|
|
14
|
+
PendingJoinPlan,
|
|
15
|
+
PendingRetirement,
|
|
16
|
+
ReclaimedRetirement,
|
|
17
|
+
RejectedRetirement,
|
|
18
|
+
ReplayAck,
|
|
19
|
+
ReplayAnswer,
|
|
20
|
+
ReplayError,
|
|
21
|
+
RetryAbandonedByClientRetirement,
|
|
22
|
+
SnapshotJoinFacts,
|
|
23
|
+
TerminalUnknownRetirement,
|
|
24
|
+
TurnRef,
|
|
25
|
+
UserMessageRef,
|
|
26
|
+
is_settlement,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
__all__ = [
|
|
30
|
+
"COMMAND_REJECTED_CODE",
|
|
31
|
+
"COMMAND_REJECTED_KIND",
|
|
32
|
+
"AbandonedRetirement",
|
|
33
|
+
"CommandErrorResponse",
|
|
34
|
+
"Held",
|
|
35
|
+
"MaterializedRetirement",
|
|
36
|
+
"PendingCommandAck",
|
|
37
|
+
"PendingCommandEntry",
|
|
38
|
+
"PendingCommandSet",
|
|
39
|
+
"PendingDisposition",
|
|
40
|
+
"PendingJoinPlan",
|
|
41
|
+
"PendingRetirement",
|
|
42
|
+
"ReclaimedRetirement",
|
|
43
|
+
"RejectedRetirement",
|
|
44
|
+
"ReplayAck",
|
|
45
|
+
"ReplayAnswer",
|
|
46
|
+
"ReplayError",
|
|
47
|
+
"RetryAbandonedByClientRetirement",
|
|
48
|
+
"SnapshotJoinFacts",
|
|
49
|
+
"TerminalUnknownRetirement",
|
|
50
|
+
"TurnRef",
|
|
51
|
+
"UserMessageRef",
|
|
52
|
+
"is_settlement",
|
|
53
|
+
]
|