meshcode 2.4.3__tar.gz → 2.4.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 (30) hide show
  1. {meshcode-2.4.3 → meshcode-2.4.5}/PKG-INFO +1 -1
  2. {meshcode-2.4.3 → meshcode-2.4.5}/meshcode/__init__.py +1 -1
  3. {meshcode-2.4.3 → meshcode-2.4.5}/meshcode/meshcode_mcp/server.py +89 -11
  4. {meshcode-2.4.3 → meshcode-2.4.5}/meshcode.egg-info/PKG-INFO +1 -1
  5. {meshcode-2.4.3 → meshcode-2.4.5}/pyproject.toml +1 -1
  6. {meshcode-2.4.3 → meshcode-2.4.5}/README.md +0 -0
  7. {meshcode-2.4.3 → meshcode-2.4.5}/meshcode/cli.py +0 -0
  8. {meshcode-2.4.3 → meshcode-2.4.5}/meshcode/comms_v4.py +0 -0
  9. {meshcode-2.4.3 → meshcode-2.4.5}/meshcode/invites.py +0 -0
  10. {meshcode-2.4.3 → meshcode-2.4.5}/meshcode/launcher.py +0 -0
  11. {meshcode-2.4.3 → meshcode-2.4.5}/meshcode/launcher_install.py +0 -0
  12. {meshcode-2.4.3 → meshcode-2.4.5}/meshcode/meshcode_mcp/__init__.py +0 -0
  13. {meshcode-2.4.3 → meshcode-2.4.5}/meshcode/meshcode_mcp/__main__.py +0 -0
  14. {meshcode-2.4.3 → meshcode-2.4.5}/meshcode/meshcode_mcp/backend.py +0 -0
  15. {meshcode-2.4.3 → meshcode-2.4.5}/meshcode/meshcode_mcp/realtime.py +0 -0
  16. {meshcode-2.4.3 → meshcode-2.4.5}/meshcode/meshcode_mcp/test_backend.py +0 -0
  17. {meshcode-2.4.3 → meshcode-2.4.5}/meshcode/meshcode_mcp/test_realtime.py +0 -0
  18. {meshcode-2.4.3 → meshcode-2.4.5}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  19. {meshcode-2.4.3 → meshcode-2.4.5}/meshcode/preferences.py +0 -0
  20. {meshcode-2.4.3 → meshcode-2.4.5}/meshcode/protocol_v2.py +0 -0
  21. {meshcode-2.4.3 → meshcode-2.4.5}/meshcode/run_agent.py +0 -0
  22. {meshcode-2.4.3 → meshcode-2.4.5}/meshcode/secrets.py +0 -0
  23. {meshcode-2.4.3 → meshcode-2.4.5}/meshcode/self_update.py +0 -0
  24. {meshcode-2.4.3 → meshcode-2.4.5}/meshcode/setup_clients.py +0 -0
  25. {meshcode-2.4.3 → meshcode-2.4.5}/meshcode.egg-info/SOURCES.txt +0 -0
  26. {meshcode-2.4.3 → meshcode-2.4.5}/meshcode.egg-info/dependency_links.txt +0 -0
  27. {meshcode-2.4.3 → meshcode-2.4.5}/meshcode.egg-info/entry_points.txt +0 -0
  28. {meshcode-2.4.3 → meshcode-2.4.5}/meshcode.egg-info/requires.txt +0 -0
  29. {meshcode-2.4.3 → meshcode-2.4.5}/meshcode.egg-info/top_level.txt +0 -0
  30. {meshcode-2.4.3 → meshcode-2.4.5}/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.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.4.3"
