meshcode 2.11.122__tar.gz → 2.11.123__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 (94) hide show
  1. {meshcode-2.11.122 → meshcode-2.11.123}/PKG-INFO +1 -1
  2. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/__init__.py +1 -1
  3. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/hostd.py +9 -1
  4. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/meshcode_mcp/server.py +21 -0
  5. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/meshcode_mcp/sleep_signals.py +30 -1
  6. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode.egg-info/PKG-INFO +1 -1
  7. {meshcode-2.11.122 → meshcode-2.11.123}/pyproject.toml +1 -1
  8. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_sleep_signals.py +48 -0
  9. {meshcode-2.11.122 → meshcode-2.11.123}/README.md +0 -0
  10. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/__main__.py +0 -0
  11. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/_session_handoff_template.py +0 -0
  12. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/_stop_hook_template.py +0 -0
  13. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/ascii_art.py +0 -0
  14. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/atomic_push.py +0 -0
  15. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/claude_update.py +0 -0
  16. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/cli.py +0 -0
  17. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/comms_v4.py +0 -0
  18. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/compat.py +0 -0
  19. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/daemon.py +0 -0
  20. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/date_parse.py +0 -0
  21. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/doctor.py +0 -0
  22. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/error_hints.py +0 -0
  23. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/exceptions.py +0 -0
  24. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/hooks/__init__.py +0 -0
  25. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/hooks/repo_path_lock.py +0 -0
  26. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/invites.py +0 -0
  27. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/launcher.py +0 -0
  28. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/launcher_install.py +0 -0
  29. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/meshcode_mcp/__init__.py +0 -0
  30. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/meshcode_mcp/__main__.py +0 -0
  31. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/meshcode_mcp/backend.py +0 -0
  32. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/meshcode_mcp/realtime.py +0 -0
  33. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/meshcode_mcp/test_backend.py +0 -0
  34. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/meshcode_mcp/test_boot_timing.py +0 -0
  35. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/meshcode_mcp/test_install_guard.py +0 -0
  36. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/meshcode_mcp/test_prefs_claude_version.py +0 -0
  37. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/meshcode_mcp/test_realtime.py +0 -0
  38. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  39. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/preferences.py +0 -0
  40. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/protocol_handler.py +0 -0
  41. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/protocol_v2.py +0 -0
  42. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/quickstart.py +0 -0
  43. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/rpc_allowlist.py +0 -0
  44. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/run_agent.py +0 -0
  45. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/scripts/check_secrets.py +0 -0
  46. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/scripts/race_rate_harness.py +0 -0
  47. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/secrets.py +0 -0
  48. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/self_update.py +0 -0
  49. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/setup_clients.py +0 -0
  50. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/supervisor.py +0 -0
  51. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/up.py +0 -0
  52. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/upload.py +0 -0
  53. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode.egg-info/SOURCES.txt +0 -0
  54. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode.egg-info/dependency_links.txt +0 -0
  55. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode.egg-info/entry_points.txt +0 -0
  56. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode.egg-info/requires.txt +0 -0
  57. {meshcode-2.11.122 → meshcode-2.11.123}/meshcode.egg-info/top_level.txt +0 -0
  58. {meshcode-2.11.122 → meshcode-2.11.123}/setup.cfg +0 -0
  59. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_auto_update_hardening.py +0 -0
  60. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_autonomous_closegap_1.py +0 -0
  61. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_autonomous_closegap_2.py +0 -0
  62. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_autonomous_closegap_3.py +0 -0
  63. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_autonomous_prompt_inject.py +0 -0
  64. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_boot_bug_regression.py +0 -0
  65. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_color_truecolor.py +0 -0
  66. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_core.py +0 -0
  67. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_cross_agent_messaging.py +0 -0
  68. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_date_parse.py +0 -0
  69. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_doctor.py +0 -0
  70. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_epistemic_v1_python_sdk.py +0 -0
  71. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_epistemic_v1_stop_conditions.py +0 -0
  72. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_esc_deaf_state.py +0 -0
  73. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_exceptions.py +0 -0
  74. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_file_upload.py +0 -0
  75. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_init_device_code.py +0 -0
  76. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_install_guard.py +0 -0
  77. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_lease_sigterm_release.py +0 -0
  78. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_mark_read_batch.py +0 -0
  79. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_marketplace_ratings.py +0 -0
  80. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_migration_integrity.py +0 -0
  81. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_realtime_event_freshness.py +0 -0
  82. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_rls_cross_tenant.py +0 -0
  83. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_rpc_grants.py +0 -0
  84. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_rpc_migrations.py +0 -0
  85. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_run_agent_dry_run.py +0 -0
  86. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_run_agent_no_server_import.py +0 -0
  87. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_security_regressions.py +0 -0
  88. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_self_update_user_site.py +0 -0
  89. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_sentinel.py +0 -0
  90. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_setup_path.py +0 -0
  91. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_status_enum_coverage.py +0 -0
  92. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_stay_on_loop_hook.py +0 -0
  93. {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_swarm_events.py +0 -0
  94. {meshcode-2.11.122 → meshcode-2.11.123}/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.122
3
+ Version: 2.11.123
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.122"
2
+ __version__ = "2.11.123"
3
3
 
4
4
  # Exception hierarchy — eagerly imported (lightweight, no deps)
5
5
  from meshcode.exceptions import ( # noqa: F401
@@ -467,7 +467,15 @@ def _do_respawns(api_key: str, host_id: str) -> int:
467
467
  if not _BOOT_AUTOSTART:
468
468
  _spawn_age = c.get("spawned_age_s")
469
469
  _hostd_uptime = time.time() - _HOSTD_STARTED_AT
470
- if _spawn_age is None or _spawn_age >= _hostd_uptime:
470
+ # ALIVE-ON-OUR-WATCH bypass (task baefc8ab part C — live miss 2026-06-10T00:31Z:
471
+ # front-end exited for respawn minutes after a hostd restart; its spawned_at predated
472
+ # the restart, the gate read it as a boot-stale leftover and ATE the respawn until a
473
+ # manual Start). An agent that HEARTBEATED during this daemon's lifetime is not a
474
+ # reboot leftover — it lived and died on our watch, so its respawn is legitimate.
475
+ # A true boot-stale leftover has heartbeat_age_s >= uptime (or None).
476
+ _hb_age = c.get("heartbeat_age_s")
477
+ _alive_on_our_watch = _hb_age is not None and _hb_age < _hostd_uptime
478
+ if (_spawn_age is None or _spawn_age >= _hostd_uptime) and not _alive_on_our_watch:
471
479
  _log(f"BOOT-AUTOSTART OFF: skip auto-respawn {proj}/{agent} (desired_state=running set "
472
480
  f"before this hostd start — boot-stale leftover; explicit launch required). "
473
481
  f"Set MESHCODE_BOOT_AUTOSTART=1 to auto-launch at boot.")
@@ -7445,6 +7445,27 @@ def meshcode_auto_wake_toggle(enabled: bool) -> Dict[str, Any]:
7445
7445
  }
7446
7446
 
7447
7447
 
7448
+ @mcp.tool()
7449
+ def meshcode_mesh_keep_alive(enabled: bool) -> Dict[str, Any]:
7450
+ """Toggle "mesh prendida" keep-alive for THIS meshwork (task baefc8ab,
7451
+ Samuel 2026-06-10: "quiero dejar la mesh prendida sin tasks — que se
7452
+ mantengan atentos a cualquier mensaje").
7453
+
7454
+ ON: the server sweep (mig 479) WAKES a stopped agent within ~5 min of an
7455
+ unread direct message arriving — sleep stays cheap (zero idle tokens) but
7456
+ the mesh answers. Explicit human Stops are still respected; agents offline
7457
+ >7 days are never resurrected. OFF (default): current behavior.
7458
+
7459
+ Owner or commander only — enforced server-side (mc_set_mesh_keep_alive).
7460
+ """
7461
+ res = be.sb_rpc("mc_set_mesh_keep_alive", {
7462
+ "p_api_key": _get_api_key(),
7463
+ "p_project_id": _PROJECT_ID,
7464
+ "p_enabled": bool(enabled),
7465
+ })
7466
+ return res if isinstance(res, dict) else {"error": str(res)}
7467
+
7468
+
7448
7469
  def inbox_resource() -> str:
7449
7470
  """Current pending messages for this agent. Read-only — does NOT mark as read."""
7450
7471
  pending = be.sb_select(
@@ -56,6 +56,35 @@ _SLEEP_TEXT_MARKERS = (
56
56
  # server + hook share authority on who counts as a human user.
57
57
  _KNOWN_HUMAN_HANDLES = frozenset({"sammybenu", "samuel", "sam"})
58
58
 
59
+ # Task 3ec2cd4b (live repro 2026-06-10T00:34Z, msg 62062a6b): Samuel wrote
60
+ # "...que NO se vayan a dormir asi nomas... sabes?" — a COMPLAINT about agents
61
+ # sleeping, the opposite of an authorization — and the bare substring
62
+ # "a dormir" tripped must_exit on the commander mid-crisis. Human TEXT only
63
+ # authorizes sleep when it is directive-SHAPED; in doubt the message degrades
64
+ # to a normal msg (never must_exit). Three guards, human-text path only
65
+ # (structured payload.type/directive is untouched — that's the dashboard
66
+ # button / deliberate-agent path):
67
+ # LENGTH: a directive is short ("todos a dormir"); >80 chars is prose.
68
+ # QUESTION: "?"/"¿" = asking or complaining, not ordering.
69
+ # NEGATION: a negation token anywhere kills it ("que no se vayan a dormir").
70
+ _HUMAN_DIRECTIVE_MAX_CHARS = 80
71
+ _HUMAN_NEGATION_TOKENS = (
72
+ " no ", " not ", " nunca ", " jamas ", " jamás ", "que no", "sin que",
73
+ "don't", "do not", " ni ",
74
+ )
75
+
76
+
77
+ def _human_text_is_directive(text: str) -> bool:
78
+ """True when human text is shaped like an order, not prose about sleep."""
79
+ if len(text) > _HUMAN_DIRECTIVE_MAX_CHARS:
80
+ return False
81
+ if "?" in text or "\u00bf" in text:
82
+ return False
83
+ padded = " " + text.strip().lower() + " "
84
+ if any(tok in padded for tok in _HUMAN_NEGATION_TOKENS):
85
+ return False
86
+ return True
87
+
59
88
  # ── ENJAMBRE G3 (task 3b0e4129) — swarm barrier events ──────────────────────
60
89
  # The mc_swarm_barrier_check trigger (mig 471) fires when a swarm's tray
61
90
  # drains. The runtime has NO raw Postgres connection (PostgREST + Realtime
@@ -121,7 +150,7 @@ def _looks_like_sleep_signal(m: Dict[str, Any]) -> bool:
121
150
  return True
122
151
  text = str(pl.get("text", "")).lower()
123
152
  if text and any(marker in text for marker in _SLEEP_TEXT_MARKERS):
124
- if _is_human_authored(m):
153
+ if _is_human_authored(m) and _human_text_is_directive(text):
125
154
  return True
126
155
  return False
127
156
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.11.122
3
+ Version: 2.11.123
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.122"
7
+ version = "2.11.123"
8
8
  description = "Real-time communication between AI agents — Supabase-backed CLI"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -158,3 +158,51 @@ class TestSplitMessages(unittest.TestCase):
158
158
 
159
159
  if __name__ == "__main__":
160
160
  unittest.main()
161
+
162
+
163
+ class TestCasualSpanishComplaintGuards(unittest.TestCase):
164
+ """Task 3ec2cd4b — live repro 2026-06-10T00:34Z (msg 62062a6b): Samuel's
165
+ COMPLAINT about agents sleeping ("que NO se vayan a dormir asi nomas...")
166
+ tripped must_exit on the commander. Human text must be directive-shaped."""
167
+
168
+ REPRO = (
169
+ "oye ya me acague los agents se estan exiting and sleepign cuando no "
170
+ "tienen nada, que no se vayan a dormir asi nomas, porque aveces lo "
171
+ "querio dejar prendido sabes? que recomeindas, por ejemplo ahroita FE "
172
+ "no tenia nada asignadno y se fue a dormir , pero que si el suaurio "
173
+ "queria dejar su mesh rpendida sin taksks? sin que se fuerman que se "
174
+ "mantengan atentos a caulqueir mensaje"
175
+ )
176
+
177
+ def test_literal_live_repro_never_sleeps(self):
178
+ m = _msg(sender="sammybenu", payload={"text": self.REPRO})
179
+ self.assertFalse(_looks_like_sleep_signal(m))
180
+ split = _split_messages([m])
181
+ self.assertEqual(len(split["done_signals"]), 0)
182
+ self.assertEqual(len(split["messages"]), 1)
183
+
184
+ def test_negated_short_directive_blocked(self):
185
+ m = _msg(sender="sammybenu", payload={"text": "que no se vayan a dormir"})
186
+ self.assertFalse(_looks_like_sleep_signal(m))
187
+
188
+ def test_question_about_sleep_blocked(self):
189
+ m = _msg(sender="sammybenu", payload={"text": "se fueron a dormir?"})
190
+ self.assertFalse(_looks_like_sleep_signal(m))
191
+
192
+ def test_long_prose_with_marker_blocked(self):
193
+ m = _msg(sender="sammybenu", payload={
194
+ "text": "bueno pues al final creo que el plan de hoy salio bien y "
195
+ "ya cada quien puede irse a dormir cuando guste, mañana "
196
+ "seguimos con lo del deploy y lo de los archivos"})
197
+ self.assertFalse(_looks_like_sleep_signal(m))
198
+
199
+ def test_short_human_directive_still_authorizes(self):
200
+ for text in ("todos a dormir", "a dormir", "go to sleep", "sleep now"):
201
+ m = _msg(sender="sammybenu", payload={"text": text})
202
+ self.assertTrue(_looks_like_sleep_signal(m), text)
203
+
204
+ def test_structured_directive_untouched_by_guards(self):
205
+ # dashboard-button path: payload.type fires regardless of text shape
206
+ m = _msg(sender="mesh-commander",
207
+ payload={"type": "got_done", "text": self.REPRO})
208
+ self.assertTrue(_looks_like_sleep_signal(m))
File without changes
File without changes
File without changes
File without changes