meshcode 2.10.45__tar.gz → 2.10.46__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 (32) hide show
  1. {meshcode-2.10.45 → meshcode-2.10.46}/PKG-INFO +1 -1
  2. {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/__init__.py +1 -1
  3. {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/meshcode_mcp/server.py +37 -4
  4. {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/run_agent.py +13 -1
  5. {meshcode-2.10.45 → meshcode-2.10.46}/meshcode.egg-info/PKG-INFO +1 -1
  6. {meshcode-2.10.45 → meshcode-2.10.46}/pyproject.toml +1 -1
  7. {meshcode-2.10.45 → meshcode-2.10.46}/README.md +0 -0
  8. {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/ascii_art.py +0 -0
  9. {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/cli.py +0 -0
  10. {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/comms_v4.py +0 -0
  11. {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/invites.py +0 -0
  12. {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/launcher.py +0 -0
  13. {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/launcher_install.py +0 -0
  14. {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/meshcode_mcp/__init__.py +0 -0
  15. {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/meshcode_mcp/__main__.py +0 -0
  16. {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/meshcode_mcp/backend.py +0 -0
  17. {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/meshcode_mcp/realtime.py +0 -0
  18. {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/meshcode_mcp/test_backend.py +0 -0
  19. {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/meshcode_mcp/test_realtime.py +0 -0
  20. {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  21. {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/preferences.py +0 -0
  22. {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/protocol_v2.py +0 -0
  23. {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/secrets.py +0 -0
  24. {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/self_update.py +0 -0
  25. {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/setup_clients.py +0 -0
  26. {meshcode-2.10.45 → meshcode-2.10.46}/meshcode.egg-info/SOURCES.txt +0 -0
  27. {meshcode-2.10.45 → meshcode-2.10.46}/meshcode.egg-info/dependency_links.txt +0 -0
  28. {meshcode-2.10.45 → meshcode-2.10.46}/meshcode.egg-info/entry_points.txt +0 -0
  29. {meshcode-2.10.45 → meshcode-2.10.46}/meshcode.egg-info/requires.txt +0 -0
  30. {meshcode-2.10.45 → meshcode-2.10.46}/meshcode.egg-info/top_level.txt +0 -0
  31. {meshcode-2.10.45 → meshcode-2.10.46}/setup.cfg +0 -0
  32. {meshcode-2.10.45 → meshcode-2.10.46}/tests/test_status_enum_coverage.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.10.45
3
+ Version: 2.10.46
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,2 +1,2 @@
1
1
  """MeshCode — Real-time communication between AI agents."""
2
- __version__ = "2.10.45"
2
+ __version__ = "2.10.46"
@@ -635,6 +635,12 @@ def with_working_status(func):
635
635
  _record_event_bg("tool_call", {"tool": name, "args_keys": list(kwargs.keys()), "estimated_tokens": _est_tokens})
636
636
  try:
637
637
  return await func(*args, **kwargs)
638
+ except asyncio.CancelledError:
639
+ # ESC in Claude Code cancels the in-flight task. Catch here
640
+ # to prevent cascade through FastMCP into the event loop.
641
+ # Return an error dict instead of propagating BaseException.
642
+ log.debug(f"[meshcode] tool {name} cancelled by client (ESC)")
643
+ return {"error": "cancelled_by_client", "tool": name}
638
644
  except Exception as e:
639
645
  if not skip:
640
646
  _auto_learn_error(name, e, list(kwargs.keys()))
@@ -1665,7 +1671,11 @@ async def meshcode_call(to: str, function: str, args: Any = None, timeout_second
1665
1671
  poll_interval = 1.0
1666
1672
  elapsed = 0.0
1667
1673
  while elapsed < timeout_seconds:
1668
- await _asyncio.sleep(poll_interval)
1674
+ try:
1675
+ await _asyncio.sleep(poll_interval)
1676
+ except _asyncio.CancelledError:
1677
+ log.debug("[meshcode] meshcode_call poll cancelled by ESC")
1678
+ return {"error": "cancelled_by_client", "call_id": call_id, "ok": False}
1669
1679
  elapsed += poll_interval
1670
1680
 
1671
1681
  # Check Realtime buffer first
@@ -1898,7 +1908,13 @@ async def meshcode_wait(timeout_seconds: int = 20, include_acks: bool = False) -
1898
1908
  # Keep polling until something actionable arrives.
1899
1909
  # The agent (LLM) is NOT called between iterations — zero token cost.
1900
1910
  while True:
1901
- result = await _meshcode_wait_inner(actual_timeout=capped_timeout, include_acks=include_acks)
1911
+ try:
1912
+ result = await _meshcode_wait_inner(actual_timeout=capped_timeout, include_acks=include_acks)
1913
+ except asyncio.CancelledError:
1914
+ # Safety net: if CancelledError escapes _meshcode_wait_inner
1915
+ # despite the inner shield, catch it here to prevent cascade.
1916
+ log.debug("[meshcode] meshcode_wait outer loop caught CancelledError")
1917
+ result = {"timed_out": True, "reason": "cancelled_by_client"}
1902
1918
 
1903
1919
  if result.get("got_message"):
1904
1920
  # Real message arrived — return to agent for processing
@@ -2041,7 +2057,20 @@ async def _meshcode_wait_inner(actual_timeout: int, include_acks: bool) -> Dict[
2041
2057
 
2042
2058
  if _rt_live:
2043
2059
  # 2a) Real async wait — zero CPU, zero Supabase calls.
2044
- woke = await _REALTIME.wait_for_message(timeout=float(actual_timeout))
2060
+ # Shield from CancelledError: when Claude Code presses ESC, it cancels
2061
+ # the in-flight asyncio task. CancelledError is a BaseException that
2062
+ # bypasses most try/except, cascades through FastMCP's tool handler,
2063
+ # and unwinds the event loop — killing the MCP server. By shielding
2064
+ # the await and catching CancelledError, we return a clean timeout
2065
+ # result instead. The mcp.run() retry loop restarts the event loop
2066
+ # without the lifespan cascade.
2067
+ try:
2068
+ woke = await asyncio.shield(
2069
+ _REALTIME.wait_for_message(timeout=float(actual_timeout))
2070
+ )
2071
+ except (asyncio.CancelledError, Exception) as _cancel_exc:
2072
+ log.debug(f"[meshcode] wait_for_message cancelled/failed: {type(_cancel_exc).__name__}")
2073
+ return {"timed_out": True, "reason": "cancelled_by_client"}
2045
2074
  if woke:
2046
2075
  buffered = _REALTIME.drain()
2047
2076
  if buffered:
@@ -2054,7 +2083,11 @@ async def _meshcode_wait_inner(actual_timeout: int, include_acks: bool) -> Dict[
2054
2083
  _poll_interval = 5
2055
2084
  _elapsed = 0
2056
2085
  while _elapsed < actual_timeout:
2057
- await asyncio.sleep(min(_poll_interval, actual_timeout - _elapsed))
2086
+ try:
2087
+ await asyncio.sleep(min(_poll_interval, actual_timeout - _elapsed))
2088
+ except asyncio.CancelledError:
2089
+ log.debug("[meshcode] DB poll sleep cancelled by ESC")
2090
+ return {"timed_out": True, "reason": "cancelled_by_client"}
2058
2091
  _elapsed += _poll_interval
2059
2092
  try:
2060
2093
  api_key = _get_api_key()
@@ -434,8 +434,20 @@ def _resolve_pinned_claude(editor_cmd: str) -> str:
434
434
  print(f"[meshcode] Global admin pin: Claude Code v{pinned}", file=sys.stderr)
435
435
  if not pinned:
436
436
  pinned = load_prefs().get("claude_version", "")
437
+
438
+ # "latest" or "none" means skip pinning — use whatever is installed
439
+ if pinned and pinned.lower() in ("latest", "none", "skip"):
440
+ print(f"[meshcode] Version pin disabled ({pinned}) — using installed version", file=sys.stderr)
441
+ return editor_cmd
442
+
437
443
  if not pinned:
438
- return editor_cmd # No pin use installed version
444
+ # Default pin: v2.1.104 is the last Claude Code version where ESC
445
+ # doesn't kill the MCP server. Newer versions send CancelledError
446
+ # that cascades through the event loop. This default is overridden
447
+ # by any of the 3 pin sources above (set to "latest" to disable).
448
+ # Remove this default once Anthropic fixes the ESC bug upstream.
449
+ pinned = "2.1.104"
450
+ print(f"[meshcode] Default ESC-safe pin: Claude Code v{pinned}", file=sys.stderr)
439
451
 
440
452
  current = _get_claude_version(editor_cmd)
441
453
  if current == pinned:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.10.45
3
+ Version: 2.10.46
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.10.45"
7
+ version = "2.10.46"
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
File without changes