meshcode 2.4.3__tar.gz → 2.4.4__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 (30) hide show
  1. {meshcode-2.4.3 → meshcode-2.4.4}/PKG-INFO +1 -1
  2. {meshcode-2.4.3 → meshcode-2.4.4}/meshcode/__init__.py +1 -1
  3. {meshcode-2.4.3 → meshcode-2.4.4}/meshcode/meshcode_mcp/server.py +69 -9
  4. {meshcode-2.4.3 → meshcode-2.4.4}/meshcode.egg-info/PKG-INFO +1 -1
  5. {meshcode-2.4.3 → meshcode-2.4.4}/pyproject.toml +1 -1
  6. {meshcode-2.4.3 → meshcode-2.4.4}/README.md +0 -0
  7. {meshcode-2.4.3 → meshcode-2.4.4}/meshcode/cli.py +0 -0
  8. {meshcode-2.4.3 → meshcode-2.4.4}/meshcode/comms_v4.py +0 -0
  9. {meshcode-2.4.3 → meshcode-2.4.4}/meshcode/invites.py +0 -0
  10. {meshcode-2.4.3 → meshcode-2.4.4}/meshcode/launcher.py +0 -0
  11. {meshcode-2.4.3 → meshcode-2.4.4}/meshcode/launcher_install.py +0 -0
  12. {meshcode-2.4.3 → meshcode-2.4.4}/meshcode/meshcode_mcp/__init__.py +0 -0
  13. {meshcode-2.4.3 → meshcode-2.4.4}/meshcode/meshcode_mcp/__main__.py +0 -0
  14. {meshcode-2.4.3 → meshcode-2.4.4}/meshcode/meshcode_mcp/backend.py +0 -0
  15. {meshcode-2.4.3 → meshcode-2.4.4}/meshcode/meshcode_mcp/realtime.py +0 -0
  16. {meshcode-2.4.3 → meshcode-2.4.4}/meshcode/meshcode_mcp/test_backend.py +0 -0
  17. {meshcode-2.4.3 → meshcode-2.4.4}/meshcode/meshcode_mcp/test_realtime.py +0 -0
  18. {meshcode-2.4.3 → meshcode-2.4.4}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  19. {meshcode-2.4.3 → meshcode-2.4.4}/meshcode/preferences.py +0 -0
  20. {meshcode-2.4.3 → meshcode-2.4.4}/meshcode/protocol_v2.py +0 -0
  21. {meshcode-2.4.3 → meshcode-2.4.4}/meshcode/run_agent.py +0 -0
  22. {meshcode-2.4.3 → meshcode-2.4.4}/meshcode/secrets.py +0 -0
  23. {meshcode-2.4.3 → meshcode-2.4.4}/meshcode/self_update.py +0 -0
  24. {meshcode-2.4.3 → meshcode-2.4.4}/meshcode/setup_clients.py +0 -0
  25. {meshcode-2.4.3 → meshcode-2.4.4}/meshcode.egg-info/SOURCES.txt +0 -0
  26. {meshcode-2.4.3 → meshcode-2.4.4}/meshcode.egg-info/dependency_links.txt +0 -0
  27. {meshcode-2.4.3 → meshcode-2.4.4}/meshcode.egg-info/entry_points.txt +0 -0
  28. {meshcode-2.4.3 → meshcode-2.4.4}/meshcode.egg-info/requires.txt +0 -0
  29. {meshcode-2.4.3 → meshcode-2.4.4}/meshcode.egg-info/top_level.txt +0 -0
  30. {meshcode-2.4.3 → meshcode-2.4.4}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.4.3
3
+ Version: 2.4.4
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.4.3"
2
+ __version__ = "2.4.4"
@@ -183,6 +183,21 @@ except Exception:
183
183
  _be_path = ""
184
184
 
185
185
 
186
+ def _capture_session() -> None:
187
+ """Stash the MCP session ref for silent auto-wake when agent is idle."""
188
+ global _STASHED_SESSION
189
+ if _STASHED_SESSION is not None:
190
+ return
191
+ try:
192
+ srv = mcp._mcp_server
193
+ ctx = getattr(srv, "request_context", None)
194
+ if ctx and getattr(ctx, "session", None):
195
+ _STASHED_SESSION = ctx.session
196
+ log.info("[meshcode] MCP session stashed for silent auto-wake")
197
+ except Exception:
198
+ pass
199
+
200
+
186
201
  def _check_hot_reload() -> None:
187
202
  """If backend.py was modified since boot/last reload, reimport it."""
188
203
  global _be_mtime, be
@@ -410,6 +425,7 @@ def with_working_status(func):
410
425
  @_functools.wraps(func)
411
426
  async def awrapper(*args, **kwargs):
412
427
  _check_hot_reload()
428
+ _capture_session() # stash session on first tool call for silent auto-wake
413
429
  if not skip:
414
430
  _set_state("working", name)
415
431
  _record_event_bg("tool_call", {"tool": name, "args_keys": list(kwargs.keys())})
