meshcode 2.0.6__tar.gz → 2.0.8__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 (29) hide show
  1. {meshcode-2.0.6 → meshcode-2.0.8}/PKG-INFO +1 -1
  2. {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/__init__.py +1 -1
  3. {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/meshcode_mcp/backend.py +14 -2
  4. {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/meshcode_mcp/server.py +71 -20
  5. {meshcode-2.0.6 → meshcode-2.0.8}/meshcode.egg-info/PKG-INFO +1 -1
  6. {meshcode-2.0.6 → meshcode-2.0.8}/pyproject.toml +1 -1
  7. {meshcode-2.0.6 → meshcode-2.0.8}/README.md +0 -0
  8. {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/cli.py +0 -0
  9. {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/comms_v4.py +0 -0
  10. {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/invites.py +0 -0
  11. {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/launcher.py +0 -0
  12. {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/launcher_install.py +0 -0
  13. {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/meshcode_mcp/__init__.py +0 -0
  14. {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/meshcode_mcp/__main__.py +0 -0
  15. {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/meshcode_mcp/realtime.py +0 -0
  16. {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/meshcode_mcp/test_backend.py +0 -0
  17. {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/meshcode_mcp/test_realtime.py +0 -0
  18. {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/preferences.py +0 -0
  19. {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/protocol_v2.py +0 -0
  20. {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/run_agent.py +0 -0
  21. {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/secrets.py +0 -0
  22. {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/self_update.py +0 -0
  23. {meshcode-2.0.6 → meshcode-2.0.8}/meshcode/setup_clients.py +0 -0
  24. {meshcode-2.0.6 → meshcode-2.0.8}/meshcode.egg-info/SOURCES.txt +0 -0
  25. {meshcode-2.0.6 → meshcode-2.0.8}/meshcode.egg-info/dependency_links.txt +0 -0
  26. {meshcode-2.0.6 → meshcode-2.0.8}/meshcode.egg-info/entry_points.txt +0 -0
  27. {meshcode-2.0.6 → meshcode-2.0.8}/meshcode.egg-info/requires.txt +0 -0
  28. {meshcode-2.0.6 → meshcode-2.0.8}/meshcode.egg-info/top_level.txt +0 -0
  29. {meshcode-2.0.6 → meshcode-2.0.8}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.0.6
3
+ Version: 2.0.8
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.0.6"
2
+ __version__ = "2.0.8"
@@ -319,10 +319,22 @@ def record_event(api_key, project_id, agent_name, session_id, event_type, payloa
319
319
  })
320
320
 
321
321
 
322
- def get_history(project_id: str, limit: int = 20) -> List[Dict]:
322
+ def get_history(project_id: str, limit: int = 20, agent_filter: str = "") -> List[Dict]:
323
+ filters = f"project_id=eq.{project_id}&type=neq.ack"
324
+ if agent_filter:
325
+ filters += f"&or=(from_agent.eq.{quote(agent_filter)},to_agent.eq.{quote(agent_filter)})"
323
326
  return sb_select(
324
327
  "mc_messages",
325
- f"project_id=eq.{project_id}&type=neq.ack",
328
+ filters,
326
329
  order="created_at.desc",
327
330
  limit=limit,
328
331
  )
332
+
333
+
334
+ def get_message_by_id(project_id: str, msg_id: str) -> Optional[Dict]:
335
+ results = sb_select(
336
+ "mc_messages",
337
+ f"project_id=eq.{project_id}&id=eq.{msg_id}",
338
+ limit=1,
339
+ )
340
+ return results[0] if results else None
@@ -342,8 +342,8 @@ _current_state = "online"
342
342
  _last_tool_at = _time.time()
343
343
  _current_tool = ""
344
344
  _IDLE_THRESHOLD_S = 120 # seconds without tool call → IDLE
345
- _PROCESSING_COOLDOWN_S = 15 # seconds after tool returns before flipping to ONLINE
346
- _processing_timer: Optional[_threading.Timer] = None
345
+ _WORKING_COOLDOWN_S = 60 # seconds after last tool returns before flipping to ONLINE
346
+ _working_timer: Optional[_threading.Timer] = None
347
347
 
348
348
 
349
349
  async def _async_flip_status(status: str, task: str = "") -> None:
@@ -372,11 +372,11 @@ def _schedule_flip(status: str, task: str = "") -> None:
372
372
 
373
373
  def _set_state(state: str, tool: str = "") -> None:
374
374
  """Update the state machine and broadcast to dashboard."""
375
- global _current_state, _current_tool, _last_tool_at, _processing_timer
376
- # Cancel any pending processing→online timer
377
- if _processing_timer is not None:
378
- _processing_timer.cancel()
379
- _processing_timer = None
375
+ global _current_state, _current_tool, _last_tool_at, _working_timer
376
+ # Cancel any pending working→online timer
377
+ if _working_timer is not None:
378
+ _working_timer.cancel()
379
+ _working_timer = None
380
380
  _current_state = state
381
381
  _current_tool = tool
382
382
  if state == "working":
@@ -384,11 +384,11 @@ def _set_state(state: str, tool: str = "") -> None:
384
384
  _schedule_flip(state, tool)
385
385
 
386
386
 
387
- def _processing_to_online() -> None:
388
- """Called after PROCESSING cooldown expires — flip to ONLINE."""
389
- global _processing_timer
390
- _processing_timer = None
391
- if _current_state == "processing":
387
+ def _working_to_online() -> None:
388
+ """Called after WORKING cooldown expires — flip to ONLINE."""
389
+ global _working_timer
390
+ _working_timer = None
391
+ if _current_state == "working":
392
392
  _set_state("online", "")
393
393
 
394
394
 
@@ -406,13 +406,12 @@ def with_working_status(func):
406
406
  return await func(*args, **kwargs)
407
407
  finally:
408
408
  if not skip:
409
- global _last_tool_at, _processing_timer
409
+ global _last_tool_at, _working_timer
410
410
  _last_tool_at = _time.time()
411
- # Enter PROCESSING cooldown — stays green for 15s
412
- _set_state("processing", "processing...")
413
- _processing_timer = _threading.Timer(_PROCESSING_COOLDOWN_S, _processing_to_online)
414
- _processing_timer.daemon = True
415
- _processing_timer.start()
411
+ # Stay WORKING with 60s cooldown — covers LLM thinking between tools
412
+ _working_timer = _threading.Timer(_WORKING_COOLDOWN_S, _working_to_online)
413
+ _working_timer.daemon = True
414
+ _working_timer.start()
416
415
  return awrapper
417
416
  else:
418
417
  @_functools.wraps(func)
@@ -700,9 +699,9 @@ def _heartbeat_thread_fn():
700
699
  while not _heartbeat_stop.is_set():
701
700
  try:
702
701
  be.sb_rpc("mc_heartbeat", {"p_project_id": _PROJECT_ID, "p_agent_name": AGENT_NAME, "p_version": "2.0.0"})
703
- # Idle detection: only from online/processing states (NOT waiting)
702
+ # Idle detection: only from online state (NOT waiting or working)
704
703
  # Agents in meshcode_wait should stay WAITING, not flip to IDLE
705
- if _current_state in ("online", "processing") and (_time.time() - _last_tool_at) > _IDLE_THRESHOLD_S:
704
+ if _current_state == "online" and (_time.time() - _last_tool_at) > _IDLE_THRESHOLD_S:
706
705
  _set_state("idle", f"idle ({int((_time.time() - _last_tool_at) / 60)}m)")
707
706
  # Sync current state to DB (in case realtime missed it)
708
707
  try:
@@ -883,6 +882,58 @@ def meshcode_read(include_acks: bool = False) -> Dict[str, Any]:
883
882
  return split
884
883
 
885
884
 
885
+ @mcp.tool()
886
+ @with_working_status
887
+ def meshcode_history(limit: int = 20, agent_filter: Optional[str] = None) -> Dict[str, Any]:
888
+ """View recent message history (both read and unread). Use when you need
889
+ context from past conversations or lost messages after context compression.
890
+
891
+ Args:
892
+ limit: Max messages to return (default 20).
893
+ agent_filter: Optional agent name to filter (shows messages to/from that agent).
894
+ """
895
+ raw = be.get_history(_PROJECT_ID, limit=limit, agent_filter=agent_filter or "")
896
+ messages = [
897
+ {
898
+ "from": m.get("from_agent", ""),
899
+ "to": m.get("to_agent", ""),
900
+ "type": m.get("type", "msg"),
901
+ "ts": m.get("created_at", ""),
902
+ "payload": m.get("payload", {}),
903
+ "id": m.get("id", ""),
904
+ "read": m.get("read", False),
905
+ }
906
+ for m in raw
907
+ ]
908
+ return {"ok": True, "messages": messages, "count": len(messages)}
909
+
910
+
911
+ @mcp.tool()
912
+ @with_working_status
913
+ def meshcode_read_message(msg_id: str) -> Dict[str, Any]:
914
+ """Fetch a specific message by ID. Use when you need to re-read a message
915
+ after context was compressed or session restarted.
916
+
917
+ Args:
918
+ msg_id: The UUID of the message to fetch.
919
+ """
920
+ msg = be.get_message_by_id(_PROJECT_ID, msg_id)
921
+ if not msg:
922
+ return {"error": "message not found", "msg_id": msg_id}
923
+ return {
924
+ "ok": True,
925
+ "message": {
926
+ "from": msg.get("from_agent", ""),
927
+ "to": msg.get("to_agent", ""),
928
+ "type": msg.get("type", "msg"),
929
+ "ts": msg.get("created_at", ""),
930
+ "payload": msg.get("payload", {}),
931
+ "id": msg.get("id", ""),
932
+ "read": msg.get("read", False),
933
+ },
934
+ }
935
+
936
+
886
937
  def _detect_global_done(messages: List[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
887
938
  """Return {reason, from} if the list contains a global_done signal, else None."""
888
939
  for m in messages:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.0.6
3
+ Version: 2.0.8
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.0.6"
7
+ version = "2.0.8"
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