meshcode 2.10.30__tar.gz → 2.10.32__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.30 → meshcode-2.10.32}/PKG-INFO +1 -1
- {meshcode-2.10.30 → meshcode-2.10.32}/meshcode/__init__.py +1 -1
- {meshcode-2.10.30 → meshcode-2.10.32}/meshcode/meshcode_mcp/server.py +63 -31
- {meshcode-2.10.30 → meshcode-2.10.32}/meshcode.egg-info/PKG-INFO +1 -1
- {meshcode-2.10.30 → meshcode-2.10.32}/pyproject.toml +1 -1
- {meshcode-2.10.30 → meshcode-2.10.32}/README.md +0 -0
- {meshcode-2.10.30 → meshcode-2.10.32}/meshcode/ascii_art.py +0 -0
- {meshcode-2.10.30 → meshcode-2.10.32}/meshcode/cli.py +0 -0
- {meshcode-2.10.30 → meshcode-2.10.32}/meshcode/comms_v4.py +0 -0
- {meshcode-2.10.30 → meshcode-2.10.32}/meshcode/invites.py +0 -0
- {meshcode-2.10.30 → meshcode-2.10.32}/meshcode/launcher.py +0 -0
- {meshcode-2.10.30 → meshcode-2.10.32}/meshcode/launcher_install.py +0 -0
- {meshcode-2.10.30 → meshcode-2.10.32}/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.10.30 → meshcode-2.10.32}/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.10.30 → meshcode-2.10.32}/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.10.30 → meshcode-2.10.32}/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.10.30 → meshcode-2.10.32}/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.10.30 → meshcode-2.10.32}/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.10.30 → meshcode-2.10.32}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.10.30 → meshcode-2.10.32}/meshcode/preferences.py +0 -0
- {meshcode-2.10.30 → meshcode-2.10.32}/meshcode/protocol_v2.py +0 -0
- {meshcode-2.10.30 → meshcode-2.10.32}/meshcode/run_agent.py +0 -0
- {meshcode-2.10.30 → meshcode-2.10.32}/meshcode/secrets.py +0 -0
- {meshcode-2.10.30 → meshcode-2.10.32}/meshcode/self_update.py +0 -0
- {meshcode-2.10.30 → meshcode-2.10.32}/meshcode/setup_clients.py +0 -0
- {meshcode-2.10.30 → meshcode-2.10.32}/meshcode.egg-info/SOURCES.txt +0 -0
- {meshcode-2.10.30 → meshcode-2.10.32}/meshcode.egg-info/dependency_links.txt +0 -0
- {meshcode-2.10.30 → meshcode-2.10.32}/meshcode.egg-info/entry_points.txt +0 -0
- {meshcode-2.10.30 → meshcode-2.10.32}/meshcode.egg-info/requires.txt +0 -0
- {meshcode-2.10.30 → meshcode-2.10.32}/meshcode.egg-info/top_level.txt +0 -0
- {meshcode-2.10.30 → meshcode-2.10.32}/setup.cfg +0 -0
- {meshcode-2.10.30 → meshcode-2.10.32}/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.32"
|
|
@@ -1217,40 +1217,53 @@ async def lifespan(_app):
|
|
|
1217
1217
|
try:
|
|
1218
1218
|
yield {"realtime": _REALTIME}
|
|
1219
1219
|
finally:
|
|
1220
|
-
|
|
1221
|
-
#
|
|
1220
|
+
# 2.10.31: minimal-risk shutdown. The previous body did network I/O,
|
|
1221
|
+
# a 5-second thread join, and an `await _REALTIME.stop()` inside the
|
|
1222
|
+
# lifespan's finally. Under Claude Code 2.1.111 ESC, that cancellation
|
|
1223
|
+
# path cascades through the awaits and unwinds the asyncio loop,
|
|
1224
|
+
# killing the subprocess. A bare FastMCP (no lifespan) in the same
|
|
1225
|
+
# bucket survives. To match that behavior without losing Realtime on
|
|
1226
|
+
# startup, we keep the startup logic and make the shutdown strictly
|
|
1227
|
+
# non-blocking, non-awaiting, non-raising. Daemon threads / realtime
|
|
1228
|
+
# socket die with the process; the lease expires on heartbeat timeout.
|
|
1222
1229
|
try:
|
|
1223
|
-
|
|
1224
|
-
api_key = _get_api_key()
|
|
1225
|
-
if api_key:
|
|
1226
|
-
be.sb_rpc("mc_memory_set", {
|
|
1227
|
-
"p_api_key": api_key,
|
|
1228
|
-
"p_project_id": _PROJECT_ID,
|
|
1229
|
-
"p_agent_name": AGENT_NAME,
|
|
1230
|
-
"p_key": f"session_retro_{_dt.date.today().isoformat()}",
|
|
1231
|
-
"p_value": {
|
|
1232
|
-
"session_id": _SESSION_ID,
|
|
1233
|
-
"agent": AGENT_NAME,
|
|
1234
|
-
"shutdown_at": _dt.datetime.utcnow().isoformat(),
|
|
1235
|
-
"instance_id": _INSTANCE_ID,
|
|
1236
|
-
"auto_generated": True,
|
|
1237
|
-
},
|
|
1238
|
-
})
|
|
1230
|
+
_heartbeat_stop.set()
|
|
1239
1231
|
except Exception:
|
|
1240
|
-
pass
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1232
|
+
pass
|
|
1233
|
+
try:
|
|
1234
|
+
_record_event_bg("shutdown", {"agent": AGENT_NAME, "session_id": _SESSION_ID})
|
|
1235
|
+
except Exception:
|
|
1236
|
+
pass
|
|
1237
|
+
# Fire-and-forget best-effort cleanup in a daemon thread so we do not
|
|
1238
|
+
# block, do not await, and any failure is confined to the thread.
|
|
1239
|
+
def _bg_cleanup():
|
|
1240
|
+
try:
|
|
1241
|
+
import datetime as _dt
|
|
1242
|
+
api_key = _get_api_key()
|
|
1243
|
+
if api_key:
|
|
1244
|
+
be.sb_rpc("mc_memory_set", {
|
|
1245
|
+
"p_api_key": api_key,
|
|
1246
|
+
"p_project_id": _PROJECT_ID,
|
|
1247
|
+
"p_agent_name": AGENT_NAME,
|
|
1248
|
+
"p_key": f"session_retro_{_dt.date.today().isoformat()}",
|
|
1249
|
+
"p_value": {
|
|
1250
|
+
"session_id": _SESSION_ID,
|
|
1251
|
+
"agent": AGENT_NAME,
|
|
1252
|
+
"shutdown_at": _dt.datetime.utcnow().isoformat(),
|
|
1253
|
+
"instance_id": _INSTANCE_ID,
|
|
1254
|
+
"auto_generated": True,
|
|
1255
|
+
},
|
|
1256
|
+
})
|
|
1257
|
+
except Exception:
|
|
1258
|
+
pass
|
|
1259
|
+
try:
|
|
1260
|
+
_release_lease()
|
|
1261
|
+
except Exception:
|
|
1262
|
+
pass
|
|
1245
1263
|
try:
|
|
1246
|
-
|
|
1247
|
-
except Exception
|
|
1248
|
-
|
|
1249
|
-
_heartbeat_stop.set()
|
|
1250
|
-
hb_thread.join(timeout=5)
|
|
1251
|
-
if hb_thread.is_alive():
|
|
1252
|
-
log.warning("heartbeat thread did not stop within 5s — lease already released")
|
|
1253
|
-
await _REALTIME.stop()
|
|
1264
|
+
_threading.Thread(target=_bg_cleanup, daemon=True, name="meshcode-shutdown-cleanup").start()
|
|
1265
|
+
except Exception:
|
|
1266
|
+
pass
|
|
1254
1267
|
|
|
1255
1268
|
|
|
1256
1269
|
# ============================================================
|
|
@@ -1272,6 +1285,25 @@ except Exception:
|
|
|
1272
1285
|
|
|
1273
1286
|
# ----------------- TOOLS -----------------
|
|
1274
1287
|
|
|
1288
|
+
@mcp.tool()
|
|
1289
|
+
async def meshcode_debug_sleep(seconds: int = 30) -> Dict[str, Any]:
|
|
1290
|
+
"""DEBUG-ONLY: sleep N seconds via pure asyncio.sleep (no Realtime, no DB).
|
|
1291
|
+
|
|
1292
|
+
Exists to isolate whether ESC kills meshcode because of our Realtime
|
|
1293
|
+
listener / lifespan infrastructure, or regardless of the tool body.
|
|
1294
|
+
Matches armored.py's sleep_long byte-for-byte at the async layer.
|
|
1295
|
+
"""
|
|
1296
|
+
import asyncio as _asyncio
|
|
1297
|
+
import os as _os
|
|
1298
|
+
try:
|
|
1299
|
+
await _asyncio.sleep(max(1, int(seconds)))
|
|
1300
|
+
return {"ok": True, "slept": int(seconds), "pid": _os.getpid()}
|
|
1301
|
+
except BaseException as e:
|
|
1302
|
+
sys.stderr.write(f"[meshcode-mcp] debug_sleep cancelled with {type(e).__name__}: {e}\n")
|
|
1303
|
+
sys.stderr.flush()
|
|
1304
|
+
raise
|
|
1305
|
+
|
|
1306
|
+
|
|
1275
1307
|
@mcp.tool()
|
|
1276
1308
|
@with_working_status
|
|
1277
1309
|
def meshcode_send(to: str, message: Any, in_reply_to: Optional[str] = None,
|
|
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
|