@@ -426,6 +442,7 @@ def with_working_status(func):
426
442
  @_functools.wraps(func)
427
443
  def swrapper(*args, **kwargs):
428
444
  _check_hot_reload()
445
+ _capture_session() # stash session on first tool call for silent auto-wake
429
446
  if not skip:
430
447
  _set_state("working", name)
431
448
  _record_event_bg("tool_call", {"tool": name, "args_keys": list(kwargs.keys())})
@@ -680,22 +697,65 @@ async def _on_new_message(msg: Dict[str, Any]) -> None:
680
697
  # Auto-wake: if agent is idle (not in wait loop), nudge the terminal
681
698
  from_agent = msg.get("from") or msg.get("from_agent") or "unknown"
682
699
  payload = msg.get("payload") or {}
683
- preview = payload.get("text", "") if isinstance(payload, dict) else str(payload)
700
+ if isinstance(payload, dict):
701
+ # Try common content fields in order of usefulness
702
+ preview = (
703
+ payload.get("text")
704
+ or payload.get("message")
705
+ or payload.get("action")
706
+ or payload.get("context")
707
+ or payload.get("type")
708
+ or (str(next(iter(payload.values()))) if payload else "")
709
+ )
710
+ preview = str(preview) if preview else "(see inbox)"
711
+ else:
712
+ preview = str(payload)
684
713
  try:
685
714
  _try_auto_wake(from_agent, preview[:60])
686
715
  except Exception:
687
716
  pass # auto-wake is best-effort, never block message handling
688
717
 
718
+ # SILENT WAKE: 3-tier strategy, all without OS keystrokes
719
+ # 1. send_resource_updated (lightweight cache invalidation)
720
+ # 2. create_message (sampling) — server PUSH that wakes the LLM (if client supports)
721
+ # 3. AppleScript (handled above) — fallback for clients that don't support either
689
722
  try:
690
- srv = mcp._mcp_server # FastMCP exposes the lowlevel server here
691
- ctx = getattr(srv, "request_context", None)
692
- if ctx and getattr(ctx, "session", None):
693
- from pydantic import AnyUrl
694
- await ctx.session.send_resource_updated(AnyUrl("meshcode://inbox"))
695
- log.info(f"sent MCP resource_updated notification for msg from {msg.get('from')}")
723
+ from pydantic import AnyUrl
724
+ url = AnyUrl("meshcode://inbox")
725
+ session = _STASHED_SESSION
726
+ if session is None:
727
+ srv = mcp._mcp_server
728
+ ctx = getattr(srv, "request_context", None)
729
+ if ctx and getattr(ctx, "session", None):
730
+ session = ctx.session
731
+ if session is not None:
732
+ # 1. resource_updated (always cheap, may not wake)
733
+ try:
734
+ await session.send_resource_updated(url)
735
+ log.info(f"silent wake A: resource_updated sent for msg from {msg.get('from')}")
736
+ except Exception as _re:
737
+ log.debug(f"resource_updated failed: {_re}")
738
+ # 2. sampling/create_message — server PUSH that asks the LLM to think
739
+ # Only attempt if client advertised sampling capability
740
+ try:
741
+ client_caps = getattr(session, "client_capabilities", None)
742
+ if client_caps and getattr(client_caps, "sampling", None):
743
+ from mcp.types import SamplingMessage, TextContent
744
+ nudge_text = f"You have a new mesh message from {from_agent}: {preview[:80]}. Call meshcode_check() to read your inbox, then process it. Stay in meshcode_wait() afterward."
745
+ await session.create_message(
746
+ messages=[SamplingMessage(role="user", content=TextContent(type="text", text=nudge_text))],
747
+ max_tokens=4096,
748
+ system_prompt="A new mesh message arrived while you were idle. Process it now.",
749
+ )
750
+ log.info(f"silent wake B: sampling/create_message PUSHED for msg from {msg.get('from')}")
751
+ except Exception as _ce:
752
+ log.debug(f"sampling/create_message failed: {_ce}")
696
753
  except Exception as e:
697
- log.debug(f"send_resource_updated unavailable: {e}")
754
+ log.debug(f"silent wake unavailable: {e}")
755
+
698
756
 
757
+ # Stashed MCP session for silent auto-wake (works when agent is idle, no active tool call)
758
+ _STASHED_SESSION = None
699
759
 
700
760
  _heartbeat_stop = _threading.Event()
701
761
 
@@ -986,7 +1046,7 @@ def _detect_global_done(messages: List[Dict[str, Any]]) -> Optional[Dict[str, An
986
1046
 
987
1047
  @mcp.tool()
988
1048
  @with_working_status
989
- async def meshcode_wait(timeout_seconds: int = 120, include_acks: bool = False) -> Dict[str, Any]:
1049
+ async def meshcode_wait(timeout_seconds: int = 60, include_acks: bool = False) -> Dict[str, Any]:
990
1050
  """Block until a mesh message arrives or timeout. Your idle state.
991
1051
 
992
1052
  Args:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.4.3
3
+ Version: 2.4.4
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.4.3"
7
+ version = "2.4.4"
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