meshcode 2.11.110__tar.gz → 2.11.111rc1__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 (101) hide show
  1. {meshcode-2.11.110 → meshcode-2.11.111rc1}/PKG-INFO +1 -1
  2. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/__init__.py +1 -1
  3. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/hostd.py +24 -0
  4. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/meshcode_mcp/server.py +107 -0
  5. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode.egg-info/PKG-INFO +1 -1
  6. {meshcode-2.11.110 → meshcode-2.11.111rc1}/pyproject.toml +1 -1
  7. {meshcode-2.11.110 → meshcode-2.11.111rc1}/README.md +0 -0
  8. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/__main__.py +0 -0
  9. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/_session_handoff_template 2.py +0 -0
  10. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/_session_handoff_template 3.py +0 -0
  11. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/_session_handoff_template.py +0 -0
  12. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/_stop_hook_template.py +0 -0
  13. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/ascii_art.py +0 -0
  14. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/atomic_push.py +0 -0
  15. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/claude_update 2.py +0 -0
  16. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/claude_update 3.py +0 -0
  17. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/claude_update.py +0 -0
  18. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/cli.py +0 -0
  19. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/comms_v4.py +0 -0
  20. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/compat.py +0 -0
  21. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/daemon.py +0 -0
  22. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/date_parse.py +0 -0
  23. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/doctor.py +0 -0
  24. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/error_hints.py +0 -0
  25. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/exceptions.py +0 -0
  26. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/hooks/__init__.py +0 -0
  27. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/hooks/repo_path_lock.py +0 -0
  28. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/hostd 2.py +0 -0
  29. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/invites.py +0 -0
  30. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/launcher.py +0 -0
  31. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/launcher_install.py +0 -0
  32. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/meshcode_mcp/__init__.py +0 -0
  33. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/meshcode_mcp/__main__.py +0 -0
  34. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/meshcode_mcp/backend.py +0 -0
  35. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/meshcode_mcp/realtime.py +0 -0
  36. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/meshcode_mcp/sleep_signals.py +0 -0
  37. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/meshcode_mcp/test_backend.py +0 -0
  38. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/meshcode_mcp/test_boot_timing.py +0 -0
  39. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/meshcode_mcp/test_install_guard.py +0 -0
  40. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/meshcode_mcp/test_prefs_claude_version.py +0 -0
  41. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/meshcode_mcp/test_realtime.py +0 -0
  42. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  43. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/preferences.py +0 -0
  44. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/protocol_handler.py +0 -0
  45. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/protocol_v2.py +0 -0
  46. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/quickstart.py +0 -0
  47. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/rpc_allowlist.py +0 -0
  48. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/run_agent.py +0 -0
  49. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/scripts/check_secrets.py +0 -0
  50. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/scripts/race_rate_harness.py +0 -0
  51. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/secrets.py +0 -0
  52. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/self_update.py +0 -0
  53. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/setup_clients.py +0 -0
  54. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/supervisor.py +0 -0
  55. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/up 2.py +0 -0
  56. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/up.py +0 -0
  57. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode/upload.py +0 -0
  58. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode.egg-info/SOURCES.txt +0 -0
  59. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode.egg-info/dependency_links.txt +0 -0
  60. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode.egg-info/entry_points.txt +0 -0
  61. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode.egg-info/requires.txt +0 -0
  62. {meshcode-2.11.110 → meshcode-2.11.111rc1}/meshcode.egg-info/top_level.txt +0 -0
  63. {meshcode-2.11.110 → meshcode-2.11.111rc1}/setup.cfg +0 -0
  64. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_auto_update_hardening.py +0 -0
  65. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_autonomous_closegap_1.py +0 -0
  66. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_autonomous_closegap_2.py +0 -0
  67. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_autonomous_closegap_3.py +0 -0
  68. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_autonomous_prompt_inject 2.py +0 -0
  69. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_autonomous_prompt_inject 3.py +0 -0
  70. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_autonomous_prompt_inject.py +0 -0
  71. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_boot_bug_regression.py +0 -0
  72. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_color_truecolor.py +0 -0
  73. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_core.py +0 -0
  74. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_cross_agent_messaging.py +0 -0
  75. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_date_parse.py +0 -0
  76. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_doctor.py +0 -0
  77. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_epistemic_v1_python_sdk.py +0 -0
  78. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_epistemic_v1_stop_conditions.py +0 -0
  79. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_esc_deaf_state.py +0 -0
  80. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_exceptions.py +0 -0
  81. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_file_upload.py +0 -0
  82. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_init_device_code.py +0 -0
  83. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_install_guard.py +0 -0
  84. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_lease_sigterm_release.py +0 -0
  85. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_mark_read_batch.py +0 -0
  86. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_marketplace_ratings.py +0 -0
  87. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_migration_integrity.py +0 -0
  88. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_realtime_event_freshness.py +0 -0
  89. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_rls_cross_tenant.py +0 -0
  90. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_rpc_grants.py +0 -0
  91. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_rpc_migrations.py +0 -0
  92. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_run_agent_dry_run.py +0 -0
  93. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_run_agent_no_server_import.py +0 -0
  94. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_security_regressions.py +0 -0
  95. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_self_update_user_site.py +0 -0
  96. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_sentinel.py +0 -0
  97. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_setup_path.py +0 -0
  98. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_sleep_signals.py +0 -0
  99. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_status_enum_coverage.py +0 -0
  100. {meshcode-2.11.110 → meshcode-2.11.111rc1}/tests/test_stay_on_loop_hook.py +0 -0
  101. {meshcode-2.11.110 → meshcode-2.11.111rc1}/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.110
