meshcode 2.6.3__tar.gz → 2.6.5__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 (31) hide show
  1. {meshcode-2.6.3 → meshcode-2.6.5}/PKG-INFO +1 -1
  2. {meshcode-2.6.3 → meshcode-2.6.5}/meshcode/__init__.py +1 -1
  3. {meshcode-2.6.3 → meshcode-2.6.5}/meshcode/meshcode_mcp/server.py +91 -6
  4. {meshcode-2.6.3 → meshcode-2.6.5}/meshcode.egg-info/PKG-INFO +1 -1
  5. {meshcode-2.6.3 → meshcode-2.6.5}/pyproject.toml +1 -1
  6. {meshcode-2.6.3 → meshcode-2.6.5}/README.md +0 -0
  7. {meshcode-2.6.3 → meshcode-2.6.5}/meshcode/cli.py +0 -0
  8. {meshcode-2.6.3 → meshcode-2.6.5}/meshcode/comms_v4.py +0 -0
  9. {meshcode-2.6.3 → meshcode-2.6.5}/meshcode/invites.py +0 -0
  10. {meshcode-2.6.3 → meshcode-2.6.5}/meshcode/launcher.py +0 -0
  11. {meshcode-2.6.3 → meshcode-2.6.5}/meshcode/launcher_install.py +0 -0
  12. {meshcode-2.6.3 → meshcode-2.6.5}/meshcode/meshcode_mcp/__init__.py +0 -0
  13. {meshcode-2.6.3 → meshcode-2.6.5}/meshcode/meshcode_mcp/__main__.py +0 -0
  14. {meshcode-2.6.3 → meshcode-2.6.5}/meshcode/meshcode_mcp/backend.py +0 -0
  15. {meshcode-2.6.3 → meshcode-2.6.5}/meshcode/meshcode_mcp/realtime.py +0 -0
  16. {meshcode-2.6.3 → meshcode-2.6.5}/meshcode/meshcode_mcp/test_backend.py +0 -0
  17. {meshcode-2.6.3 → meshcode-2.6.5}/meshcode/meshcode_mcp/test_realtime.py +0 -0
  18. {meshcode-2.6.3 → meshcode-2.6.5}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  19. {meshcode-2.6.3 → meshcode-2.6.5}/meshcode/preferences.py +0 -0
  20. {meshcode-2.6.3 → meshcode-2.6.5}/meshcode/protocol_v2.py +0 -0
  21. {meshcode-2.6.3 → meshcode-2.6.5}/meshcode/run_agent.py +0 -0
  22. {meshcode-2.6.3 → meshcode-2.6.5}/meshcode/secrets.py +0 -0
  23. {meshcode-2.6.3 → meshcode-2.6.5}/meshcode/self_update.py +0 -0
  24. {meshcode-2.6.3 → meshcode-2.6.5}/meshcode/setup_clients.py +0 -0
  25. {meshcode-2.6.3 → meshcode-2.6.5}/meshcode.egg-info/SOURCES.txt +0 -0
  26. {meshcode-2.6.3 → meshcode-2.6.5}/meshcode.egg-info/dependency_links.txt +0 -0
  27. {meshcode-2.6.3 → meshcode-2.6.5}/meshcode.egg-info/entry_points.txt +0 -0
  28. {meshcode-2.6.3 → meshcode-2.6.5}/meshcode.egg-info/requires.txt +0 -0
  29. {meshcode-2.6.3 → meshcode-2.6.5}/meshcode.egg-info/top_level.txt +0 -0
  30. {meshcode-2.6.3 → meshcode-2.6.5}/setup.cfg +0 -0
  31. {meshcode-2.6.3 → meshcode-2.6.5}/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.6.3
3
+ Version: 2.6.5
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.6.3"
2
+ __version__ = "2.6.5"
@@ -746,7 +746,7 @@ RULES:
746
746
 
747
747
  SESSION START (do these IMMEDIATELY — don't wait for user input):
748
748
  1. meshcode_set_status(status="online", task="ready")
749
- 2. meshcode_check() — read pending messages
749
+ 2. meshcode_check() — read NEW messages only (last_seen is auto-restored from mesh memory on boot, old messages are skipped automatically)
750
750
  3. meshcode_tasks() — check for assigned/pending tasks and claim any unclaimed ones
751
751
  4. meshcode_auto_wake() — scan meshwork health, create tasks for issues found
752
752
  5. meshcode_status() — see who's online
@@ -1124,8 +1124,8 @@ except Exception:
1124
1124
  @mcp.tool()
1125
1125
  @with_working_status
1126
1126
  def meshcode_send(to: str, message: Any, in_reply_to: Optional[str] = None,
1127
- sensitive: bool = False, encrypted: bool = True) -> Dict[str, Any]:
1128
- """Send message. Use "agent@meshwork" for cross-mesh. sensitive=True hides from exports. All messages encrypted by default (AES-256-GCM). Pass encrypted=False to send plaintext."""
1127
+ sensitive: bool = False, encrypted: bool = False) -> Dict[str, Any]:
1128
+ """Send message. Use "agent@meshwork" for cross-mesh. sensitive=True hides from exports. Pass encrypted=True for secrets/credentials (AES-256-GCM)."""
1129
1129
  if isinstance(message, str):
