meshcode 2.11.124__tar.gz → 2.11.125__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.
- {meshcode-2.11.124 → meshcode-2.11.125}/PKG-INFO +1 -1
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/__init__.py +1 -1
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/meshcode_mcp/sleep_signals.py +22 -6
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode.egg-info/PKG-INFO +1 -1
- {meshcode-2.11.124 → meshcode-2.11.125}/pyproject.toml +1 -1
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_sleep_signals.py +52 -6
- {meshcode-2.11.124 → meshcode-2.11.125}/README.md +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/__main__.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/_session_handoff_template.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/_stop_hook_template.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/ascii_art.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/atomic_push.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/claude_update.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/cli.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/comms_v4.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/compat.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/daemon.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/date_parse.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/doctor.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/error_hints.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/exceptions.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/hooks/__init__.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/hooks/repo_path_lock.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/hostd.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/invites.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/launcher.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/launcher_install.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/meshcode_mcp/server.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/meshcode_mcp/swarm.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/meshcode_mcp/test_boot_timing.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/meshcode_mcp/test_install_guard.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/meshcode_mcp/test_prefs_claude_version.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/meshcode_mcp/test_swarm.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/preferences.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/protocol_handler.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/protocol_v2.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/quickstart.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/rpc_allowlist.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/run_agent.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/scripts/check_secrets.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/scripts/race_rate_harness.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/secrets.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/self_update.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/setup_clients.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/supervisor.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/up.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/upload.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode.egg-info/SOURCES.txt +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode.egg-info/dependency_links.txt +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode.egg-info/entry_points.txt +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode.egg-info/requires.txt +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/meshcode.egg-info/top_level.txt +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/setup.cfg +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_auto_update_hardening.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_autonomous_closegap_1.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_autonomous_closegap_2.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_autonomous_closegap_3.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_autonomous_prompt_inject.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_boot_bug_regression.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_color_truecolor.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_core.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_cross_agent_messaging.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_date_parse.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_doctor.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_epistemic_v1_python_sdk.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_epistemic_v1_stop_conditions.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_esc_deaf_state.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_exceptions.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_file_upload.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_init_device_code.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_install_guard.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_lease_sigterm_release.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_mark_read_batch.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_marketplace_ratings.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_migration_integrity.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_realtime_event_freshness.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_rls_cross_tenant.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_rpc_grants.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_rpc_migrations.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_run_agent_dry_run.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_run_agent_no_server_import.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_security_regressions.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_self_update_user_site.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_sentinel.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_setup_path.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_status_enum_coverage.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_stay_on_loop_hook.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_swarm_events.py +0 -0
- {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_wait_open_tasks_contradiction.py +0 -0
|
@@ -136,18 +136,32 @@ def _is_human_authored(m: Dict[str, Any]) -> bool:
|
|
|
136
136
|
return False
|
|
137
137
|
|
|
138
138
|
|
|
139
|
+
def _sender_may_order_sleep(m: Dict[str, Any]) -> bool:
|
|
140
|
+
"""Samuel directive 2026-06-10 (msg 54eec209): a RUNNING agent is only
|
|
141
|
+
slept by an order from the USER or the COMMANDER. True when the message
|
|
142
|
+
is human-authored OR carries the SERVER-VERIFIED commander stamp
|
|
143
|
+
(payload.sender_is_commander, mig 483 — stamped by mc_send_message, never
|
|
144
|
+
client-claimed). A plain ai peer can no longer must_exit a sibling."""
|
|
145
|
+
if _is_human_authored(m):
|
|
146
|
+
return True
|
|
147
|
+
pl = m.get("payload") or {}
|
|
148
|
+
return isinstance(pl, dict) and pl.get("sender_is_commander") is True
|
|
149
|
+
|
|
150
|
+
|
|
139
151
|
def _looks_like_sleep_signal(m: Dict[str, Any]) -> bool:
|
|
140
152
|
"""Detect mesh messages that authorize the wait-loop exit.
|
|
141
153
|
|
|
142
154
|
See module docstring for the two valid encodings and the rationale
|
|
143
|
-
for ignoring idiom matches from AI-role senders.
|
|
155
|
+
for ignoring idiom matches from AI-role senders. Since mig 483 + .125,
|
|
156
|
+
structured directives additionally require an authorized sender
|
|
157
|
+
(human or server-stamped commander) — see _sender_may_order_sleep.
|
|
144
158
|
"""
|
|
145
159
|
pl = m.get("payload") or {}
|
|
146
160
|
if isinstance(pl, dict):
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
if
|
|
150
|
-
return
|
|
161
|
+
structured = (str(pl.get("type", "")).lower() in _SLEEP_PAYLOAD_TYPES
|
|
162
|
+
or str(pl.get("directive", "")).lower() in _SLEEP_PAYLOAD_TYPES)
|
|
163
|
+
if structured:
|
|
164
|
+
return _sender_may_order_sleep(m)
|
|
151
165
|
text = str(pl.get("text", "")).lower()
|
|
152
166
|
if text and any(marker in text for marker in _SLEEP_TEXT_MARKERS):
|
|
153
167
|
if _is_human_authored(m) and _human_text_is_directive(text):
|
|
@@ -194,7 +208,9 @@ def _split_messages(messages: List[Dict[str, Any]]) -> Dict[str, Any]:
|
|
|
194
208
|
t = m.get("type", "msg")
|
|
195
209
|
if t == "ack":
|
|
196
210
|
acks.append(m)
|
|
197
|
-
elif (t == "done"
|
|
211
|
+
elif ((t == "done" and _sender_may_order_sleep(m)) or _looks_like_sleep_signal(m)) \
|
|
212
|
+
and _recent_enough(m):
|
|
213
|
+
# type='done' rows are sleep-class too — same sender gate applies
|
|
198
214
|
dones.append(m)
|
|
199
215
|
else:
|
|
200
216
|
real.append(m)
|
|
@@ -77,17 +77,23 @@ class TestStructuredDirective(unittest.TestCase):
|
|
|
77
77
|
of sender — these are deliberately-shaped directives."""
|
|
78
78
|
|
|
79
79
|
def test_got_done_broadcast_from_commander(self):
|
|
80
|
+
# post-mig-483 reality: the server stamps sender_is_commander
|
|
80
81
|
m = _msg(sender="mesh-commander", mtype="broadcast",
|
|
81
|
-
payload={"type": "got_done"})
|
|
82
|
+
payload={"type": "got_done", "sender_is_commander": True})
|
|
82
83
|
self.assertTrue(_looks_like_sleep_signal(m))
|
|
83
84
|
|
|
84
85
|
def test_payload_type_sleep_from_ai(self):
|
|
86
|
+
# POLICY CHANGE (54eec209): bare ai sleep directive is ignored
|
|
85
87
|
m = _msg(sender="any-agent", payload={"type": "sleep"})
|
|
86
|
-
self.
|
|
88
|
+
self.assertFalse(_looks_like_sleep_signal(m))
|
|
87
89
|
|
|
88
90
|
def test_payload_directive_shutdown(self):
|
|
91
|
+
# POLICY CHANGE (54eec209): a plain ai peer cannot order shutdown
|
|
89
92
|
m = _msg(sender="any-agent", payload={"directive": "shutdown"})
|
|
90
|
-
self.
|
|
93
|
+
self.assertFalse(_looks_like_sleep_signal(m))
|
|
94
|
+
m2 = _msg(sender="any-agent",
|
|
95
|
+
payload={"directive": "shutdown", "sender_is_commander": True})
|
|
96
|
+
self.assertTrue(_looks_like_sleep_signal(m2))
|
|
91
97
|
|
|
92
98
|
def test_payload_type_unknown_is_not_sleep(self):
|
|
93
99
|
m = _msg(sender="any-agent", payload={"type": "msg"})
|
|
@@ -132,7 +138,7 @@ class TestSplitMessages(unittest.TestCase):
|
|
|
132
138
|
|
|
133
139
|
def test_got_done_broadcast_lands_in_dones(self):
|
|
134
140
|
msgs = [_msg(sender="mesh-commander", mtype="broadcast",
|
|
135
|
-
payload={"type": "got_done"})]
|
|
141
|
+
payload={"type": "got_done", "sender_is_commander": True})]
|
|
136
142
|
out = _split_messages(msgs)
|
|
137
143
|
self.assertEqual(len(out["done_signals"]), 1)
|
|
138
144
|
|
|
@@ -146,7 +152,8 @@ class TestSplitMessages(unittest.TestCase):
|
|
|
146
152
|
msgs = [
|
|
147
153
|
_msg(sender="ai", payload={"text": "a dormir is what Samuel said"}),
|
|
148
154
|
_msg(sender="sammybenu", payload={"text": "a dormir"}),
|
|
149
|
-
_msg(sender="commander", mtype="broadcast",
|
|
155
|
+
_msg(sender="commander", mtype="broadcast",
|
|
156
|
+
payload={"type": "got_done", "sender_is_commander": True}),
|
|
150
157
|
_msg(sender="other", payload={"text": "normal chatter"}),
|
|
151
158
|
_msg(mtype="ack", payload={}),
|
|
152
159
|
]
|
|
@@ -204,5 +211,44 @@ class TestCasualSpanishComplaintGuards(unittest.TestCase):
|
|
|
204
211
|
def test_structured_directive_untouched_by_guards(self):
|
|
205
212
|
# dashboard-button path: payload.type fires regardless of text shape
|
|
206
213
|
m = _msg(sender="mesh-commander",
|
|
207
|
-
payload={"type": "got_done", "text": self.REPRO
|
|
214
|
+
payload={"type": "got_done", "text": self.REPRO,
|
|
215
|
+
"sender_is_commander": True})
|
|
216
|
+
self.assertTrue(_looks_like_sleep_signal(m))
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
class TestOnlyCommanderOrHumanMayOrderSleep(unittest.TestCase):
|
|
220
|
+
"""Samuel directive 2026-06-10 (msg 54eec209) + mig 483: structured sleep
|
|
221
|
+
directives and type='done' messages from a plain AI peer must NOT
|
|
222
|
+
must_exit; only humans or the server-stamped commander may order sleep."""
|
|
223
|
+
|
|
224
|
+
def test_ai_peer_structured_sleep_ignored(self):
|
|
225
|
+
m = _msg(sender="backend2", payload={"type": "got_done", "sender_role": "ai"})
|
|
226
|
+
self.assertFalse(_looks_like_sleep_signal(m))
|
|
227
|
+
|
|
228
|
+
def test_commander_stamp_authorizes(self):
|
|
229
|
+
m = _msg(sender="mesh-commander",
|
|
230
|
+
payload={"type": "got_done", "sender_role": "ai",
|
|
231
|
+
"sender_is_commander": True})
|
|
208
232
|
self.assertTrue(_looks_like_sleep_signal(m))
|
|
233
|
+
|
|
234
|
+
def test_client_claimed_stamp_string_rejected(self):
|
|
235
|
+
# only boolean True (server-stamped) counts
|
|
236
|
+
m = _msg(sender="rogue", payload={"type": "stop", "sender_is_commander": "true"})
|
|
237
|
+
self.assertFalse(_looks_like_sleep_signal(m))
|
|
238
|
+
|
|
239
|
+
def test_human_structured_still_works(self):
|
|
240
|
+
m = _msg(sender="sammybenu", payload={"type": "sleep"})
|
|
241
|
+
self.assertTrue(_looks_like_sleep_signal(m))
|
|
242
|
+
|
|
243
|
+
def test_done_type_from_ai_peer_not_sleep(self):
|
|
244
|
+
m = _msg(sender="backend2", mtype="done",
|
|
245
|
+
payload={"text": "task xyz done", "sender_role": "ai"})
|
|
246
|
+
split = _split_messages([m])
|
|
247
|
+
self.assertEqual(len(split["done_signals"]), 0)
|
|
248
|
+
self.assertEqual(len(split["messages"]), 1)
|
|
249
|
+
|
|
250
|
+
def test_done_type_from_commander_is_sleep(self):
|
|
251
|
+
m = _msg(sender="mesh-commander", mtype="done",
|
|
252
|
+
payload={"text": "got_done", "sender_is_commander": True})
|
|
253
|
+
split = _split_messages([m])
|
|
254
|
+
self.assertEqual(len(split["done_signals"]), 1)
|
|
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
|
|
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
|
|
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
|