meshcode 2.11.121__tar.gz → 2.11.122__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 (94) hide show
  1. {meshcode-2.11.121 → meshcode-2.11.122}/PKG-INFO +1 -1
  2. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/__init__.py +1 -1
  3. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/hostd.py +55 -0
  4. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/meshcode_mcp/server.py +28 -19
  5. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode.egg-info/PKG-INFO +1 -1
  6. {meshcode-2.11.121 → meshcode-2.11.122}/pyproject.toml +1 -1
  7. {meshcode-2.11.121 → meshcode-2.11.122}/README.md +0 -0
  8. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/__main__.py +0 -0
  9. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/_session_handoff_template.py +0 -0
  10. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/_stop_hook_template.py +0 -0
  11. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/ascii_art.py +0 -0
  12. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/atomic_push.py +0 -0
  13. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/claude_update.py +0 -0
  14. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/cli.py +0 -0
  15. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/comms_v4.py +0 -0
  16. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/compat.py +0 -0
  17. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/daemon.py +0 -0
  18. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/date_parse.py +0 -0
  19. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/doctor.py +0 -0
  20. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/error_hints.py +0 -0
  21. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/exceptions.py +0 -0
  22. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/hooks/__init__.py +0 -0
  23. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/hooks/repo_path_lock.py +0 -0
  24. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/invites.py +0 -0
  25. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/launcher.py +0 -0
  26. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/launcher_install.py +0 -0
  27. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/meshcode_mcp/__init__.py +0 -0
  28. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/meshcode_mcp/__main__.py +0 -0
  29. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/meshcode_mcp/backend.py +0 -0
  30. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/meshcode_mcp/realtime.py +0 -0
  31. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/meshcode_mcp/sleep_signals.py +0 -0
  32. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/meshcode_mcp/test_backend.py +0 -0
  33. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/meshcode_mcp/test_boot_timing.py +0 -0
  34. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/meshcode_mcp/test_install_guard.py +0 -0
  35. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/meshcode_mcp/test_prefs_claude_version.py +0 -0
  36. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/meshcode_mcp/test_realtime.py +0 -0
  37. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  38. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/preferences.py +0 -0
  39. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/protocol_handler.py +0 -0
  40. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/protocol_v2.py +0 -0
  41. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/quickstart.py +0 -0
  42. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/rpc_allowlist.py +0 -0
  43. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/run_agent.py +0 -0
  44. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/scripts/check_secrets.py +0 -0
  45. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/scripts/race_rate_harness.py +0 -0
  46. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/secrets.py +0 -0
  47. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/self_update.py +0 -0
  48. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/setup_clients.py +0 -0
  49. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/supervisor.py +0 -0
  50. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/up.py +0 -0
  51. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode/upload.py +0 -0
  52. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode.egg-info/SOURCES.txt +0 -0
  53. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode.egg-info/dependency_links.txt +0 -0
  54. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode.egg-info/entry_points.txt +0 -0
  55. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode.egg-info/requires.txt +0 -0
  56. {meshcode-2.11.121 → meshcode-2.11.122}/meshcode.egg-info/top_level.txt +0 -0
  57. {meshcode-2.11.121 → meshcode-2.11.122}/setup.cfg +0 -0
  58. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_auto_update_hardening.py +0 -0
  59. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_autonomous_closegap_1.py +0 -0
  60. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_autonomous_closegap_2.py +0 -0
  61. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_autonomous_closegap_3.py +0 -0
  62. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_autonomous_prompt_inject.py +0 -0
  63. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_boot_bug_regression.py +0 -0
  64. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_color_truecolor.py +0 -0
  65. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_core.py +0 -0
  66. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_cross_agent_messaging.py +0 -0
  67. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_date_parse.py +0 -0
  68. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_doctor.py +0 -0
  69. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_epistemic_v1_python_sdk.py +0 -0
  70. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_epistemic_v1_stop_conditions.py +0 -0
  71. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_esc_deaf_state.py +0 -0
  72. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_exceptions.py +0 -0
  73. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_file_upload.py +0 -0
  74. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_init_device_code.py +0 -0
  75. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_install_guard.py +0 -0
  76. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_lease_sigterm_release.py +0 -0
  77. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_mark_read_batch.py +0 -0
  78. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_marketplace_ratings.py +0 -0
  79. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_migration_integrity.py +0 -0
  80. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_realtime_event_freshness.py +0 -0
  81. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_rls_cross_tenant.py +0 -0
  82. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_rpc_grants.py +0 -0
  83. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_rpc_migrations.py +0 -0
  84. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_run_agent_dry_run.py +0 -0
  85. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_run_agent_no_server_import.py +0 -0
  86. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_security_regressions.py +0 -0
  87. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_self_update_user_site.py +0 -0
  88. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_sentinel.py +0 -0
  89. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_setup_path.py +0 -0
  90. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_sleep_signals.py +0 -0
  91. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_status_enum_coverage.py +0 -0
  92. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_stay_on_loop_hook.py +0 -0
  93. {meshcode-2.11.121 → meshcode-2.11.122}/tests/test_swarm_events.py +0 -0
  94. {meshcode-2.11.121 → meshcode-2.11.122}/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.121
