meshcode 2.11.171__tar.gz → 2.11.173__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 (119) hide show
  1. {meshcode-2.11.171 → meshcode-2.11.173}/PKG-INFO +1 -1
  2. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/__init__.py +1 -1
  3. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/claude_update.py +51 -0
  4. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/hostd.py +68 -8
  5. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode.egg-info/PKG-INFO +1 -1
  6. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode.egg-info/SOURCES.txt +1 -0
  7. {meshcode-2.11.171 → meshcode-2.11.173}/pyproject.toml +1 -1
  8. {meshcode-2.11.171 → meshcode-2.11.173}/README.md +0 -0
  9. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/__main__.py +0 -0
  10. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/_launch_smoke.py +0 -0
  11. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/_session_handoff_template.py +0 -0
  12. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/_stop_hook_template.py +0 -0
  13. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/_update_guard.py +0 -0
  14. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/ascii_art.py +0 -0
  15. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/atomic_push.py +0 -0
  16. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/cli.py +0 -0
  17. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/comms_v4.py +0 -0
  18. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/compat.py +0 -0
  19. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/daemon.py +0 -0
  20. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/date_parse.py +0 -0
  21. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/doctor.py +0 -0
  22. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/error_hints.py +0 -0
  23. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/exceptions.py +0 -0
  24. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/hooks/__init__.py +0 -0
  25. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/hooks/push_guard.py +0 -0
  26. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/hooks/repo_path_lock.py +0 -0
  27. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/invites.py +0 -0
  28. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/launcher.py +0 -0
  29. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/launcher_install.py +0 -0
  30. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/meshcode_mcp/__init__.py +0 -0
  31. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/meshcode_mcp/__main__.py +0 -0
  32. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/meshcode_mcp/backend.py +0 -0
  33. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/meshcode_mcp/realtime.py +0 -0
  34. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/meshcode_mcp/server.py +0 -0
  35. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/meshcode_mcp/sleep_signals.py +0 -0
  36. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/meshcode_mcp/test_backend.py +0 -0
  37. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/meshcode_mcp/test_boot_timing.py +0 -0
  38. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/meshcode_mcp/test_install_guard.py +0 -0
  39. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/meshcode_mcp/test_prefs_claude_version.py +0 -0
  40. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/meshcode_mcp/test_realtime.py +0 -0
  41. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  42. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/preferences.py +0 -0
  43. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/protocol_handler.py +0 -0
  44. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/protocol_v2.py +0 -0
  45. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/quickstart.py +0 -0
  46. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/rpc_allowlist.py +0 -0
  47. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/run_agent.py +0 -0
  48. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/scripts/check_secrets.py +0 -0
  49. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/scripts/race_rate_harness.py +0 -0
  50. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/secrets.py +0 -0
  51. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/self_update.py +0 -0
  52. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/setup_clients.py +0 -0
  53. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/supervisor.py +0 -0
  54. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/terminal_mirror_runner.py +0 -0
  55. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/up.py +0 -0
  56. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode/upload.py +0 -0
  57. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode.egg-info/dependency_links.txt +0 -0
  58. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode.egg-info/entry_points.txt +0 -0
  59. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode.egg-info/requires.txt +0 -0
  60. {meshcode-2.11.171 → meshcode-2.11.173}/meshcode.egg-info/top_level.txt +0 -0
  61. {meshcode-2.11.171 → meshcode-2.11.173}/setup.cfg +0 -0
  62. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_auto_update_hardening.py +0 -0
  63. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_autonomous_closegap_1.py +0 -0
  64. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_autonomous_closegap_2.py +0 -0
  65. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_autonomous_closegap_3.py +0 -0
  66. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_autonomous_prompt_inject.py +0 -0
  67. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_boot_bug_regression.py +0 -0
  68. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_color_truecolor.py +0 -0
  69. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_core.py +0 -0
  70. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_cross_agent_messaging.py +0 -0
  71. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_date_parse.py +0 -0
  72. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_doctor.py +0 -0
  73. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_ensure_boot_env_urgent_wake.py +0 -0
  74. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_epistemic_v1_python_sdk.py +0 -0
  75. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_epistemic_v1_stop_conditions.py +0 -0
  76. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_esc_deaf_state.py +0 -0
  77. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_exceptions.py +0 -0
  78. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_file_upload.py +0 -0
  79. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_fleet_reaper.py +0 -0
  80. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_hostd_launch_pinned_env.py +0 -0
  81. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_hostd_serve_discovery_split.py +0 -0
  82. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_hostd_zombie_sessions.py +0 -0
  83. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_init_device_code.py +0 -0
  84. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_install_guard.py +0 -0
  85. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_launch_smoke.py +0 -0
  86. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_lease_sigterm_release.py +0 -0
  87. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_live_mesh_guard.py +0 -0
  88. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_mark_read_batch.py +0 -0
  89. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_marketplace_ratings.py +0 -0
  90. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_migration_integrity.py +0 -0
  91. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_no_appleevents_on_sweep.py +0 -0
  92. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_preflight_hb_gate.py +0 -0
  93. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_pretrust_claude.py +0 -0
  94. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_prompt_dedup_budget.py +0 -0
  95. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_push_guard.py +0 -0
  96. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_realtime_event_freshness.py +0 -0
  97. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_replica_base_workspace_fallback.py +0 -0
  98. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_replica_boot_protocol_unconditional.py +0 -0
  99. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_rls_cross_tenant.py +0 -0
  100. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_rm_guard.py +0 -0
  101. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_rpc_grants.py +0 -0
  102. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_rpc_migrations.py +0 -0
  103. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_run_agent_dry_run.py +0 -0
  104. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_run_agent_no_server_import.py +0 -0
  105. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_security_regressions.py +0 -0
  106. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_self_update_user_site.py +0 -0
  107. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_sentinel.py +0 -0
  108. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_session_replay_gate.py +0 -0
  109. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_setup_path.py +0 -0
  110. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_sleep_signals.py +0 -0
  111. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_status_enum_coverage.py +0 -0
  112. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_stay_on_loop_hook.py +0 -0
  113. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_stop_ghost_terminal.py +0 -0
  114. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_task_progress.py +0 -0
  115. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_terminal_lifecycle.py +0 -0
  116. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_up_launch_cmd.py +0 -0
  117. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_update_guard.py +0 -0
  118. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_urgent_wake_tmux.py +0 -0
  119. {meshcode-2.11.171 → meshcode-2.11.173}/tests/test_wait_open_tasks_contradiction.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.11.171
