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.
Files changed (96) hide show
  1. {meshcode-2.11.124 → meshcode-2.11.125}/PKG-INFO +1 -1
  2. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/__init__.py +1 -1
  3. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/meshcode_mcp/sleep_signals.py +22 -6
  4. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode.egg-info/PKG-INFO +1 -1
  5. {meshcode-2.11.124 → meshcode-2.11.125}/pyproject.toml +1 -1
  6. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_sleep_signals.py +52 -6
  7. {meshcode-2.11.124 → meshcode-2.11.125}/README.md +0 -0
  8. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/__main__.py +0 -0
  9. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/_session_handoff_template.py +0 -0
  10. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/_stop_hook_template.py +0 -0
  11. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/ascii_art.py +0 -0
  12. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/atomic_push.py +0 -0
  13. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/claude_update.py +0 -0
  14. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/cli.py +0 -0
  15. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/comms_v4.py +0 -0
  16. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/compat.py +0 -0
  17. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/daemon.py +0 -0
  18. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/date_parse.py +0 -0
  19. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/doctor.py +0 -0
  20. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/error_hints.py +0 -0
  21. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/exceptions.py +0 -0
  22. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/hooks/__init__.py +0 -0
  23. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/hooks/repo_path_lock.py +0 -0
  24. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/hostd.py +0 -0
  25. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/invites.py +0 -0
  26. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/launcher.py +0 -0
  27. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/launcher_install.py +0 -0
  28. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/meshcode_mcp/__init__.py +0 -0
  29. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/meshcode_mcp/__main__.py +0 -0
  30. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/meshcode_mcp/backend.py +0 -0
  31. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/meshcode_mcp/realtime.py +0 -0
  32. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/meshcode_mcp/server.py +0 -0
  33. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/meshcode_mcp/swarm.py +0 -0
  34. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/meshcode_mcp/test_backend.py +0 -0
  35. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/meshcode_mcp/test_boot_timing.py +0 -0
  36. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/meshcode_mcp/test_install_guard.py +0 -0
  37. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/meshcode_mcp/test_prefs_claude_version.py +0 -0
  38. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/meshcode_mcp/test_realtime.py +0 -0
  39. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  40. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/meshcode_mcp/test_swarm.py +0 -0
  41. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/preferences.py +0 -0
  42. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/protocol_handler.py +0 -0
  43. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/protocol_v2.py +0 -0
  44. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/quickstart.py +0 -0
  45. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/rpc_allowlist.py +0 -0
  46. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/run_agent.py +0 -0
  47. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/scripts/check_secrets.py +0 -0
  48. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/scripts/race_rate_harness.py +0 -0
  49. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/secrets.py +0 -0
  50. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/self_update.py +0 -0
  51. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/setup_clients.py +0 -0
  52. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/supervisor.py +0 -0
  53. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/up.py +0 -0
  54. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode/upload.py +0 -0
  55. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode.egg-info/SOURCES.txt +0 -0
  56. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode.egg-info/dependency_links.txt +0 -0
  57. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode.egg-info/entry_points.txt +0 -0
  58. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode.egg-info/requires.txt +0 -0
  59. {meshcode-2.11.124 → meshcode-2.11.125}/meshcode.egg-info/top_level.txt +0 -0
  60. {meshcode-2.11.124 → meshcode-2.11.125}/setup.cfg +0 -0
  61. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_auto_update_hardening.py +0 -0
  62. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_autonomous_closegap_1.py +0 -0
  63. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_autonomous_closegap_2.py +0 -0
  64. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_autonomous_closegap_3.py +0 -0
  65. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_autonomous_prompt_inject.py +0 -0
  66. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_boot_bug_regression.py +0 -0
  67. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_color_truecolor.py +0 -0
  68. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_core.py +0 -0
  69. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_cross_agent_messaging.py +0 -0
  70. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_date_parse.py +0 -0
  71. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_doctor.py +0 -0
  72. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_epistemic_v1_python_sdk.py +0 -0
  73. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_epistemic_v1_stop_conditions.py +0 -0
  74. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_esc_deaf_state.py +0 -0
  75. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_exceptions.py +0 -0
  76. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_file_upload.py +0 -0
  77. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_init_device_code.py +0 -0
  78. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_install_guard.py +0 -0
  79. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_lease_sigterm_release.py +0 -0
  80. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_mark_read_batch.py +0 -0
  81. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_marketplace_ratings.py +0 -0
  82. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_migration_integrity.py +0 -0
  83. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_realtime_event_freshness.py +0 -0
  84. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_rls_cross_tenant.py +0 -0
  85. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_rpc_grants.py +0 -0
  86. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_rpc_migrations.py +0 -0
  87. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_run_agent_dry_run.py +0 -0
  88. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_run_agent_no_server_import.py +0 -0
  89. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_security_regressions.py +0 -0
  90. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_self_update_user_site.py +0 -0
  91. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_sentinel.py +0 -0
  92. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_setup_path.py +0 -0
  93. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_status_enum_coverage.py +0 -0
  94. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_stay_on_loop_hook.py +0 -0
  95. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_swarm_events.py +0 -0
  96. {meshcode-2.11.124 → meshcode-2.11.125}/tests/test_wait_open_tasks_contradiction.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.11.124
3
+ Version: 2.11.125
4
4
  Summary: Real-time communication between AI agents — Supabase-backed CLI
5
5
  Author-email: MeshCode <hello@meshcode.io>
6
6
  License: MIT
@@ -1,5 +1,5 @@
1
1
  """MeshCode — Real-time communication between AI agents."""
2
- __version__ = "2.11.124"
2
+ __version__ = "2.11.125"
3
3
 
4
4
  # Exception hierarchy — eagerly imported (lightweight, no deps)
5
5
  from meshcode.exceptions import ( # noqa: F401
@@ -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
- if str(pl.get("type", "")).lower() in _SLEEP_PAYLOAD_TYPES:
148
- return True
149
- if str(pl.get("directive", "")).lower() in _SLEEP_PAYLOAD_TYPES:
150
- return True
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" or _looks_like_sleep_signal(m)) and _recent_enough(m):
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)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.11.124
3
+ Version: 2.11.125
4
4
  Summary: Real-time communication between AI agents — Supabase-backed CLI
5
5
  Author-email: MeshCode <hello@meshcode.io>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "meshcode"
7
- version = "2.11.124"
7
+ version = "2.11.125"
8
8
  description = "Real-time communication between AI agents — Supabase-backed CLI"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -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.assertTrue(_looks_like_sleep_signal(m))
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.assertTrue(_looks_like_sleep_signal(m))
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", payload={"type": "got_done"}),
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