meshcode 2.10.58__tar.gz → 2.10.60__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 (46) hide show
  1. {meshcode-2.10.58 → meshcode-2.10.60}/PKG-INFO +1 -1
  2. {meshcode-2.10.58 → meshcode-2.10.60}/meshcode/__init__.py +1 -1
  3. {meshcode-2.10.58 → meshcode-2.10.60}/meshcode/comms_v4.py +26 -0
  4. {meshcode-2.10.58 → meshcode-2.10.60}/meshcode/meshcode_mcp/server.py +61 -0
  5. meshcode-2.10.60/meshcode/supervisor.py +186 -0
  6. {meshcode-2.10.58 → meshcode-2.10.60}/meshcode.egg-info/PKG-INFO +1 -1
  7. {meshcode-2.10.58 → meshcode-2.10.60}/meshcode.egg-info/SOURCES.txt +1 -0
  8. {meshcode-2.10.58 → meshcode-2.10.60}/pyproject.toml +1 -1
  9. {meshcode-2.10.58 → meshcode-2.10.60}/README.md +0 -0
  10. {meshcode-2.10.58 → meshcode-2.10.60}/meshcode/ascii_art.py +0 -0
  11. {meshcode-2.10.58 → meshcode-2.10.60}/meshcode/cli.py +0 -0
  12. {meshcode-2.10.58 → meshcode-2.10.60}/meshcode/compat.py +0 -0
  13. {meshcode-2.10.58 → meshcode-2.10.60}/meshcode/exceptions.py +0 -0
  14. {meshcode-2.10.58 → meshcode-2.10.60}/meshcode/invites.py +0 -0
  15. {meshcode-2.10.58 → meshcode-2.10.60}/meshcode/launcher.py +0 -0
  16. {meshcode-2.10.58 → meshcode-2.10.60}/meshcode/launcher_install.py +0 -0
  17. {meshcode-2.10.58 → meshcode-2.10.60}/meshcode/meshcode_mcp/__init__.py +0 -0
  18. {meshcode-2.10.58 → meshcode-2.10.60}/meshcode/meshcode_mcp/__main__.py +0 -0
  19. {meshcode-2.10.58 → meshcode-2.10.60}/meshcode/meshcode_mcp/backend.py +0 -0
  20. {meshcode-2.10.58 → meshcode-2.10.60}/meshcode/meshcode_mcp/realtime.py +0 -0
  21. {meshcode-2.10.58 → meshcode-2.10.60}/meshcode/meshcode_mcp/test_backend.py +0 -0
  22. {meshcode-2.10.58 → meshcode-2.10.60}/meshcode/meshcode_mcp/test_realtime.py +0 -0
  23. {meshcode-2.10.58 → meshcode-2.10.60}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  24. {meshcode-2.10.58 → meshcode-2.10.60}/meshcode/preferences.py +0 -0
  25. {meshcode-2.10.58 → meshcode-2.10.60}/meshcode/protocol_v2.py +0 -0
  26. {meshcode-2.10.58 → meshcode-2.10.60}/meshcode/run_agent.py +0 -0
  27. {meshcode-2.10.58 → meshcode-2.10.60}/meshcode/secrets.py +0 -0
  28. {meshcode-2.10.58 → meshcode-2.10.60}/meshcode/self_update.py +0 -0
  29. {meshcode-2.10.58 → meshcode-2.10.60}/meshcode/setup_clients.py +0 -0
  30. {meshcode-2.10.58 → meshcode-2.10.60}/meshcode.egg-info/dependency_links.txt +0 -0
  31. {meshcode-2.10.58 → meshcode-2.10.60}/meshcode.egg-info/entry_points.txt +0 -0
  32. {meshcode-2.10.58 → meshcode-2.10.60}/meshcode.egg-info/requires.txt +0 -0
  33. {meshcode-2.10.58 → meshcode-2.10.60}/meshcode.egg-info/top_level.txt +0 -0
  34. {meshcode-2.10.58 → meshcode-2.10.60}/setup.cfg +0 -0
  35. {meshcode-2.10.58 → meshcode-2.10.60}/tests/test_core.py +0 -0
  36. {meshcode-2.10.58 → meshcode-2.10.60}/tests/test_cross_agent_messaging.py +0 -0
  37. {meshcode-2.10.58 → meshcode-2.10.60}/tests/test_esc_deaf_state.py +0 -0
  38. {meshcode-2.10.58 → meshcode-2.10.60}/tests/test_exceptions.py +0 -0
  39. {meshcode-2.10.58 → meshcode-2.10.60}/tests/test_mark_read_batch.py +0 -0
  40. {meshcode-2.10.58 → meshcode-2.10.60}/tests/test_migration_integrity.py +0 -0
  41. {meshcode-2.10.58 → meshcode-2.10.60}/tests/test_realtime_event_freshness.py +0 -0
  42. {meshcode-2.10.58 → meshcode-2.10.60}/tests/test_rls_cross_tenant.py +0 -0
  43. {meshcode-2.10.58 → meshcode-2.10.60}/tests/test_rpc_migrations.py +0 -0
  44. {meshcode-2.10.58 → meshcode-2.10.60}/tests/test_security_regressions.py +0 -0
  45. {meshcode-2.10.58 → meshcode-2.10.60}/tests/test_sentinel.py +0 -0
  46. {meshcode-2.10.58 → meshcode-2.10.60}/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.10.58