1130
1130
  # Auto-wrap strings but warn if too long — prefer structured JSON
1131
1131
  if len(message) > 400:
@@ -1291,6 +1291,25 @@ _CONSECUTIVE_IDLE_SECONDS = 0
1291
1291
  _AUTO_SLEEP_THRESHOLD = int(os.environ.get("MESHCODE_AUTO_SLEEP_SECONDS", "600")) # default 10min
1292
1292
  _LAST_SEEN_TS: Optional[str] = None # auto-persisted for message dedup
1293
1293
 
1294
+ # Hydrate _LAST_SEEN_TS from mesh memory on boot so restarts skip old messages
1295
+ try:
1296
+ _ls_result = be.sb_rpc("mc_memory_get", {
1297
+ "p_api_key": _get_api_key(),
1298
+ "p_agent_name": AGENT_NAME,
1299
+ "p_key": "last_seen",
1300
+ "p_project_name": PROJECT_NAME,
1301
+ })
1302
+ if isinstance(_ls_result, dict) and _ls_result.get("ok"):
1303
+ _ls_val = _ls_result.get("value")
1304
+ if isinstance(_ls_val, dict) and _ls_val.get("_raw"):
1305
+ _LAST_SEEN_TS = str(_ls_val["_raw"])
1306
+ elif isinstance(_ls_val, str):
1307
+ _LAST_SEEN_TS = _ls_val
1308
+ if _LAST_SEEN_TS:
1309
+ print(f"[meshcode] Restored last_seen={_LAST_SEEN_TS} from mesh memory.", file=sys.stderr)
1310
+ except Exception as _e:
1311
+ print(f"[meshcode] Could not restore last_seen: {_e}", file=sys.stderr)
1312
+
1294
1313
 
1295
1314
  def _get_pending_tasks_summary() -> Optional[List[Dict[str, str]]]:
1296
1315
  """Fetch open/in_progress tasks assigned to this agent. Returns compact list or None."""
@@ -1306,7 +1325,7 @@ def _get_pending_tasks_summary() -> Optional[List[Dict[str, str]]]:
1306
1325
  {"id": t["id"][:8], "title": t["title"][:80], "priority": t.get("priority", "normal"), "status": t["status"]}
1307
1326
  for t in tasks
1308
1327
  if t.get("status") in ("open", "in_progress")
1309
- and (t.get("assigned_to") in (AGENT_NAME, "*", None) or t.get("claimed_by") == AGENT_NAME)
1328
+ and (t.get("assignee") in (AGENT_NAME, "*", None) or t.get("claimed_by") == AGENT_NAME)
1310
1329
  ]
1311
1330
  return pending if pending else None
1312
1331
  except Exception:
@@ -1337,7 +1356,7 @@ async def meshcode_wait(timeout_seconds: int = 120, include_acks: bool = False)
1337
1356
  # The in-memory dedupe (_SEEN_MSG_IDS) can mark messages as "seen" via
1338
1357
  # realtime without the agent actually processing them. Always check DB.
1339
1358
  try:
1340
- db_pending = be.count_pending(_PROJECT_ID, AGENT_NAME)
1359
+ db_pending = be.count_pending(_PROJECT_ID, AGENT_NAME, api_key=_get_api_key())
1341
1360
  if db_pending and db_pending > 0:
1342
1361
  # Fetch and return the messages — mark_read=True so the next
1343
1362
  # meshcode_wait() won't re-refuse with the same messages.
@@ -1404,11 +1423,49 @@ async def meshcode_wait(timeout_seconds: int = 120, include_acks: bool = False)
1404
1423
  latest_ts = max((m.get("ts", "") for m in msgs), default="")
1405
1424
  if latest_ts:
1406
1425
  _LAST_SEEN_TS = latest_ts
1426
+ # Persist to mesh memory so next session resumes here
1427
+ try:
1428
+ be.sb_rpc("mc_memory_set", {
1429
+ "p_api_key": _get_api_key(),
1430
+ "p_agent_name": AGENT_NAME,
1431
+ "p_key": "last_seen",
1432
+ "p_value": {"_raw": latest_ts},
1433
+ "p_project_name": PROJECT_NAME,
1434
+ })
1435
+ except Exception:
1436
+ pass # best-effort, never block wait loop
1407
1437
  return result