3
+ Version: 2.11.173
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.11.171"
2
+ __version__ = "2.11.173"
3
3
 
4
4
  # Exception hierarchy — eagerly imported (lightweight, no deps)
5
5
  from meshcode.exceptions import ( # noqa: F401
@@ -45,6 +45,34 @@ INSTALL_TIMEOUT_SEC = 90 # npm i -g can be slow on cold-cache machines
45
45
  STATE_DIR = Path.home() / ".meshcode"
46
46
  LOG_PATH = STATE_DIR / "claude_update.log"
47
47
 
48
+ # --- P0 launch-latency (task 9fb81ff5 / 1d4276c3): never run a doomed global install ---
49
+ SKIP_STATE = STATE_DIR / "claude_update_skip.json"
50
+ NEG_CACHE_SEC = 6 * 3600 # a failed target version is not retried for 6h
51
+
52
+
53
+ def _global_prefix_writable(npm: str) -> bool:
54
+ """True if `npm i -g` can succeed (global node_modules writable).
55
+
56
+ On boxes where /usr/local/lib/node_modules is root-owned, every blocking
57
+ global install hits EACCES — retried on EVERY launch, it can never
58
+ succeed (446 doomed attempts observed in claude_update.log, 2026-07-03).
59
+ """
60
+ try:
61
+ r = subprocess.run([npm, "prefix", "-g"], capture_output=True, text=True, timeout=5)
62
+ root = os.path.join((r.stdout or "").strip(), "lib", "node_modules")
63
+ probe = os.path.join(root, NPM_PKG.split("/")[0]) # @anthropic-ai scope dir
64
+ return os.access(probe if os.path.isdir(probe) else root, os.W_OK)
65
+ except Exception:
66
+ return True # fail-open: unknown -> let npm try once
67
+
68
+
69
+ def _mark_skip(version: str, reason: str) -> None:
70
+ """Record a failed/doomed target version so it is not retried for 6h."""
71
+ try:
72
+ SKIP_STATE.write_text(json.dumps({"version": version, "ts": time.time(), "reason": reason}))
73
+ except Exception:
74
+ pass
75
+
48
76
 
49
77
  # ============================================================
50
78
  # Skip-condition helpers
@@ -214,6 +242,27 @@ def check_and_maybe_update_claude_blocking(
214
242
  )
215
243
  return None
216
244
 
245
+ # negative cache: same target version failed <6h ago -> skip silently
246
+ try:
247
+ st = json.loads(SKIP_STATE.read_text())
248
+ if st.get("version") == latest and time.time() - st.get("ts", 0) < NEG_CACHE_SEC:
249
+ return None
250
+ except Exception:
251
+ pass
252
+
253
+ if not _global_prefix_writable(npm):
254
+ # global install can NEVER succeed (EACCES) -> route latest via npx instead
255
+ # (run_agent npx launch path picks this env up; user npm cache, no root needed)
256
+ os.environ["_MESHCODE_NPX_CLAUDE_VERSION"] = latest
257
+ _mark_skip(latest, "eacces")
258
+ if verbose:
259
+ print(
260
+ f"[meshcode] WARN: npm global prefix not writable; using npx claude@{latest}. "
261
+ f"Permanent fix: sudo chown -R $(whoami) /usr/local/lib/node_modules",
262
+ file=sys.stderr,
263
+ )
264
+ return None
265
+
217
266
  if verbose:
218
267
  print(
219
268
  f"[meshcode] Upgrading claude {current} -> {latest} (blocking, can take ~30s)...",
@@ -234,6 +283,7 @@ def check_and_maybe_update_claude_blocking(
234
283
  if verbose:
235
284
  print(f"[meshcode] claude upgraded to {latest}", file=sys.stderr)
236
285
  return latest
286
+ _mark_skip(latest, f"exit_{proc.returncode}")
237
287
  if verbose:
238
288
  print(
239
289
  f"[meshcode] WARN: npm install exit {proc.returncode}; "
@@ -242,6 +292,7 @@ def check_and_maybe_update_claude_blocking(
242
292
  )
243
293
  return None
244
294
  except subprocess.TimeoutExpired:
295
+ _mark_skip(latest, "timeout")
245
296
  if verbose:
246
297
  print(
247
298
  f"[meshcode] WARN: npm install timed out after {timeout_sec}s; "
@@ -525,8 +525,27 @@ def _api_key() -> Optional[str]:
525
525
  return None
526
526
 
527
527
 
528
+ # P0 launch-latency FIX B (task 9fb81ff5 / 1d4276c3): during a DB degradation
529
+ # (e.g. PostgREST pool exhaustion, 2026-07-03) each sweep runs 5-7 sequential
530
+ # RPCs; at the old hard timeout=15 a fully-degraded sweep took 75-105s and
531
+ # Launch clicks sat unhonored for minutes. 6s is >> p99 healthy RPC latency
532
+ # and cuts the worst-case sweep to ~42s. Override via MESHCODE_HOSTD_RPC_TIMEOUT.
533
+ try:
534
+ _RPC_TIMEOUT = int(os.environ.get("MESHCODE_HOSTD_RPC_TIMEOUT", "6"))
535
+ except ValueError:
536
+ _RPC_TIMEOUT = 6
537
+
538
+ # Consecutive _rpc transport failures (timeout / unreachable / None). Reset on
539
+ # success OR HTTPError (an HTTP error proves the cloud is reachable — that's a
540
+ # server-side refusal, not degradation). At >=3 the sweep loop skips its
541
+ # non-critical phases (stops / force-kills / ghost sweep / reap) so heartbeat +
542
+ # respawns — the paths that honor an explicit human Launch — stay first-class.
543
+ _rpc_fail_streak = 0
544
+
545
+
528
546
  def _rpc(fn: str, payload: dict) -> Optional[dict]:
529
547
  """Call a PostgREST RPC. Returns parsed JSON or None on any failure."""
548
+ global _rpc_fail_streak
530
549
  url, key = _supabase_cfg()
531
550
  if not url or not key:
532
551
  _log("WARN: SUPABASE_URL/KEY not set — cannot reach cloud")
@@ -542,12 +561,15 @@ def _rpc(fn: str, payload: dict) -> Optional[dict]:
542
561
  },
543
562
  method="POST",
544
563
  )
545
- with urllib.request.urlopen(req, timeout=15) as resp:
546
- return json.loads(resp.read().decode("utf-8"))
564
+ with urllib.request.urlopen(req, timeout=_RPC_TIMEOUT) as resp:
565
+ out = json.loads(resp.read().decode("utf-8"))
566
+ _rpc_fail_streak = 0
567
+ return out
547
568
  except urllib.error.HTTPError as e:
548
569
  # task 89d50a14 [E]: log the PostgREST error BODY, not just the status line.
549
570
  # mc_log_respawn_event failed 400 for DAYS and the cause (42804 host_id
550
571
  # uuid-vs-text) was undiagnosable from "HTTP Error 400: Bad Request" alone.
572
+ _rpc_fail_streak = 0 # cloud reachable — server refusal, not degradation
551
573
  try:
552
574
  body = e.read().decode("utf-8", "replace").strip()[:300]
553
575
  except Exception:
@@ -555,6 +577,7 @@ def _rpc(fn: str, payload: dict) -> Optional[dict]:
555
577
  _log(f"WARN: rpc {fn} failed: {e}" + (f" — {body}" if body else ""))
556
578
  return None
557
579
  except Exception as e:
580
+ _rpc_fail_streak += 1
558
581
  _log(f"WARN: rpc {fn} failed: {e}")
559
582
  return None
560
583
 
@@ -3285,6 +3308,33 @@ def cmd_hostd(args: list) -> int:
3285
3308
  sys.excepthook = lambda t, v, tb: _log("UNCAUGHT: " + "".join(_tbmod.format_exception(t, v, tb)))
3286
3309
  except Exception:
3287
3310
  pass
3311
+ # task cccb0b88: DEATH-MARKER — hostd must never exit silently again
3312
+ # (2026-07-03: died 11:16:46 with no FATAL line). atexit covers normal
3313
+ # interpreter exit / sys.exit; handlers cover TERM/INT/HUP and re-raise
3314
+ # with SIG_DFL so launchd still sees the true exit status (KeepAlive
3315
+ # relaunch semantics unchanged). SIGKILL/OOM is unloggable by the dying
3316
+ # process — covered server-side by mc_hostd_absent_sweep (mig 704).
3317
+ try:
3318
+ import atexit as _atexit
3319
+ _death_spawn_mono = time.monotonic()
3320
+ def _death_marker(reason: str) -> None:
3321
+ try:
3322
+ _log(f"DEATH-MARKER: hostd exiting reason={reason} pid={os.getpid()} "
3323
+ f"uptime={int(time.monotonic() - _death_spawn_mono)}s")
3324
+ except Exception:
3325
+ pass
3326
+ _atexit.register(_death_marker, "atexit")
3327
+ def _death_sig(signum, frame):
3328
+ _death_marker(f"signal-{_signal.Signals(signum).name}")
3329
+ _signal.signal(signum, _signal.SIG_DFL)
3330
+ os.kill(os.getpid(), signum)
3331
+ for _s in (_signal.SIGTERM, _signal.SIGINT, _signal.SIGHUP):
3332
+ try:
3333
+ _signal.signal(_s, _death_sig)
3334
+ except Exception:
3335
+ pass
3336
+ except Exception:
3337
+ pass
3288
3338
  # Register this host in mc_host_config so the dashboard can list it as a launch target.
3289
3339
  _reg = _rpc("mc_host_config_set", {"p_api_key": api_key, "p_host_id": host_id})
3290
3340
  if _reg and _reg.get("ok"):
@@ -3346,12 +3396,22 @@ def cmd_hostd(args: list) -> int:
3346
3396
  # always run on a stale-running host without manual intervention.
3347
3397
  _maybe_self_restart_on_version_drift()
3348
3398
  relaunched = _do_respawns(api_key, host_id)
3349
- stopped = _do_stops(api_key, host_id)
3350
- force_killed = _do_force_kills(api_key, host_id) # 38523a98 Gap1: visible explicit human stop
3351
- # 91201315: stopped agents whose instance/heartbeat already cleared
3352
- # invisible to both RPC sweeps above (their <90s heartbeat gate).
3353
- ghost_killed = _do_stopped_ghost_sweep(api_key, host_id)
3354
- reaped = _do_reap(api_key, host_id) # 38523a98: kill ghosts/dup-PIDs/crashed-orphans
3399
+ # P0 FIX B (task 9fb81ff5 / 1d4276c3): degraded-sweep short-circuit.
3400
+ # Heartbeat + respawns above ALWAYS run they honor an explicit
3401
+ # human Launch. When the cloud is degraded (>=3 consecutive _rpc
3402
+ # transport failures), skip the non-critical phases this sweep so
3403
+ # a fully-degraded pass stays ~2 RPCs instead of 5-7 × timeout.
3404
+ # Stops/kills/reaps resume on the next healthy sweep.
3405
+ if _rpc_fail_streak >= 3:
3406
+ _log(f"sweep degraded (rpc_fail_streak={_rpc_fail_streak}) — skipping non-critical phases (stops/force-kills/ghost-sweep/reap)")
3407
+ stopped = force_killed = ghost_killed = reaped = 0
3408
+ else:
3409
+ stopped = _do_stops(api_key, host_id)
3410
+ force_killed = _do_force_kills(api_key, host_id) # 38523a98 Gap1: visible explicit human stop
3411
+ # 91201315: stopped agents whose instance/heartbeat already cleared —
3412
+ # invisible to both RPC sweeps above (their <90s heartbeat gate).
3413
+ ghost_killed = _do_stopped_ghost_sweep(api_key, host_id)
3414
+ reaped = _do_reap(api_key, host_id) # 38523a98: kill ghosts/dup-PIDs/crashed-orphans
3355
3415
  _gc_headless_pids() # cb90b058: drop dead PIDs (stale entry can't mask a live agent)
3356
3416
  _gc_stop_markers() # a4001d59: stale stop-markers can't flip a later crash into a silent close
3357
3417
  _up = int(time.monotonic() - _spawn_mono)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.11.171
3
+ Version: 2.11.173
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,6 @@
1
1
  README.md
2
2
  pyproject.toml
3
+ setup.cfg
3
4
  meshcode/__init__.py
4
5
  meshcode/__main__.py
5
6
  meshcode/_launch_smoke.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "meshcode"
7
- version = "2.11.171"
7
+ version = "2.11.173"
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