meshcode 2.10.22__tar.gz → 2.10.25__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 (32) hide show
  1. {meshcode-2.10.22 → meshcode-2.10.25}/PKG-INFO +1 -1
  2. {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/__init__.py +1 -1
  3. {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/meshcode_mcp/server.py +163 -15
  4. {meshcode-2.10.22 → meshcode-2.10.25}/meshcode.egg-info/PKG-INFO +1 -1
  5. {meshcode-2.10.22 → meshcode-2.10.25}/pyproject.toml +1 -1
  6. {meshcode-2.10.22 → meshcode-2.10.25}/README.md +0 -0
  7. {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/ascii_art.py +0 -0
  8. {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/cli.py +0 -0
  9. {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/comms_v4.py +0 -0
  10. {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/invites.py +0 -0
  11. {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/launcher.py +0 -0
  12. {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/launcher_install.py +0 -0
  13. {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/meshcode_mcp/__init__.py +0 -0
  14. {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/meshcode_mcp/__main__.py +0 -0
  15. {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/meshcode_mcp/backend.py +0 -0
  16. {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/meshcode_mcp/realtime.py +0 -0
  17. {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/meshcode_mcp/test_backend.py +0 -0
  18. {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/meshcode_mcp/test_realtime.py +0 -0
  19. {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  20. {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/preferences.py +0 -0
  21. {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/protocol_v2.py +0 -0
  22. {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/run_agent.py +0 -0
  23. {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/secrets.py +0 -0
  24. {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/self_update.py +0 -0
  25. {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/setup_clients.py +0 -0
  26. {meshcode-2.10.22 → meshcode-2.10.25}/meshcode.egg-info/SOURCES.txt +0 -0
  27. {meshcode-2.10.22 → meshcode-2.10.25}/meshcode.egg-info/dependency_links.txt +0 -0
  28. {meshcode-2.10.22 → meshcode-2.10.25}/meshcode.egg-info/entry_points.txt +0 -0
  29. {meshcode-2.10.22 → meshcode-2.10.25}/meshcode.egg-info/requires.txt +0 -0
  30. {meshcode-2.10.22 → meshcode-2.10.25}/meshcode.egg-info/top_level.txt +0 -0
  31. {meshcode-2.10.22 → meshcode-2.10.25}/setup.cfg +0 -0
  32. {meshcode-2.10.22 → meshcode-2.10.25}/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.22
3
+ Version: 2.10.25
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.10.22"
2
+ __version__ = "2.10.25"
@@ -1345,14 +1345,12 @@ def meshcode_broadcast(payload: Any) -> Dict[str, Any]:
1345
1345
  elif not isinstance(payload, dict):
1346
1346
  return {"error": "payload must be a string or object", "got_type": type(payload).__name__}
1347
1347
 
1348
- agents = be.get_board(_PROJECT_ID)
1349
- sent = 0
1350
- for a in agents:
1351
- if a["name"] != AGENT_NAME:
1352
- be.send_message(_PROJECT_ID, AGENT_NAME, a["name"], payload, msg_type="broadcast",
1353
- api_key=_get_api_key())
1354
- sent += 1
1355
- return {"broadcast": True, "agents_notified": sent}
1348
+ # Canonical broadcast: one row with to_agent='*'. Every agent's inbox
1349
+ # query includes `to_agent = '*'` so all recipients see it, without the
1350
+ # N-row fanout that caused duplicate-render bugs.
1351
+ be.send_message(_PROJECT_ID, AGENT_NAME, "*", payload, msg_type="broadcast",
1352
+ api_key=_get_api_key())
1353
+ return {"broadcast": True, "to": "*"}
1356
1354
 
1357
1355
 
1358
1356
  @mcp.tool()
@@ -1488,7 +1486,7 @@ def _get_pending_tasks_summary() -> Optional[List[Dict[str, str]]]:
1488
1486
 
1489
1487
  @mcp.tool()
1490
1488
  @with_working_status
1491
- async def meshcode_wait(timeout_seconds: int = 120, include_acks: bool = False) -> Dict[str, Any]:
1489
+ async def meshcode_wait(timeout_seconds: int = 20, include_acks: bool = False) -> Dict[str, Any]:
1492
1490
  """Block until a mesh message arrives or a task needs attention.
1493
1491
 
1494
1492
  INTERNAL LOOP: This function loops internally and only returns when
@@ -1498,7 +1496,10 @@ async def meshcode_wait(timeout_seconds: int = 120, include_acks: bool = False)
1498
1496
  accidentally using ScheduleWakeup or exiting the loop.
1499
1497
 
1500
1498
  Args:
1501
- timeout_seconds: Max wait time per poll cycle (default 120, hard cap 120).
1499
+ timeout_seconds: Max wait time per poll cycle (default 20, hard cap 20).
1500
+ Short cap keeps the outer tool call bounded so the user can press
1501
+ ESC in Claude Code without killing the MCP server — the inner
1502
+ loop continues polling across cycles at zero token cost.
1502
1503
  """
1503
1504
  global _IN_WAIT, _CONSECUTIVE_IDLE_SECONDS, _LAST_SEEN_TS
1504
1505
 
@@ -1541,7 +1542,7 @@ async def meshcode_wait(timeout_seconds: int = 120, include_acks: bool = False)
1541
1542
 
1542
1543
  _IN_WAIT = True
1543
1544
  _set_state("waiting", "listening for messages")
1544
- capped_timeout = min(max(1, int(timeout_seconds)), 120)
1545
+ capped_timeout = min(max(1, int(timeout_seconds)), 20)
1545
1546
  try:
1546
1547
  # ── INTERNAL LOOP ──────────────────────────────────────────
1547
1548
  # Keep polling until something actionable arrives.
@@ -1944,17 +1945,44 @@ def meshcode_task_create(title: str, description: str = "", assignee: str = "*",
1944
1945
 
1945
1946
  @mcp.tool()
1946
1947
  @with_working_status
1947
- def meshcode_tasks(status_filter: Optional[str] = None) -> Dict[str, Any]:
1948
+ def meshcode_tasks(status_filter: Optional[str] = None, verbose: bool = False) -> Dict[str, Any]:
1948
1949
  """List tasks. Filter by status to find work.
1949
1950
 
1950
1951
  Args:
1951
1952
  status_filter: 'open' / 'in_progress' / 'done' / etc. None = all.
1953
+ verbose: When False (default), descriptions are truncated to 200 chars
1954
+ and null/empty fields are dropped to save tokens. Pass True for
1955
+ the full payload.
1952
1956
 
1953
1957
  Returns: {"ok": true, "tasks": [...]}
1954
1958
  """
1955
1959
  api_key = _get_api_key()
1956
1960
  include_done = status_filter == "done"
1957
- return be.task_list(api_key, _PROJECT_ID, AGENT_NAME, status_filter=status_filter, include_done=include_done)
1961
+ result = be.task_list(api_key, _PROJECT_ID, AGENT_NAME, status_filter=status_filter, include_done=include_done)
1962
+ if verbose or not isinstance(result, dict) or not result.get("ok"):
1963
+ return result
1964
+ compact: List[Dict[str, Any]] = []
1965
+ for t in result.get("tasks", []):
1966
+ desc = t.get("description") or ""
1967
+ row: Dict[str, Any] = {
1968
+ "id": t.get("id"),
1969
+ "title": t.get("title"),
1970
+ "status": t.get("status"),
1971
+ "priority": t.get("priority"),
1972
+ "assignee": t.get("assignee"),
1973
+ }
1974
+ if t.get("claimed_by"):
1975
+ row["claimed_by"] = t["claimed_by"]
1976
+ if t.get("parent_task_id"):
1977
+ row["parent_task_id"] = t["parent_task_id"]
1978
+ if desc:
1979
+ if len(desc) > 200:
1980
+ row["description"] = desc[:200]
1981
+ row["_description_truncated"] = True
1982
+ else:
1983
+ row["description"] = desc
1984
+ compact.append(row)
1985
+ return {"ok": True, "tasks": compact}
1958
1986
 
1959
1987
 
1960
1988
  @mcp.tool()
@@ -1967,9 +1995,32 @@ def meshcode_task_claim(task_id: str) -> Dict[str, Any]:
1967
1995
 
1968
1996
  @mcp.tool()
1969
1997
  @with_working_status
1970
- def meshcode_task_complete(task_id: str, summary: str = "") -> Dict[str, Any]:
1971
- """Complete a claimed task with summary. Auto-remembers the task summary."""
1998
+ def meshcode_task_complete(task_id: str, summary: str = "", force: bool = False) -> Dict[str, Any]:
1999
+ """Complete a claimed task with summary. Auto-remembers the task summary.
2000
+
2001
+ Refuses if the task has open subtasks, unless force=True is passed with a
2002
+ reason in `summary`. This prevents closing a parent while lanes are still
2003
+ in flight.
2004
+ """
1972
2005
  api_key = _get_api_key()
2006
+ if not force:
2007
+ try:
2008
+ listing = be.task_list(api_key, _PROJECT_ID, AGENT_NAME, status_filter=None, include_done=False)
2009
+ if isinstance(listing, dict) and listing.get("ok"):
2010
+ open_subs = [
2011
+ {"id": t["id"][:8], "title": t["title"][:80], "status": t["status"]}
2012
+ for t in listing.get("tasks", [])
2013
+ if t.get("parent_task_id") == task_id
2014
+ and t.get("status") in ("open", "in_progress", "in_review")
2015
+ ]
2016
+ if open_subs:
2017
+ return {
2018
+ "refused": True,
2019
+ "reason": f"Cannot complete — {len(open_subs)} open subtasks. Finish them or pass force=True with a reason in summary.",
2020
+ "open_subtasks": open_subs,
2021
+ }
2022
+ except Exception:
2023
+ pass # Best-effort check; don't block on listing failure.
1973
2024
  result = be.task_complete(api_key, _PROJECT_ID, task_id, AGENT_NAME, summary=summary)
1974
2025
  # Auto-remember task completion for future context
1975
2026
  if isinstance(result, dict) and result.get("ok") and summary:
@@ -2020,6 +2071,62 @@ def meshcode_task_reject(task_id: str, feedback: str = "") -> Dict[str, Any]:
2020
2071
  })
2021
2072
 
2022
2073
 
2074
+ @mcp.tool()
2075
+ @with_working_status
2076
+ def meshcode_task_search(title_contains: Optional[str] = None,
2077
+ status: Optional[str] = None,
2078
+ assignee: Optional[str] = None,
2079
+ limit: int = 20) -> Dict[str, Any]:
2080
+ """Search tasks by title/status/assignee. Use before task_create to dedupe."""
2081
+ return be.sb_rpc("mc_search_tasks", {
2082
+ "p_api_key": _get_api_key(),
2083
+ "p_project_id": _PROJECT_ID,
2084
+ "p_caller_agent": AGENT_NAME,
2085
+ "p_title_contains": title_contains,
2086
+ "p_status": status,
2087
+ "p_assignee": assignee,
2088
+ "p_limit": limit,
2089
+ })
2090
+
2091
+
2092
+ @mcp.tool()
2093
+ @with_working_status
2094
+ def meshcode_agent_list() -> Dict[str, Any]:
2095
+ """Roster of agents in this meshwork with status + last_heartbeat."""
2096
+ return be.sb_rpc("mc_list_agents", {
2097
+ "p_api_key": _get_api_key(),
2098
+ "p_project_id": _PROJECT_ID,
2099
+ })
2100
+
2101
+
2102
+ @mcp.tool()
2103
+ @with_working_status
2104
+ def meshcode_task_cancel(task_id: str, reason: str = "") -> Dict[str, Any]:
2105
+ """Cancel a task. Creator or commander only. Works on open/in_progress/in_review."""
2106
+ api_key = _get_api_key()
2107
+ return be.sb_rpc("mc_task_cancel", {
2108
+ "p_api_key": api_key,
2109
+ "p_project_id": _PROJECT_ID,
2110
+ "p_task_id": task_id,
2111
+ "p_cancelling_agent": AGENT_NAME,
2112
+ "p_reason": reason,
2113
+ })
2114
+
2115
+
2116
+ @mcp.tool()
2117
+ @with_working_status
2118
+ def meshcode_task_reassign(task_id: str, new_assignee: str) -> Dict[str, Any]:
2119
+ """Reassign an unclaimed open task to a different agent. Creator or commander only."""
2120
+ api_key = _get_api_key()
2121
+ return be.sb_rpc("mc_task_reassign", {
2122
+ "p_api_key": api_key,
2123
+ "p_project_id": _PROJECT_ID,
2124
+ "p_task_id": task_id,
2125
+ "p_reassigning_agent": AGENT_NAME,
2126
+ "p_new_assignee": new_assignee,
2127
+ })
2128
+
2129
+
2023
2130
  # ----------------- PROACTIVE HEALTH SCAN -----------------
2024
2131
 
2025
2132
  @mcp.tool()
@@ -2149,6 +2256,47 @@ def meshcode_links() -> Dict[str, Any]:
2149
2256
  })
2150
2257
 
2151
2258
 
2259
+ @mcp.tool()
2260
+ @with_working_status
2261
+ def meshcode_link_invite(source_project: str, note: str = "") -> Dict[str, Any]:
2262
+ """Generate a one-time invite token for another meshwork to link in.
2263
+
2264
+ Returns {token, expires_at}. Share the token out-of-band. The recipient
2265
+ calls meshcode_link_redeem(token, their_meshwork) to auto-link
2266
+ (no separate accept step).
2267
+ """
2268
+ return be.sb_rpc("mc_create_link_invite", {
2269
+ "p_source_project": source_project,
2270
+ "p_note": note,
2271
+ })
2272
+
2273
+
2274
+ @mcp.tool()
2275
+ @with_working_status
2276
+ def meshcode_link_redeem(token: str, target_meshwork: str) -> Dict[str, Any]:
2277
+ """Redeem an invite token — creates an ACTIVE mesh link in one step."""
2278
+ return be.sb_rpc("mc_redeem_link_invite", {
2279
+ "p_token": token,
2280
+ "p_target_project": target_meshwork,
2281
+ })
2282
+
2283
+
2284
+ @mcp.tool()
2285
+ @with_working_status
2286
+ def meshcode_link_invites(source_project: Optional[str] = None) -> Dict[str, Any]:
2287
+ """List link invites this user has created (active/redeemed/expired/revoked)."""
2288
+ return be.sb_rpc("mc_list_my_link_invites", {
2289
+ "p_source_project": source_project,
2290
+ })
2291
+
2292
+
2293
+ @mcp.tool()
2294
+ @with_working_status
2295
+ def meshcode_link_invite_revoke(token: str) -> Dict[str, Any]:
2296
+ """Revoke an outstanding (unredeemed) invite token."""
2297
+ return be.sb_rpc("mc_revoke_link_invite", {"p_token": token})
2298
+
2299
+
2152
2300
  @mcp.tool()
2153
2301
  @with_working_status
2154
2302
  def meshcode_expand_link(link_id: str, agents: str) -> Dict[str, Any]:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.10.22
3
+ Version: 2.10.25
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.10.22"
7
+ version = "2.10.25"
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