1408
1438
  finally:
1409
1439
  _IN_WAIT = False
1410
1440
 
1411
1441
 
1442
+ def _mark_realtime_msgs_read_in_db(messages: List[Dict[str, Any]]) -> None:
1443
+ """Mark realtime-delivered messages as read in DB (background thread).
1444
+
1445
+ Without this, messages delivered via realtime stay read=false in the DB
1446
+ and inflate pending counts when the agent restarts.
1447
+ """
1448
+ import threading
1449
+ msg_ids = [m.get("id") for m in messages if m.get("id")]
1450
+ if not msg_ids:
1451
+ return
1452
+ api_key = _get_api_key()
1453
+ if not api_key:
1454
+ return
1455
+
1456
+ def _do():
1457
+ for mid in msg_ids:
1458
+ try:
1459
+ be.sb_rpc("mc_mark_message_read", {
1460
+ "p_api_key": api_key,
1461
+ "p_project_id": _PROJECT_ID,
1462
+ "p_message_id": mid,
1463
+ })
1464
+ except Exception:
1465
+ pass
1466
+ threading.Thread(target=_do, daemon=True).start()
1467
+
1468
+
1412
1469
  async def _meshcode_wait_inner(actual_timeout: int, include_acks: bool) -> Dict[str, Any]:
1413
1470
 
1414
1471
  def _return_from_buffered(buffered: List[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
@@ -1436,6 +1493,9 @@ async def _meshcode_wait_inner(actual_timeout: int, include_acks: bool) -> Dict[
1436
1493
  # are no real messages or done signals, treat as nothing useful.
1437
1494
  if not split["messages"] and not split["done_signals"] and not split["acks"]:
1438
1495
  return None
1496
+ # Mark realtime-delivered messages as read in DB so they don't
1497
+ # inflate pending counts on agent restart.
1498
+ _mark_realtime_msgs_read_in_db(deduped)
1439
1499
  out: Dict[str, Any] = {
1440
1500
  "got_message": True,
1441
1501
  "source": "realtime",
@@ -1468,6 +1528,31 @@ async def _meshcode_wait_inner(actual_timeout: int, include_acks: bool) -> Dict[
1468
1528
  # Realtime unavailable — plain sleep fallback so we still honor timeout.
1469
1529
  await asyncio.sleep(actual_timeout)
1470
1530
 
1531
+ # Fallback: poll DB for messages that realtime may have missed
1532
+ # (connection drop, startup race, etc.)
1533
+ try:
1534
+ api_key = _get_api_key()
1535
+ if api_key:
1536
+ db_pending = be.count_pending(_PROJECT_ID, AGENT_NAME, api_key=api_key)
1537
+ if db_pending and db_pending > 0:
1538
+ raw = be.read_inbox(_PROJECT_ID, AGENT_NAME, mark_read=True, api_key=api_key)
1539
+ if raw:
1540
+ msgs = [
1541
+ {"from": m["from_agent"], "type": m.get("type", "msg"),
1542
+ "ts": m.get("created_at"), "payload": m.get("payload", {}),
1543
+ "id": m.get("id"), "parent_id": m.get("parent_msg_id")}
1544
+ for m in raw
1545
+ ]
1546
+ deduped = _filter_and_mark(msgs)
1547
+ if deduped:
1548
+ split = _split_messages(deduped)
1549
+ if not include_acks:
1550
+ split["acks"] = []
1551
+ if split["messages"] or split["done_signals"]:
1552
+ return {"got_message": True, "source": "db_fallback", **split}
1553
+ except Exception:
1554
+ pass
1555
+
1471
1556
  # Check if there's any pending work before returning timeout
1472
1557
  pending_tasks = _get_pending_tasks_summary()
1473
1558
  out: Dict[str, Any] = {"timed_out": True}
@@ -1750,7 +1835,7 @@ def meshcode_auto_wake() -> Dict[str, Any]:
1750
1835
  task_result = be.task_list(api_key, _PROJECT_ID, AGENT_NAME, status_filter="open")
1751
1836
  if isinstance(task_result, dict) and task_result.get("ok"):
1752
1837
  open_tasks = task_result.get("tasks", [])
1753
- unassigned = [t for t in open_tasks if not t.get("assigned_to") or t.get("assigned_to") == "*"]
1838
+ unassigned = [t for t in open_tasks if not t.get("assignee") or t.get("assignee") == "*"]
1754
1839
  if len(unassigned) > 3:
1755
1840
  suggestions.append({
1756
1841
  "title": f"Task backlog: {len(unassigned)} unassigned open tasks",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.6.3
3
+ Version: 2.6.5
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.6.3"
7
+ version = "2.6.5"
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
File without changes
File without changes
File without changes
File without changes
File without changes