macp-sdk-python 0.12.0__tar.gz → 0.13.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.
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/PKG-INFO +1 -1
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/pyproject.toml +1 -1
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk/handoff.py +10 -7
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk/projections.py +4 -1
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk/proposal.py +55 -5
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk/quorum.py +6 -2
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk_python.egg-info/PKG-INFO +1 -1
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/LICENSE +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/README.md +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/setup.cfg +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk/__init__.py +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk/_logging.py +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk/agent/__init__.py +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk/agent/cancel_callback.py +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk/agent/dispatcher.py +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk/agent/participant.py +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk/agent/runner.py +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk/agent/strategies.py +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk/agent/transports.py +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk/agent/types.py +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk/auth.py +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk/base_projection.py +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk/base_session.py +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk/client.py +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk/commitment_hash.py +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk/constants.py +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk/decision.py +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk/envelope.py +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk/errors.py +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk/policy.py +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk/proto_registry.py +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk/py.typed +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk/retry.py +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk/task.py +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk/validation.py +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk/watchers.py +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk_python.egg-info/SOURCES.txt +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk_python.egg-info/dependency_links.txt +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk_python.egg-info/requires.txt +0 -0
- {macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk_python.egg-info/top_level.txt +0 -0
|
@@ -30,9 +30,11 @@ class HandoffRecord:
|
|
|
30
30
|
declined_by: str | None
|
|
31
31
|
# True when the acceptance was an implicit accept synthesized by the
|
|
32
32
|
# runtime (RFC-MACP-0010 §5.1) rather than an explicit client HandoffAccept.
|
|
33
|
-
# Runtime >= 0.8.0 emits these
|
|
34
|
-
#
|
|
35
|
-
#
|
|
33
|
+
# Runtime >= 0.8.0 emits these only for sessions whose bound governance
|
|
34
|
+
# policy declares a non-zero ``acceptance.implicit_accept_timeout_ms``
|
|
35
|
+
# (RFC-MACP-0010 §5.1, RFC-MACP-0012 §4.5), once that timeout elapses
|
|
36
|
+
# unactioned; with no policy bound the timeout resolves to 0 and no
|
|
37
|
+
# implicit accept ever fires. Client-submitted accepts are always
|
|
36
38
|
# ``implicit=False`` (the runtime rejects a forged True).
|
|
37
39
|
implicit: bool = False
|
|
38
40
|
|
|
@@ -177,9 +179,10 @@ class HandoffProjection(BaseProjection):
|
|
|
177
179
|
"""True if *handoff_id* was accepted by a runtime implicit accept.
|
|
178
180
|
|
|
179
181
|
Distinguishes a timeout-driven implicit accept (RFC-MACP-0010 §5.1)
|
|
180
|
-
from an explicit client ``HandoffAccept``. Runtime >= 0.8.0 emits
|
|
181
|
-
|
|
182
|
-
``implicit_accept_timeout_ms`` elapses
|
|
182
|
+
from an explicit client ``HandoffAccept``. Runtime >= 0.8.0 emits one
|
|
183
|
+
only when the session's bound governance policy declares a non-zero
|
|
184
|
+
``acceptance.implicit_accept_timeout_ms`` and that timeout elapses
|
|
185
|
+
unactioned; sessions with no such policy never see one.
|
|
183
186
|
"""
|
|
184
187
|
handoff = self.handoffs.get(handoff_id)
|
|
185
188
|
return handoff is not None and handoff.status == "accepted" and handoff.implicit
|
|
@@ -275,7 +278,7 @@ class HandoffSession(BaseSession):
|
|
|
275
278
|
# ``implicit=true`` for runtime-synthesized accepts and the runtime
|
|
276
279
|
# rejects a client-submitted True. Client accepts always leave the
|
|
277
280
|
# field at its proto3 default (False). See the regression test in
|
|
278
|
-
# tests/unit/
|
|
281
|
+
# tests/unit/test_absorb_runtime_v050.py.
|
|
279
282
|
validate_required_field("handoff_id", handoff_id)
|
|
280
283
|
payload = handoff_pb2.HandoffAcceptPayload(
|
|
281
284
|
handoff_id=handoff_id,
|
|
@@ -132,7 +132,10 @@ class DecisionProjection(BaseProjection):
|
|
|
132
132
|
"""Count votes per proposal, keyed by proposal_id.
|
|
133
133
|
|
|
134
134
|
ABSTAIN votes are tracked but excluded from the totals returned
|
|
135
|
-
here (which counts only APPROVE votes).
|
|
135
|
+
here (which counts only APPROVE votes). A proposal_id that received
|
|
136
|
+
no votes at all is **absent** from the returned dict -- not present
|
|
137
|
+
with a 0 value. Use ``.get(proposal_id, 0)`` when checking an
|
|
138
|
+
arbitrary proposal_id.
|
|
136
139
|
"""
|
|
137
140
|
totals: dict[str, int] = {}
|
|
138
141
|
for proposal_id, sender_votes in self.votes.items():
|
|
@@ -25,11 +25,50 @@ class ProposalRecord:
|
|
|
25
25
|
proposal_id: str
|
|
26
26
|
title: str
|
|
27
27
|
summary: str
|
|
28
|
-
|
|
28
|
+
sender: str
|
|
29
29
|
supersedes: str # "" if original
|
|
30
|
-
|
|
30
|
+
# Reachable values only. An Accept is recorded on the projection's
|
|
31
|
+
# ``accepts`` list (and surfaced via ``accepted_proposal`` /
|
|
32
|
+
# ``is_accepted``), never on this field -- no code path assigns
|
|
33
|
+
# "accepted" here. A non-terminal Reject likewise leaves this "open";
|
|
34
|
+
# only ``terminal=True`` sets "rejected".
|
|
35
|
+
#
|
|
36
|
+
# That is by design, not an omission (issue #112). Acceptance is a
|
|
37
|
+
# per-sender, supersedable relation (RFC-MACP-0008 §5 rule 5), not a
|
|
38
|
+
# per-proposal fact, so a scalar field here cannot hold it: "alice
|
|
39
|
+
# accepts p2 while bob still accepts p1" is a legal state. This mirrors
|
|
40
|
+
# the runtime, whose ``ProposalDisposition`` is {Live, Withdrawn} with
|
|
41
|
+
# acceptance in a separate ``accepts`` map, and typescript-sdk's
|
|
42
|
+
# ``projections/proposal.ts``, which also never assigns "accepted".
|
|
43
|
+
# ``task.py``/``handoff.py`` do set "accepted" because their acceptance
|
|
44
|
+
# is one actor claiming one slot. See docs/modes/proposal.md.
|
|
45
|
+
status: str # "open" | "rejected" | "withdrawn"
|
|
31
46
|
tags: list[str]
|
|
32
47
|
|
|
48
|
+
# ── Deprecated alias (issue #120 / multiagentcoordinationprotocol#177) ──
|
|
49
|
+
# ``proposer`` is kept as a read-only, INSTANCE-level property alias for
|
|
50
|
+
# one minor version, removed at this SDK's next major version. This is a
|
|
51
|
+
# different mechanism from the module-level ``__getattr__``/PEP 562
|
|
52
|
+
# aliases below (``RejectRecord``/``AcceptRecord``, issue #103): those
|
|
53
|
+
# intercept MODULE attribute access (``macp_sdk.proposal.RejectRecord``);
|
|
54
|
+
# PEP 562 has no bearing on INSTANCE attribute access
|
|
55
|
+
# (``some_record.proposer``), which is what this field needs. A
|
|
56
|
+
# ``@dataclass(slots=True)`` class derives ``__slots__`` strictly from
|
|
57
|
+
# its annotated fields, so a plain same-named class attribute alias is
|
|
58
|
+
# not an option here -- but a property is not a field and is untouched
|
|
59
|
+
# by the slots transformation, so it coexists cleanly: ``fields()``,
|
|
60
|
+
# ``__slots__``, ``repr()``, and ``dataclasses.replace()`` all still
|
|
61
|
+
# see/use only the real ``sender`` field. No setter is defined: nothing
|
|
62
|
+
# in this module ever assigns ``.proposer``.
|
|
63
|
+
@property
|
|
64
|
+
def proposer(self) -> str:
|
|
65
|
+
warnings.warn(
|
|
66
|
+
"ProposalRecord.proposer is deprecated; use ProposalRecord.sender instead.",
|
|
67
|
+
DeprecationWarning,
|
|
68
|
+
stacklevel=2,
|
|
69
|
+
)
|
|
70
|
+
return self.sender
|
|
71
|
+
|
|
33
72
|
|
|
34
73
|
@dataclass(slots=True)
|
|
35
74
|
class ProposalRejectRecord:
|
|
@@ -79,7 +118,7 @@ class ProposalProjection(BaseProjection):
|
|
|
79
118
|
proposal_id=p.proposal_id,
|
|
80
119
|
title=p.title,
|
|
81
120
|
summary=p.summary,
|
|
82
|
-
|
|
121
|
+
sender=envelope.sender,
|
|
83
122
|
supersedes="",
|
|
84
123
|
status="open",
|
|
85
124
|
tags=list(p.tags),
|
|
@@ -93,7 +132,7 @@ class ProposalProjection(BaseProjection):
|
|
|
93
132
|
proposal_id=p.proposal_id,
|
|
94
133
|
title=p.title,
|
|
95
134
|
summary=p.summary,
|
|
96
|
-
|
|
135
|
+
sender=envelope.sender,
|
|
97
136
|
supersedes=p.supersedes_proposal_id,
|
|
98
137
|
status="open",
|
|
99
138
|
tags=[],
|
|
@@ -126,9 +165,20 @@ class ProposalProjection(BaseProjection):
|
|
|
126
165
|
)
|
|
127
166
|
if p.terminal:
|
|
128
167
|
rec = self.proposals.get(p.proposal_id)
|
|
168
|
+
# A terminal Reject must name a proposal this projection has
|
|
169
|
+
# actually seen (RFC-MACP-0008 §5 rule 3) before it ends the
|
|
170
|
+
# negotiation. "TerminalRejected" is in TERMINAL_PHASES
|
|
171
|
+
# (agent/participant.py:57-59), so moving phase here for an
|
|
172
|
+
# unknown proposal_id would fire a Participant's on_terminal
|
|
173
|
+
# and tear down its stream for a session that never actually
|
|
174
|
+
# terminated. Same shape as task.py's per-task gates
|
|
175
|
+
# (:172/:196/:212): gate the state transition, keep the
|
|
176
|
+
# audit append above unconditional. has_terminal_rejection()
|
|
177
|
+
# / is_terminally_rejected() deliberately still read
|
|
178
|
+
# self.rejections, not phase, so they stay True either way.
|
|
129
179
|
if rec is not None:
|
|
130
180
|
rec.status = "rejected"
|
|
131
|
-
|
|
181
|
+
self._set_phase("TerminalRejected")
|
|
132
182
|
return
|
|
133
183
|
|
|
134
184
|
if mt == "Withdraw":
|
|
@@ -97,8 +97,12 @@ class QuorumProjection(BaseProjection):
|
|
|
97
97
|
# enforces RFC-MACP-0011 §5 rule 3's *across-type* cardinality rule
|
|
98
98
|
# ("at most one ballot across Approve, Reject, or Abstain"). First
|
|
99
99
|
# ballot per sender stands; which-of-two-stands is inferred from
|
|
100
|
-
# RFC-MACP-0007 §5.3 parity plus what the only runtime enforces
|
|
101
|
-
#
|
|
100
|
+
# RFC-MACP-0007 §5.3 parity plus what the only runtime enforces: the
|
|
101
|
+
# three same-sender ballot guards in macp-runtime's
|
|
102
|
+
# crates/macp-modes/src/mode/quorum.rs (QuorumMode's
|
|
103
|
+
# Approve/Reject/Abstain arms, each rejecting when
|
|
104
|
+
# state.ballots.contains_key(&env.sender), confirmed at runtime
|
|
105
|
+
# v0.8.6) -- RFC-0011 itself is silent on that.
|
|
102
106
|
sender = envelope.sender
|
|
103
107
|
sender_map = self.ballots.setdefault(request_id, {})
|
|
104
108
|
existing = sender_map.get(sender)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk_python.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
{macp_sdk_python-0.12.0 → macp_sdk_python-0.13.0}/src/macp_sdk_python.egg-info/top_level.txt
RENAMED
|
File without changes
|