meshcode 2.6.8__tar.gz → 2.6.9__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.8 → meshcode-2.6.9}/PKG-INFO +1 -1
  2. {meshcode-2.6.8 → meshcode-2.6.9}/meshcode/__init__.py +1 -1
  3. {meshcode-2.6.8 → meshcode-2.6.9}/meshcode/meshcode_mcp/realtime.py +23 -0
  4. {meshcode-2.6.8 → meshcode-2.6.9}/meshcode/meshcode_mcp/server.py +36 -5
  5. {meshcode-2.6.8 → meshcode-2.6.9}/meshcode.egg-info/PKG-INFO +1 -1
  6. {meshcode-2.6.8 → meshcode-2.6.9}/pyproject.toml +1 -1
  7. {meshcode-2.6.8 → meshcode-2.6.9}/README.md +0 -0
  8. {meshcode-2.6.8 → meshcode-2.6.9}/meshcode/cli.py +0 -0
  9. {meshcode-2.6.8 → meshcode-2.6.9}/meshcode/comms_v4.py +0 -0
  10. {meshcode-2.6.8 → meshcode-2.6.9}/meshcode/invites.py +0 -0
  11. {meshcode-2.6.8 → meshcode-2.6.9}/meshcode/launcher.py +0 -0
  12. {meshcode-2.6.8 → meshcode-2.6.9}/meshcode/launcher_install.py +0 -0
  13. {meshcode-2.6.8 → meshcode-2.6.9}/meshcode/meshcode_mcp/__init__.py +0 -0
  14. {meshcode-2.6.8 → meshcode-2.6.9}/meshcode/meshcode_mcp/__main__.py +0 -0
  15. {meshcode-2.6.8 → meshcode-2.6.9}/meshcode/meshcode_mcp/backend.py +0 -0
  16. {meshcode-2.6.8 → meshcode-2.6.9}/meshcode/meshcode_mcp/test_backend.py +0 -0
  17. {meshcode-2.6.8 → meshcode-2.6.9}/meshcode/meshcode_mcp/test_realtime.py +0 -0
  18. {meshcode-2.6.8 → meshcode-2.6.9}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  19. {meshcode-2.6.8 → meshcode-2.6.9}/meshcode/preferences.py +0 -0
  20. {meshcode-2.6.8 → meshcode-2.6.9}/meshcode/protocol_v2.py +0 -0
  21. {meshcode-2.6.8 → meshcode-2.6.9}/meshcode/run_agent.py +0 -0
  22. {meshcode-2.6.8 → meshcode-2.6.9}/meshcode/secrets.py +0 -0
  23. {meshcode-2.6.8 → meshcode-2.6.9}/meshcode/self_update.py +0 -0
  24. {meshcode-2.6.8 → meshcode-2.6.9}/meshcode/setup_clients.py +0 -0
  25. {meshcode-2.6.8 → meshcode-2.6.9}/meshcode.egg-info/SOURCES.txt +0 -0
  26. {meshcode-2.6.8 → meshcode-2.6.9}/meshcode.egg-info/dependency_links.txt +0 -0
  27. {meshcode-2.6.8 → meshcode-2.6.9}/meshcode.egg-info/entry_points.txt +0 -0
  28. {meshcode-2.6.8 → meshcode-2.6.9}/meshcode.egg-info/requires.txt +0 -0
  29. {meshcode-2.6.8 → meshcode-2.6.9}/meshcode.egg-info/top_level.txt +0 -0
  30. {meshcode-2.6.8 → meshcode-2.6.9}/setup.cfg +0 -0
  31. {meshcode-2.6.8 → meshcode-2.6.9}/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.8
3
+ Version: 2.6.9
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.8"
2
+ __version__ = "2.6.9"
@@ -148,6 +148,25 @@ class RealtimeListener:
148
148
  }
149
149
  await ws.send(json.dumps(join_msg))
150
150
 
151
+ # Wait for phx_reply to confirm subscription was accepted.
152
+ self._subscription_ok = False
153
+ try:
154
+ reply_raw = await asyncio.wait_for(ws.recv(), timeout=10.0)
155
+ reply = json.loads(reply_raw)
156
+ reply_status = (reply.get("payload") or {}).get("status")
157
+ if reply_status == "ok":
158
+ self._subscription_ok = True
159
+ log.info(f"Realtime subscription OK for {self.agent_name} on {topic}")
160
+ else:
161
+ log.error(
162
+ f"Realtime subscription FAILED for {self.agent_name}: "
163
+ f"status={reply_status} payload={reply.get('payload')}"
164
+ )
165
+ except asyncio.TimeoutError:
166
+ log.error(f"Realtime subscription TIMEOUT — no phx_reply in 10s for {self.agent_name}")
167
+ except Exception as e:
168
+ log.error(f"Realtime subscription error reading phx_reply: {e}")
169
+
151
170
  # Heartbeat task to keep the connection alive