2
+ __version__ = "2.4.5"
@@ -37,14 +37,32 @@ _AUTO_WAKE = os.environ.get("MESHCODE_AUTO_WAKE", "1").lower() not in ("0", "fal
37
37
 
38
38
 
39
39
  def _try_auto_wake(from_agent: str, preview: str) -> None:
40
- """Inject a nudge into the terminal if the agent is idle (not in wait).
40
+ """Inject a nudge into the terminal ONLY when agent is truly sleeping.
41
41
 
42
42
  macOS: AppleScript → keystroke into Terminal/iTerm2
43
43
  Windows: PowerShell SendKeys to the console window
44
44
  Linux: xdotool (best-effort)
45
+
46
+ Conditions to fire (ALL must be true):
47
+ - _AUTO_WAKE is enabled
48
+ - NOT in meshcode_wait (wait loop already handles delivery)
49
+ - _current_state is 'sleeping' (truly idle, not working, not just online)
50
+ - LLM CPU is low (not actively generating)
51
+ Otherwise the keystroke would interrupt the user's typing or
52
+ the LLM mid-thought.
45
53
  """
46
- if _IN_WAIT or not _AUTO_WAKE:
54
+ if not _AUTO_WAKE or _IN_WAIT:
55
+ return
56
+ # Only fire when agent is actually sleeping — never when working or online
57
+ # (working = LLM generating; online = brief post-tool window; both would be interrupted)
58
+ if _current_state != 'sleeping':
47
59
  return
60
+ # Belt-and-suspenders: also check parent CPU is low (LLM not generating right now)
61
+ try:
62
+ if _get_parent_cpu() > 5.0:
63
+ return # LLM is actively generating, don't interrupt
64
+ except Exception:
65
+ pass
48
66
  import subprocess, platform, re
49
67
  # Sanitize inputs: strip everything except alphanumeric, spaces, basic punctuation
50
68
  safe_agent = re.sub(r'[^a-zA-Z0-9_\- ]', '', from_agent)[:50]
@@ -183,6 +201,21 @@ except Exception:
183
201
  _be_path = ""
184
202
 
185
203
 
204
+ def _capture_session() -> None:
205
+ """Stash the MCP session ref for silent auto-wake when agent is idle."""
206
+ global _STASHED_SESSION
207
+ if _STASHED_SESSION is not None:
208
+ return
209
+ try:
210
+ srv = mcp._mcp_server
211
+ ctx = getattr(srv, "request_context", None)
212
+ if ctx and getattr(ctx, "session", None):
213
+ _STASHED_SESSION = ctx.session
214
+ log.info("[meshcode] MCP session stashed for silent auto-wake")
215
+ except Exception:
216
+ pass
217
+
218
+
186
219
  def _check_hot_reload() -> None:
187
220
  """If backend.py was modified since boot/last reload, reimport it."""
188
221
  global _be_mtime, be
@@ -410,6 +443,7 @@ def with_working_status(func):
410
443
  @_functools.wraps(func)
411
444
  async def awrapper(*args, **kwargs):
412
445
  _check_hot_reload()
446
+ _capture_session() # stash session on first tool call for silent auto-wake
413
447
  if not skip:
414
448
  _set_state("working", name)
415
449
  _record_event_bg("tool_call", {"tool": name, "args_keys": list(kwargs.keys())})
@@ -426,6 +460,7 @@ def with_working_status(func):
426
460
  @_functools.wraps(func)
427
461
  def swrapper(*args, **kwargs):
428
462
  _check_hot_reload()
463
+ _capture_session() # stash session on first tool call for silent auto-wake
429
464
  if not skip:
430
465
  _set_state("working", name)
431
466
  _record_event_bg("tool_call", {"tool": name, "args_keys": list(kwargs.keys())})
@@ -680,22 +715,65 @@ async def _on_new_message(msg: Dict[str, Any]) -> None:
680
715
  # Auto-wake: if agent is idle (not in wait loop), nudge the terminal
681
716
  from_agent = msg.get("from") or msg.get("from_agent") or "unknown"
682
717
  payload = msg.get("payload") or {}
683
- preview = payload.get("text", "") if isinstance(payload, dict) else str(payload)
718
+ if isinstance(payload, dict):
719
+ # Try common content fields in order of usefulness
720
+ preview = (
721
+ payload.get("text")
722
+ or payload.get("message")
723
+ or payload.get("action")
724
+ or payload.get("context")
725
+ or payload.get("type")
726
+ or (str(next(iter(payload.values()))) if payload else "")
727
+ )
728
+ preview = str(preview) if preview else "(see inbox)"
729
+ else:
730
+ preview = str(payload)
684
731
  try:
685
732
  _try_auto_wake(from_agent, preview[:60])
686
733
  except Exception:
687
734
  pass # auto-wake is best-effort, never block message handling
688
735
 
736
+ # SILENT WAKE: 3-tier strategy, all without OS keystrokes
737
+ # 1. send_resource_updated (lightweight cache invalidation)
738
+ # 2. create_message (sampling) — server PUSH that wakes the LLM (if client supports)
739
+ # 3. AppleScript (handled above) — fallback for clients that don't support either
689
740
  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')}")
741
+ from pydantic import AnyUrl
742
+ url = AnyUrl("meshcode://inbox")
743
+ session = _STASHED_SESSION
744
+ if session is None:
745
+ srv = mcp._mcp_server
746
+ ctx = getattr(srv, "request_context", None)
747
+ if ctx and getattr(ctx, "session", None):
748
+ session = ctx.session
749
+ if session is not None:
750
+ # 1. resource_updated (always cheap, may not wake)
751
+ try:
752
+ await session.send_resource_updated(url)
753
+ log.info(f"silent wake A: resource_updated sent for msg from {msg.get('from')}")
754
+ except Exception as _re:
755
+ log.debug(f"resource_updated failed: {_re}")
756
+ # 2. sampling/create_message — server PUSH that asks the LLM to think
757
+ # Only attempt if client advertised sampling capability
758
+ try:
759
+ client_caps = getattr(session, "client_capabilities", None)
760
+ if client_caps and getattr(client_caps, "sampling", None):
761
+ from mcp.types import SamplingMessage, TextContent
762
+ 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."
763
+ await session.create_message(
764
+ messages=[SamplingMessage(role="user", content=TextContent(type="text", text=nudge_text))],
765
+ max_tokens=4096,
766
+ system_prompt="A new mesh message arrived while you were idle. Process it now.",
767
+ )
768
+ log.info(f"silent wake B: sampling/create_message PUSHED for msg from {msg.get('from')}")
769
+ except Exception as _ce:
770
+ log.debug(f"sampling/create_message failed: {_ce}")
696
771
  except Exception as e:
697
- log.debug(f"send_resource_updated unavailable: {e}")
772
+ log.debug(f"silent wake unavailable: {e}")
773
+
698
774
 
775
+ # Stashed MCP session for silent auto-wake (works when agent is idle, no active tool call)
776
+ _STASHED_SESSION = None
699
777
 
700
778
  _heartbeat_stop = _threading.Event()
701
779
 
@@ -986,7 +1064,7 @@ def _detect_global_done(messages: List[Dict[str, Any]]) -> Optional[Dict[str, An
986
1064
 
987
1065
  @mcp.tool()
988
1066
  @with_working_status
989
- async def meshcode_wait(timeout_seconds: int = 120, include_acks: bool = False) -> Dict[str, Any]:
1067
+ async def meshcode_wait(timeout_seconds: int = 60, include_acks: bool = False) -> Dict[str, Any]:
990
1068
  """Block until a mesh message arrives or timeout. Your idle state.
991
1069
 
992
1070
  Args:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.4.3
3
+ Version: 2.4.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.4.3"
7
+ version = "2.4.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