3
+ Version: 2.11.111rc1
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.110"
2
+ __version__ = "2.11.111rc1"
3
3
 
4
4
  # Exception hierarchy — eagerly imported (lightweight, no deps)
5
5
  from meshcode.exceptions import ( # noqa: F401
@@ -159,6 +159,17 @@ STATE_DIR = Path.home() / ".meshcode"
159
159
  HOST_ID_PATH = STATE_DIR / "host_id"
160
160
  LOG_PATH = STATE_DIR / "hostd.log"
161
161
 
162
+ # BOOT-AUTOSTART GATE (task b6da0d54, Samuel: "terminals open by themselves at boot").
163
+ # hostd is launched at OS logon (Task Scheduler / launchd). On a fresh start it must NOT
164
+ # auto-respawn agents that were merely left desired_state='running' by a PRIOR session —
165
+ # that's the "terminals pop at boot" bug. We record hostd's own start time; an agent whose
166
+ # last heartbeat PREDATES this start (or who never heartbeated) is a boot-stale leftover and
167
+ # is skipped (explicit launch required). An agent that was alive AFTER this start and then
168
+ # died = a real crash -> respawned (crash-recovery preserved). Opt back into the old
169
+ # auto-launch-everything behavior with MESHCODE_BOOT_AUTOSTART=1.
170
+ _HOSTD_STARTED_AT = time.time()
171
+ _BOOT_AUTOSTART = os.environ.get("MESHCODE_BOOT_AUTOSTART", "").strip().lower() in ("1", "true", "yes", "on")
172
+
162
173
  def _env_poll_interval() -> int:
163
174
  # task 399d7b51 SPEED: poll faster so click->desired_state->spawn latency drops (was 45s).
164
175
  # Env-configurable (survives upgrade); floor 3s so we never hammer the DB.
@@ -516,6 +527,19 @@ def _do_respawns(api_key: str, host_id: str) -> int:
516
527
  # do NOT re-record here (that would inflate the count on a mere rate-limit skip).
517
528
  _log(f"SKIP respawn {proj}/{agent}: not allowed (count={c.get('respawn_count')}, rate-limited/at-cap)")
518
529
  continue
530
+ # BOOT-AUTOSTART GATE (task b6da0d54): don't auto-launch an agent whose last heartbeat
531
+ # PREDATES this hostd start (or never heartbeated) — that's a boot-stale 'running' leftover
532
+ # from a prior session, NOT a live-then-crashed agent. Skip it (explicit launch required) so
533
+ # rebooting / hostd-restart does NOT pop terminals. An agent alive AFTER startup that died =
534
+ # crash -> falls through to respawn (crash-recovery preserved). MESHCODE_BOOT_AUTOSTART=1 opts out.
535
+ if not _BOOT_AUTOSTART:
536
+ _hb_age = c.get("heartbeat_age_s")
537
+ _hostd_uptime = time.time() - _HOSTD_STARTED_AT
538
+ if _hb_age is None or _hb_age >= _hostd_uptime:
539
+ _log(f"BOOT-AUTOSTART OFF: skip auto-respawn {proj}/{agent} (no heartbeat since hostd "
540
+ f"start — boot-stale 'running' leftover; explicit launch required). "
541
+ f"Set MESHCODE_BOOT_AUTOSTART=1 to auto-launch at boot.")
542
+ continue
519
543
  # RECYCLE FAST-PATH (task c0fc5597): a recycle-exited agent (recycle_fast) is relaunched
520
544
  # PROMPTLY (the RPC returned it at a 15s stale gate, not STALE_SECONDS) and recorded as a
521
545
  # RECYCLE (mc_record_recycle), NEVER against the crash respawn cap.
@@ -1591,6 +1591,105 @@ def _install_shutdown_signal_handlers() -> None:
1591
1591
  _mc_log(f"could not register SIGINT handler: {e}", "warn")
1592
1592
 
1593
1593
 
1594
+ # ── Close-to-stop: closing the terminal STOPS the agent (task b6da0d54, Samuel) ─
1595
+ # DISTINCT from the graceful SIGTERM/SIGINT handler above. This fires on a real
1596
+ # console CLOSE (window/tab) and flips desired_state->stopped so hostd does NOT
1597
+ # respawn it (hostd respawns ONLY desired_state='running'). A genuine CRASH never
1598
+ # runs this (process is gone before the handler) -> desired_state stays 'running'
1599
+ # -> hostd crash-respawn is PRESERVED. ESC is NOT a close (the window stays open),
1600
+ # so it never triggers this. "al cerrar la terminal el agente se debe parar."
1601
+ _WIN_CTRL_HANDLER_REF = None # keep the WINFUNCTYPE callback alive (GC guard)
1602
+
1603
+
1604
+ def _flip_desired_state_stopped(timeout_s: float = 3.0) -> None:
1605
+ """Best-effort flip THIS agent's desired_state->stopped via mc_set_desired_state
1606
+ (api_key self-scoped to the calling agent). Fast — Windows CTRL_CLOSE allows ~5s
1607
+ before the OS force-kills, so we time-box the RPC and never block exit."""
1608
+ done = _threading.Event()
1609
+
1610
+ def _do():
1611
+ try:
1612
+ ak = _get_api_key()
1613
+ if ak:
1614
+ be.sb_rpc("mc_set_desired_state", {"p_api_key": ak, "p_state": "stopped"})
1615
+ except Exception:
1616
+ pass
1617
+ done.set()
1618
+
1619
+ try:
1620
+ _threading.Thread(target=_do, daemon=True, name="meshcode-close-set-stopped").start()
1621
+ done.wait(timeout=timeout_s)
1622
+ except Exception:
1623
+ try:
1624
+ ak = _get_api_key()
1625
+ if ak:
1626
+ be.sb_rpc("mc_set_desired_state", {"p_api_key": ak, "p_state": "stopped"})
1627
+ except Exception:
1628
+ pass
1629
+
1630
+
1631
+ def _close_to_stop_handler(signum=None, frame=None): # pragma: no cover - signal handler
1632
+ """Terminal CLOSE -> stop this agent (no respawn). See the block comment above."""
1633
+ try:
1634
+ sys.stderr.write(
1635
+ f"[meshcode-mcp] terminal close (sig={signum}) — desired_state->stopped + release lease\n")
1636
+ sys.stderr.flush()
1637
+ except Exception:
1638
+ pass
1639
+ try:
1640
+ _flip_desired_state_stopped(timeout_s=3.0)
1641
+ except Exception:
1642
+ pass
1643
+ try:
1644
+ _release_lease_synchronous(timeout_s=1.0)
1645
+ except Exception:
1646
+ pass
1647
+ os._exit(0)
1648
+
1649
+
1650
+ def _install_close_to_stop_handlers() -> None:
1651
+ """Register console-CLOSE handlers so closing the terminal STOPS the agent (b6da0d54).
1652
+
1653
+ Windows: SetConsoleCtrlHandler for CTRL_CLOSE/LOGOFF/SHUTDOWN — ALWAYS ON (a window
1654
+ close is never an ESC; reliably distinct). This is the prod fix (prod = Windows).
1655
+
1656
+ POSIX: SIGHUP->stop is GATED behind MESHCODE_CLOSE_STOP_SIGHUP=1 (default OFF). The
1657
+ run_server._diag_ignore deliberately IGNORES SIGHUP for ESC/session-restructure
1658
+ survival; reversing it unconditionally risks a false-stop on a transient SIGHUP.
1659
+ Enable only once verified that Claude ESC does not emit SIGHUP to this process.
1660
+ Never touches SIGINT (ESC) or SIGTERM (handled by the opt-in graceful handler)."""
1661
+ if sys.platform == "win32":
1662
+ try:
1663
+ import ctypes
1664
+ from ctypes import wintypes
1665
+ global _WIN_CTRL_HANDLER_REF
1666
+ _HANDLER = ctypes.WINFUNCTYPE(wintypes.BOOL, wintypes.DWORD)
1667
+ CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, CTRL_SHUTDOWN_EVENT = 2, 5, 6
1668
+
1669
+ def _win_ctrl(ctrl_type):
1670
+ if ctrl_type in (CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, CTRL_SHUTDOWN_EVENT):
1671
+ _close_to_stop_handler(signum=f"win_ctrl:{ctrl_type}")
1672
+ return True
1673
+ return False # CTRL_C / CTRL_BREAK -> default handler (not our concern)
1674
+
1675
+ _WIN_CTRL_HANDLER_REF = _HANDLER(_win_ctrl)
1676
+ if ctypes.windll.kernel32.SetConsoleCtrlHandler(_WIN_CTRL_HANDLER_REF, True):
1677
+ _mc_log("registered Windows CTRL_CLOSE close=stop handler (b6da0d54)")
1678
+ else:
1679
+ _mc_log("SetConsoleCtrlHandler(close) returned 0", "warn")
1680
+ except Exception as e:
1681
+ _mc_log(f"could not register Windows close handler: {e}", "warn")
1682
+ else:
1683
+ if os.environ.get("MESHCODE_CLOSE_STOP_SIGHUP", "").lower() in ("1", "true", "yes"):
1684
+ import signal as _sig_mod
1685
+ try:
1686
+ _sig_mod.signal(_sig_mod.SIGHUP, _close_to_stop_handler)
1687
+ _mc_log("registered SIGHUP close=stop handler (MESHCODE_CLOSE_STOP_SIGHUP=1, b6da0d54)")
1688
+ except (ValueError, OSError) as e:
1689
+ _mc_log(f"could not register SIGHUP handler: {e}", "warn")
1690
+ # else: posix SIGHUP stays as run_server._diag_ignore (ESC-survival default)
1691
+
1692
+
1594
1693
  # ── Crash logging + graceful shutdown ──────────────────────────
1595
1694
  _SHUTDOWN_LOGGED = False
1596
1695
 
@@ -2645,6 +2744,14 @@ async def lifespan(_app):
2645
2744
  except Exception as _sig_e:
2646
2745
  log.debug(f"shutdown signal handler install failed: {_sig_e}")
2647
2746
 
2747
+ # Close-to-stop (task b6da0d54): closing the terminal flips desired_state->stopped
2748
+ # so hostd does not respawn (Samuel: 'al cerrar la terminal el agente se debe parar').
2749
+ # Windows CTRL_CLOSE always-on; posix SIGHUP gated (default OFF) — see the fn docstring.
2750
+ try:
2751
+ _install_close_to_stop_handlers()
2752
+ except Exception as _close_e:
2753
+ log.debug(f"close-to-stop handler install failed: {_close_e}")
2754
+
2648
2755
  log.info(f"lifespan started — Realtime + heartbeat thread + orphan watchdog active for {AGENT_NAME}")
2649
2756
  # Enable session recording in backend.py (hot-reloadable)
2650
2757
  try:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.11.110
3
+ Version: 2.11.111rc1
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.110"
7
+ version = "2.11.111rc1"
8
8
  description = "Real-time communication between AI agents — Supabase-backed CLI"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
File without changes
File without changes