152
171
  heartbeat_task = asyncio.create_task(self._heartbeat(ws))
153
172
  try:
@@ -247,3 +266,7 @@ class RealtimeListener:
247
266
  @property
248
267
  def is_connected(self) -> bool:
249
268
  return self._connected
269
+
270
+ @property
271
+ def is_subscribed(self) -> bool:
272
+ return self._connected and getattr(self, "_subscription_ok", False)
@@ -1525,7 +1525,11 @@ async def _meshcode_wait_inner(actual_timeout: int, include_acks: bool) -> Dict[
1525
1525
  if shaped:
1526
1526
  return shaped
1527
1527
 
1528
- # 2) Real async wait zero CPU, zero Supabase calls.
1528
+ # Determine if realtime is actually delivering events.
1529
+ _rt_live = _REALTIME and _REALTIME.is_subscribed
1530
+
1531
+ if _rt_live:
1532
+ # 2a) Real async wait — zero CPU, zero Supabase calls.
1529
1533
  woke = await _REALTIME.wait_for_message(timeout=float(actual_timeout))
1530
1534
  if woke:
1531
1535
  buffered = _REALTIME.drain()
@@ -1534,11 +1538,37 @@ async def _meshcode_wait_inner(actual_timeout: int, include_acks: bool) -> Dict[
1534
1538
  if shaped:
1535
1539
  return shaped
1536
1540
  else:
1537
- # Realtime unavailableplain sleep fallback so we still honor timeout.
1538
- await asyncio.sleep(actual_timeout)
1541
+ # 2b) Realtime NOT subscribed aggressive DB polling every 5s
1542
+ # so messages arrive within seconds, not after 120s timeout.
1543
+ _poll_interval = 5
1544
+ _elapsed = 0
1545
+ while _elapsed < actual_timeout:
1546
+ await asyncio.sleep(min(_poll_interval, actual_timeout - _elapsed))
1547
+ _elapsed += _poll_interval
1548
+ try:
1549
+ api_key = _get_api_key()
1550
+ if api_key:
1551
+ db_pending = be.count_pending(_PROJECT_ID, AGENT_NAME, api_key=api_key)
1552
+ if db_pending and db_pending > 0:
1553
+ raw = be.read_inbox(_PROJECT_ID, AGENT_NAME, mark_read=True, api_key=api_key)
1554
+ if raw:
1555
+ msgs = [
1556
+ {"from": m["from_agent"], "type": m.get("type", "msg"),
1557
+ "ts": m.get("created_at"), "payload": m.get("payload", {}),
1558
+ "id": m.get("id"), "parent_id": m.get("parent_msg_id")}
1559
+ for m in raw
1560
+ ]
1561
+ deduped = _filter_and_mark(msgs)
1562
+ if deduped:
1563
+ split = _split_messages(deduped)
1564
+ if not include_acks:
1565
+ split["acks"] = []
1566
+ if split["messages"] or split["done_signals"]:
1567
+ return {"got_message": True, "source": "db_poll_fallback", **split}
1568
+ except Exception:
1569
+ pass
1539
1570
 
1540
- # Fallback: poll DB for messages that realtime may have missed
1541
- # (connection drop, startup race, etc.)
1571
+ # Final fallback: one last DB check (covers realtime path missing msgs)
1542
1572
  try:
1543
1573
  api_key = _get_api_key()
1544
1574
  if api_key:
@@ -1647,6 +1677,7 @@ def meshcode_check(include_acks: bool = False, since: Optional[str] = None) -> D
1647
1677
  "agent": AGENT_NAME,
1648
1678
  "project": PROJECT_NAME,
1649
1679
  "realtime_connected": _REALTIME.is_connected if _REALTIME else False,
1680
+ "realtime_subscribed": _REALTIME.is_subscribed if _REALTIME else False,
1650
1681
  **split,
1651
1682
  }
1652
1683
  # Auto-inject pending tasks
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.6.8
3
+ Version: 2.6.9
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.8"
7
+ version = "2.6.9"
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