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.
- {meshcode-2.11.122 → meshcode-2.11.123}/PKG-INFO +1 -1
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/__init__.py +1 -1
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/hostd.py +9 -1
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/meshcode_mcp/server.py +21 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/meshcode_mcp/sleep_signals.py +30 -1
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode.egg-info/PKG-INFO +1 -1
- {meshcode-2.11.122 → meshcode-2.11.123}/pyproject.toml +1 -1
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_sleep_signals.py +48 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/README.md +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/__main__.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/_session_handoff_template.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/_stop_hook_template.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/ascii_art.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/atomic_push.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/claude_update.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/cli.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/comms_v4.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/compat.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/daemon.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/date_parse.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/doctor.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/error_hints.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/exceptions.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/hooks/__init__.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/hooks/repo_path_lock.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/invites.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/launcher.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/launcher_install.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/meshcode_mcp/test_boot_timing.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/meshcode_mcp/test_install_guard.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/meshcode_mcp/test_prefs_claude_version.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/preferences.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/protocol_handler.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/protocol_v2.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/quickstart.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/rpc_allowlist.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/run_agent.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/scripts/check_secrets.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/scripts/race_rate_harness.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/secrets.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/self_update.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/setup_clients.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/supervisor.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/up.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode/upload.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode.egg-info/SOURCES.txt +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode.egg-info/dependency_links.txt +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode.egg-info/entry_points.txt +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode.egg-info/requires.txt +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/meshcode.egg-info/top_level.txt +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/setup.cfg +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_auto_update_hardening.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_autonomous_closegap_1.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_autonomous_closegap_2.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_autonomous_closegap_3.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_autonomous_prompt_inject.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_boot_bug_regression.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_color_truecolor.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_core.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_cross_agent_messaging.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_date_parse.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_doctor.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_epistemic_v1_python_sdk.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_epistemic_v1_stop_conditions.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_esc_deaf_state.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_exceptions.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_file_upload.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_init_device_code.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_install_guard.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_lease_sigterm_release.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_mark_read_batch.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_marketplace_ratings.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_migration_integrity.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_realtime_event_freshness.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_rls_cross_tenant.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_rpc_grants.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_rpc_migrations.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_run_agent_dry_run.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_run_agent_no_server_import.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_security_regressions.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_self_update_user_site.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_sentinel.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_setup_path.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_status_enum_coverage.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_stay_on_loop_hook.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_swarm_events.py +0 -0
- {meshcode-2.11.122 → meshcode-2.11.123}/tests/test_wait_open_tasks_contradiction.py +0 -0
|
@@ -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
|
-
|
|
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
|
|
|
@@ -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
|
|
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
|