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.
- {meshcode-2.10.45 → meshcode-2.10.46}/PKG-INFO +1 -1
- {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/__init__.py +1 -1
- {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/meshcode_mcp/server.py +37 -4
- {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/run_agent.py +13 -1
- {meshcode-2.10.45 → meshcode-2.10.46}/meshcode.egg-info/PKG-INFO +1 -1
- {meshcode-2.10.45 → meshcode-2.10.46}/pyproject.toml +1 -1
- {meshcode-2.10.45 → meshcode-2.10.46}/README.md +0 -0
- {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/ascii_art.py +0 -0
- {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/cli.py +0 -0
- {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/comms_v4.py +0 -0
- {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/invites.py +0 -0
- {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/launcher.py +0 -0
- {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/launcher_install.py +0 -0
- {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/preferences.py +0 -0
- {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/protocol_v2.py +0 -0
- {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/secrets.py +0 -0
- {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/self_update.py +0 -0
- {meshcode-2.10.45 → meshcode-2.10.46}/meshcode/setup_clients.py +0 -0
- {meshcode-2.10.45 → meshcode-2.10.46}/meshcode.egg-info/SOURCES.txt +0 -0
- {meshcode-2.10.45 → meshcode-2.10.46}/meshcode.egg-info/dependency_links.txt +0 -0
- {meshcode-2.10.45 → meshcode-2.10.46}/meshcode.egg-info/entry_points.txt +0 -0
- {meshcode-2.10.45 → meshcode-2.10.46}/meshcode.egg-info/requires.txt +0 -0
- {meshcode-2.10.45 → meshcode-2.10.46}/meshcode.egg-info/top_level.txt +0 -0
- {meshcode-2.10.45 → meshcode-2.10.46}/setup.cfg +0 -0
- {meshcode-2.10.45 → meshcode-2.10.46}/tests/test_status_enum_coverage.py +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""MeshCode — Real-time communication between AI agents."""
|
|
2
|
-
__version__ = "2.10.
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|