3
+ Version: 2.10.60
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,5 +1,5 @@
1
1
  """MeshCode — Real-time communication between AI agents."""
2
- __version__ = "2.10.58"
2
+ __version__ = "2.10.60"
3
3
 
4
4
  # Exception hierarchy — eagerly imported (lightweight, no deps)
5
5
  from meshcode.exceptions import ( # noqa: F401
@@ -3327,6 +3327,31 @@ if __name__ == "__main__":
3327
3327
  elif cmd in ("help", "--help", "-h"):
3328
3328
  show_help()
3329
3329
 
3330
+ elif cmd == "supervisor":
3331
+ # meshcode supervisor start|stop|status [project] [agent]
3332
+ import importlib
3333
+ _sup = importlib.import_module("meshcode.supervisor")
3334
+ sub = pos[0] if pos else "status"
3335
+ if sub == "start":
3336
+ if len(pos) < 3:
3337
+ print("Usage: meshcode supervisor start <project> <agent>")
3338
+ sys.exit(1)
3339
+ _sup.start(pos[1], pos[2])
3340
+ elif sub == "stop":
3341
+ if len(pos) < 3:
3342
+ print("Usage: meshcode supervisor stop <project> <agent>")
3343
+ sys.exit(1)
3344
+ _sup.stop(pos[1], pos[2])
3345
+ elif sub == "status":
3346
+ _sup.status()
3347
+ elif sub == "--simple":
3348
+ if len(pos) < 3:
3349
+ print("Usage: meshcode supervisor --simple <project> <agent>")
3350
+ sys.exit(1)
3351
+ _sup.simple(pos[1], pos[2])
3352
+ else:
3353
+ print(f"Usage: meshcode supervisor [start|stop|status|--simple] <project> <agent>")
3354
+
3330
3355
  else:
3331
3356
  known_cmds = [
3332
3357
  "register", "send", "broadcast", "read", "check", "watch", "inbox", "next",
@@ -3335,6 +3360,7 @@ if __name__ == "__main__":
3335
3360
  "setup", "run", "go", "invite", "join", "invites", "members",
3336
3361
  "revoke-invite", "revoke-member", "login", "prefs", "launcher",
3337
3362
  "help", "init", "doctor", "compat", "upgrade", "profile", "validate-sessions", "wake-headless",
3363
+ "supervisor",
3338
3364
  ]
3339
3365
  # Simple fuzzy: prefix match + Levenshtein-like best match
3340
3366
  suggestions = [c for c in known_cmds if c.startswith(cmd)]
@@ -1949,6 +1949,47 @@ def _get_pending_tasks_summary() -> Optional[List[Dict[str, str]]]:
1949
1949
  return None
1950
1950
 
1951
1951
 
1952
+ def _try_auto_claim_task() -> Optional[Dict[str, str]]:
1953
+ """Auto-claim the oldest open task matching this agent's role.
1954
+
1955
+ Called during meshcode_wait idle loops. Only claims wildcard ('*')
1956
+ tasks to avoid stealing directly-assigned work. Returns the claimed
1957
+ task summary or None.
1958
+ """
1959
+ try:
1960
+ api_key = _get_api_key()
1961
+ if not api_key:
1962
+ return None
1963
+ result = be.task_list(api_key, _PROJECT_ID, AGENT_NAME, status_filter="open")
1964
+ if not isinstance(result, dict) or not result.get("ok"):
1965
+ return None
1966
+ tasks = result.get("tasks", [])
1967
+ # Only auto-claim wildcard tasks that nobody claimed yet
1968
+ claimable = [
1969
+ t for t in tasks
1970
+ if t.get("assignee") == "*"
1971
+ and not t.get("claimed_by")
1972
+ and t.get("status") == "open"
1973
+ ]
1974
+ if not claimable:
1975
+ return None
1976
+ # Sort by priority (urgent first), then oldest
1977
+ _PRIO = {"urgent": 0, "high": 1, "normal": 2, "low": 3}
1978
+ claimable.sort(key=lambda t: (_PRIO.get(t.get("priority", "normal"), 2), t.get("created_at", "")))
1979
+ target = claimable[0]
1980
+ # Claim it
1981
+ claim_result = be.sb_rpc("mc_task_claim", {
1982
+ "p_api_key": api_key,
1983
+ "p_task_id": target["id"],
1984
+ })
1985
+ if isinstance(claim_result, dict) and claim_result.get("ok"):
1986
+ log.info(f"[meshcode] auto-claimed task: {target['title'][:60]}")
1987
+ return {"id": target["id"][:8], "title": target["title"][:80], "priority": target.get("priority", "normal")}
1988
+ except Exception as e:
1989
+ log.debug(f"[meshcode] auto-claim failed: {e}")
1990
+ return None
1991
+
1992
+
1952
1993
  @mcp.tool()
1953
1994
  @with_working_status
1954
1995
  async def meshcode_wait(timeout_seconds: int = 20, include_acks: bool = False) -> Dict[str, Any]:
@@ -2042,6 +2083,16 @@ async def meshcode_wait(timeout_seconds: int = 20, include_acks: bool = False) -
2042
2083
  result["pending_tasks"] = pending_tasks
2043
2084
  break # Return so agent can work tasks
2044
2085
 
2086
+ # Auto-claim: when idle >30s with no assigned tasks, try to
2087
+ # claim the oldest open wildcard task. This makes agents
2088
+ # self-directing instead of waiting passively for assignment.
2089
+ if _CONSECUTIVE_IDLE_SECONDS >= 30 and not _is_leader_agent():
2090
+ claimed = _try_auto_claim_task()
2091
+ if claimed:
2092
+ result["auto_claimed"] = claimed
2093
+ result["pending_tasks"] = [claimed]
2094
+ break # Return so agent works the claimed task
2095
+
2045
2096
  # Update status to sleeping after threshold, but keep looping
2046
2097
  if _AUTO_SLEEP_THRESHOLD > 0 and _CONSECUTIVE_IDLE_SECONDS >= _AUTO_SLEEP_THRESHOLD and not _STAY_AWAKE:
2047
2098
  try:
@@ -2132,6 +2183,16 @@ def _mark_realtime_msgs_read_in_db(messages: List[Dict[str, Any]]) -> None:
2132
2183
  except Exception as e:
2133
2184
  log.debug(f"mark_read failed for msg {mid}: {e}")
2134
2185
 
2186
+ # Confirm delivery (best-effort, background) — completes the
2187
+ # delivery pipeline: sent → read → confirmed.
2188
+ try:
2189
+ be.sb_rpc("mc_confirm_delivery", {
2190
+ "p_api_key": api_key,
2191
+ "p_message_ids": msg_ids,
2192
+ })
2193
+ except Exception:
2194
+ pass # Non-critical: messages still work without confirmation
2195
+
2135
2196
 
2136
2197
  async def _meshcode_wait_inner(actual_timeout: int, include_acks: bool) -> Dict[str, Any]:
2137
2198
 
@@ -0,0 +1,186 @@
1
+ """MeshCode Process Supervisor — auto-restart agents on crash.
2
+
3
+ Generates macOS launchd plists (or simple bash wrappers) that keep
4
+ agents running 24/7. When Claude crashes or MCP dies, the supervisor
5
+ restarts in 2-5 seconds automatically.
6
+
7
+ Usage:
8
+ meshcode supervisor start <project> <agent> — install + start launchd plist
9
+ meshcode supervisor stop <project> <agent> — stop + uninstall
10
+ meshcode supervisor status — list supervised agents
11
+ meshcode supervisor --simple <project> <agent> — bash while-loop fallback
12
+ """
13
+ import os
14
+ import sys
15
+ import subprocess
16
+ import json
17
+ import platform
18
+ from pathlib import Path
19
+
20
+
21
+ _PLIST_DIR = Path.home() / "Library" / "LaunchAgents"
22
+ _PLIST_PREFIX = "io.meshcode.agent"
23
+
24
+
25
+ def _plist_path(project: str, agent: str) -> Path:
26
+ safe_name = f"{project}-{agent}".replace("/", "-").replace(" ", "-")
27
+ return _PLIST_DIR / f"{_PLIST_PREFIX}.{safe_name}.plist"
28
+
29
+
30
+ def _plist_label(project: str, agent: str) -> str:
31
+ safe_name = f"{project}-{agent}".replace("/", "-").replace(" ", "-")
32
+ return f"{_PLIST_PREFIX}.{safe_name}"
33
+
34
+
35
+ def _find_meshcode_bin() -> str:
36
+ """Find the meshcode executable path."""
37
+ import shutil
38
+ path = shutil.which("meshcode")
39
+ if path:
40
+ return path
41
+ # Fallback: python -m meshcode
42
+ return f"{sys.executable} -m meshcode"
43
+
44
+
45
+ def _generate_plist(project: str, agent: str) -> str:
46
+ """Generate a launchd plist XML for auto-restarting an agent."""
47
+ label = _plist_label(project, agent)
48
+ meshcode_bin = _find_meshcode_bin()
49
+ log_dir = Path.home() / ".meshcode" / "logs"
50
+ log_dir.mkdir(parents=True, exist_ok=True)
51
+ stdout_log = log_dir / f"{project}-{agent}.stdout.log"
52
+ stderr_log = log_dir / f"{project}-{agent}.stderr.log"
53
+
54
+ # Build the command
55
+ if " -m " in meshcode_bin:
56
+ parts = meshcode_bin.split()
57
+ program_args = parts + ["run", project, agent]
58
+ else:
59
+ program_args = [meshcode_bin, "run", project, agent]
60
+
61
+ args_xml = "\n".join(f" <string>{a}</string>" for a in program_args)
62
+
63
+ return f"""<?xml version="1.0" encoding="UTF-8"?>
64
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
65
+ <plist version="1.0">
66
+ <dict>
67
+ <key>Label</key>
68
+ <string>{label}</string>
69
+ <key>ProgramArguments</key>
70
+ <array>
71
+ {args_xml}
72
+ </array>
73
+ <key>KeepAlive</key>
74
+ <true/>
75
+ <key>ThrottleInterval</key>
76
+ <integer>5</integer>
77
+ <key>StandardOutPath</key>
78
+ <string>{stdout_log}</string>
79
+ <key>StandardErrorPath</key>
80
+ <string>{stderr_log}</string>
81
+ <key>EnvironmentVariables</key>
82
+ <dict>
83
+ <key>PATH</key>
84
+ <string>/usr/local/bin:/usr/bin:/bin:{os.path.dirname(sys.executable)}</string>
85
+ </dict>
86
+ </dict>
87
+ </plist>
88
+ """
89
+
90
+
91
+ def start(project: str, agent: str) -> None:
92
+ """Install and start a launchd supervisor for an agent."""
93
+ if platform.system() != "Darwin":
94
+ print("[meshcode supervisor] launchd is macOS only. Use --simple for other platforms.")
95
+ return
96
+
97
+ plist = _plist_path(project, agent)
98
+ _PLIST_DIR.mkdir(parents=True, exist_ok=True)
99
+
100
+ # Write plist
101
+ plist.write_text(_generate_plist(project, agent))
102
+ print(f"[meshcode supervisor] Plist written: {plist}")
103
+
104
+ # Load it
105
+ result = subprocess.run(
106
+ ["launchctl", "load", str(plist)],
107
+ capture_output=True, text=True
108
+ )
109
+ if result.returncode == 0:
110
+ print(f"[meshcode supervisor] Agent {agent}@{project} supervised (KeepAlive=true)")
111
+ print(f"[meshcode supervisor] Logs: ~/.meshcode/logs/{project}-{agent}.stderr.log")
112
+ else:
113
+ print(f"[meshcode supervisor] ERROR: launchctl load failed: {result.stderr.strip()}")
114
+
115
+
116
+ def stop(project: str, agent: str) -> None:
117
+ """Stop and uninstall a supervised agent."""
118
+ plist = _plist_path(project, agent)
119
+ if not plist.exists():
120
+ print(f"[meshcode supervisor] No plist found for {agent}@{project}")
121
+ return
122
+
123
+ result = subprocess.run(
124
+ ["launchctl", "unload", str(plist)],
125
+ capture_output=True, text=True
126
+ )
127
+ plist.unlink(missing_ok=True)
128
+ print(f"[meshcode supervisor] Agent {agent}@{project} unsupervised")
129
+
130
+
131
+ def status() -> None:
132
+ """List all supervised agents."""
133
+ if not _PLIST_DIR.exists():
134
+ print("[meshcode supervisor] No supervised agents")
135
+ return
136
+
137
+ plists = list(_PLIST_DIR.glob(f"{_PLIST_PREFIX}.*.plist"))
138
+ if not plists:
139
+ print("[meshcode supervisor] No supervised agents")
140
+ return
141
+
142
+ print(f"[meshcode supervisor] {len(plists)} supervised agent(s):")
143
+ for p in sorted(plists):
144
+ label = p.stem.replace(f"{_PLIST_PREFIX}.", "")
145
+ # Check if running
146
+ result = subprocess.run(
147
+ ["launchctl", "list", f"{_PLIST_PREFIX}.{label}"],
148
+ capture_output=True, text=True
149
+ )
150
+ running = result.returncode == 0
151
+ state = "RUNNING" if running else "STOPPED"
152
+ print(f" {label}: {state}")
153
+
154
+
155
+ def simple(project: str, agent: str) -> None:
156
+ """Simple bash while-loop supervisor (cross-platform fallback)."""
157
+ meshcode_bin = _find_meshcode_bin()
158
+ print(f"[meshcode supervisor] Starting simple supervisor for {agent}@{project}")
159
+ print("[meshcode supervisor] Press Ctrl+C to stop")
160
+
161
+ restart_count = 0
162
+ while True:
163
+ restart_count += 1
164
+ print(f"\n[meshcode supervisor] Starting agent (attempt #{restart_count})...")
165
+ try:
166
+ if " -m " in meshcode_bin:
167
+ parts = meshcode_bin.split()
168
+ cmd = parts + ["run", project, agent]
169
+ else:
170
+ cmd = [meshcode_bin, "run", project, agent]
171
+ result = subprocess.run(cmd)
172
+ print(f"[meshcode supervisor] Agent exited with code {result.returncode}")
173
+ except KeyboardInterrupt:
174
+ print("\n[meshcode supervisor] Stopped by user")
175
+ break
176
+ except Exception as e:
177
+ print(f"[meshcode supervisor] Error: {e}")
178
+
179
+ import time
180
+ backoff = min(2 + restart_count, 10)
181
+ print(f"[meshcode supervisor] Restarting in {backoff}s...")
182
+ try:
183
+ time.sleep(backoff)
184
+ except KeyboardInterrupt:
185
+ print("\n[meshcode supervisor] Stopped by user")
186
+ break
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.10.58
3
+ Version: 2.10.60
4
4
  Summary: Real-time communication between AI agents — Supabase-backed CLI
5
5
  Author-email: MeshCode <hello@meshcode.io>
6
6
  License: MIT
@@ -15,6 +15,7 @@ meshcode/run_agent.py
15
15
  meshcode/secrets.py
16
16
  meshcode/self_update.py
17
17
  meshcode/setup_clients.py
18
+ meshcode/supervisor.py
18
19
  meshcode.egg-info/PKG-INFO
19
20
  meshcode.egg-info/SOURCES.txt
20
21
  meshcode.egg-info/dependency_links.txt
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "meshcode"
7
- version = "2.10.58"
7
+ version = "2.10.60"
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