3
+ Version: 2.11.122
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.121"
2
+ __version__ = "2.11.122"
3
3
 
4
4
  # Exception hierarchy — eagerly imported (lightweight, no deps)
5
5
  from meshcode.exceptions import ( # noqa: F401
@@ -1762,6 +1762,52 @@ def _do_version_recycles(api_key: str, host_id: str) -> int:
1762
1762
  return n
1763
1763
 
1764
1764
 
1765
+ # c301be69: hold the singleton lock handle for the daemon's lifetime — module
1766
+ # global so it's never GC'd (GC would close the fd and release the lock).
1767
+ _HOSTD_LOCK_FH = None
1768
+
1769
+
1770
+ def _acquire_hostd_singleton():
1771
+ """One hostd per machine (task c301be69 — duplicate-daemon race observed
1772
+ live 2026-06-09: a manual `nohup meshcode hostd run` raced auto-bootstrap,
1773
+ TWO daemons ran ~30s = double respawn/recycle sweeps = storm class).
1774
+
1775
+ Exclusive flock on ~/.meshcode/hostd.lock, held for the process lifetime.
1776
+ The OS releases it on ANY death (crash, kill -9), so there is no
1777
+ stale-pidfile problem; the pid written inside is informational only.
1778
+ Python opens fds CLOEXEC (PEP 446), so the self-reexec-on-version-drift
1779
+ path releases the lock at exec time and the fresh image re-acquires it.
1780
+
1781
+ Returns (fh, status): status in 'acquired' | 'held' | 'error'.
1782
+ 'error' means the lock could not be evaluated — callers FAIL OPEN
1783
+ (one possibly-duplicate daemon beats no daemon at all)."""
1784
+ try:
1785
+ STATE_DIR.mkdir(parents=True, exist_ok=True)
1786
+ fh = open(STATE_DIR / "hostd.lock", "a+")
1787
+ try:
1788
+ if sys.platform == "win32":
1789
+ import msvcrt
1790
+ fh.seek(0)
1791
+ msvcrt.locking(fh.fileno(), msvcrt.LK_NBLCK, 1)
1792
+ else:
1793
+ import fcntl
1794
+ fcntl.flock(fh.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
1795
+ except OSError:
1796
+ fh.close()
1797
+ return (None, "held")
1798
+ try:
1799
+ fh.seek(0)
1800
+ fh.truncate()
1801
+ fh.write(str(os.getpid()))
1802
+ fh.flush()
1803
+ except Exception:
1804
+ pass
1805
+ return (fh, "acquired")
1806
+ except Exception as e:
1807
+ _log(f"WARN singleton lock unavailable ({e}) — proceeding unlocked (fail-open)")
1808
+ return (None, "error")
1809
+
1810
+
1765
1811
  def cmd_hostd(args: list) -> int:
1766
1812
  """Entry point for `meshcode hostd ...`."""
1767
1813
  if not args or args[0] in ("-h", "--help"):
@@ -1790,6 +1836,15 @@ def cmd_hostd(args: list) -> int:
1790
1836
  if not api_key:
1791
1837
  _log("FATAL: no api key — run `meshcode login` (key is read from the keychain)")
1792
1838
  return 1
1839
+ # SINGLETON (task c301be69): second instance exits 0 with one log line.
1840
+ # Covers the manual-run vs auto-bootstrap race; _hostd_bootstrap_if_needed
1841
+ # honors it implicitly — its spawned `hostd run` hits this gate and exits.
1842
+ global _HOSTD_LOCK_FH
1843
+ _lk, _lk_status = _acquire_hostd_singleton()
1844
+ if _lk_status == "held":
1845
+ _log("hostd already running on this machine (hostd.lock held) — exiting (singleton c301be69)")
1846
+ return 0
1847
+ _HOSTD_LOCK_FH = _lk # may be None on 'error' (fail-open)
1793
1848
  # P1 hostd_CRASH_LOOP: the detached Windows daemon has NO console, so crashes (incl hard
1794
1849
  # faults / uncaught exceptions) went to INVISIBLE stderr — hostd.log showed nothing. Capture
1795
1850
  # stderr + faulthandler tracebacks to a file + install an excepthook so the NEXT crash is
@@ -4109,11 +4109,14 @@ def _try_auto_complete_on_ship(payload: Dict[str, Any]) -> Optional[Dict[str, An
4109
4109
  return None
4110
4110
 
4111
4111
 
4112
- def _try_auto_complete_on_done_status(task_hint: str) -> Optional[Dict[str, Any]]:
4113
- """CLOSEGAP-3 sibling: when set_status='done', complete the single
4114
- in_progress task if there's exactly one. No-op for 0 or >1 active.
4115
- Summary built from the task_hint + a fallback note.
4116
- """
4112
+ def _try_auto_park_on_done_status(task_hint: str, status: str = "done") -> Optional[Dict[str, Any]]:
4113
+ """CLOSEGAP-3 v2 (task 53767993): when set_status='done'/'sleeping' with
4114
+ exactly ONE in_progress claim, PARK it roll back to 'claimed' with a
4115
+ checkpoint note (mc_task_checkpoint_park, mig 476) NEVER complete it.
4116
+ Live incident 2026-06-09T23:30Z: the old auto-COMPLETE closed 250b7c1e as
4117
+ done with zero work at an ordered exit-for-respawn (board lied to Samuel;
4118
+ re-opened as 7a467894). status='done' may now ONLY come from an explicit
4119
+ meshcode_task_complete. No-op for 0 or >1 active claims."""
4117
4120
  try:
4118
4121
  api_key = _get_api_key()
4119
4122
  if not api_key:
@@ -4128,24 +4131,30 @@ def _try_auto_complete_on_done_status(task_hint: str) -> Optional[Dict[str, Any]
4128
4131
  if len(active) != 1:
4129
4132
  return None
4130
4133
  target = active[0]
4131
- summary = (
4132
- f"AUTO-CHECKPOINT (CLOSEGAP-3): agent flipped set_status='done'. "
4133
- f"Task hint: {(task_hint or '(none)')[:200]}. "
4134
- f"No ship-report content capturedset_status payload-only."
4134
+ note = (
4135
+ f"CHECKPOINT (CLOSEGAP-3 park): agent flipped set_status='{status}' "
4136
+ f"mid-claim (rotation/sleep). Task hint: {(task_hint or '(none)')[:200]}. "
4137
+ f"Work NOT verified completeresuming agent must task_complete explicitly."
4135
4138
  )
4136
- result = be.task_complete(api_key, _PROJECT_ID, target["id"], AGENT_NAME, summary=summary)
4139
+ result = be.sb_rpc("mc_task_checkpoint_park", {
4140
+ "p_api_key": api_key,
4141
+ "p_project_id": _PROJECT_ID,
4142
+ "p_task_id": target["id"],
4143
+ "p_agent": AGENT_NAME,
4144
+ "p_note": note,
4145
+ })
4137
4146
  if not (isinstance(result, dict) and result.get("ok")):
4138
4147
  return None
4139
4148
  try:
4140
4149
  _log_activity_bg(
4141
4150
  "auto_checkpoint",
4142
- f"{AGENT_NAME} auto-completed task {target['id'][:8]} on set_status=done",
4151
+ f"{AGENT_NAME} PARKED task {target['id'][:8]} on set_status={status} (claimed again, NOT done)",
4143
4152
  )
4144
4153
  except Exception:
4145
4154
  pass
4146
- return {"task_id": target["id"][:8], "title": str(target.get("title") or "")[:80]}
4155
+ return {"task_id": target["id"][:8], "title": str(target.get("title") or "")[:80], "parked": True}
4147
4156
  except Exception as e:
4148
- log.debug(f"[meshcode] auto-checkpoint on done-status failed: {e}")
4157
+ log.debug(f"[meshcode] auto-park on done-status failed: {e}")
4149
4158
  return None
4150
4159
 
4151
4160
 
@@ -5079,15 +5088,15 @@ def meshcode_set_status(status: str, task: str = "") -> Dict[str, Any]:
5079
5088
  }
5080
5089
  except Exception:
5081
5090
  pass # best-effort; never block set_status
5082
- # CLOSEGAP-3 (task 69dc5a62, Samuel reframe ce506e0d): when the
5083
- # agent flips set_status='done' with exactly one active in_progress
5084
- # claim, auto-complete that claim with a status-driven summary.
5091
+ # CLOSEGAP-3 v2 (task 53767993, was 69dc5a62): when the agent flips
5092
+ # set_status='done'/'sleeping' with exactly one active in_progress claim,
5093
+ # PARK that claim (back to 'claimed' + checkpoint note) — never complete.
5085
5094
  # Baseline (NOT autonomous-gated). Best-effort.
5086
- if status == "done" and isinstance(resp, dict) and resp.get("ok"):
5095
+ if status in ("done", "sleeping") and isinstance(resp, dict) and resp.get("ok"):
5087
5096
  try:
5088
- _cp = _try_auto_complete_on_done_status(task)
5097
+ _cp = _try_auto_park_on_done_status(task, status)
5089
5098
  if _cp:
5090
- resp["auto_checkpointed_task"] = _cp
5099
+ resp["auto_parked_task"] = _cp
5091
5100
  except Exception:
5092
5101
  pass
5093
5102
  return resp
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.11.121
3
+ Version: 2.11.122
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.11.121"
7
+ version = "2.11.122"
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