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.
- {meshcode-2.10.22 → meshcode-2.10.25}/PKG-INFO +1 -1
- {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/__init__.py +1 -1
- {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/meshcode_mcp/server.py +163 -15
- {meshcode-2.10.22 → meshcode-2.10.25}/meshcode.egg-info/PKG-INFO +1 -1
- {meshcode-2.10.22 → meshcode-2.10.25}/pyproject.toml +1 -1
- {meshcode-2.10.22 → meshcode-2.10.25}/README.md +0 -0
- {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/ascii_art.py +0 -0
- {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/cli.py +0 -0
- {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/comms_v4.py +0 -0
- {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/invites.py +0 -0
- {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/launcher.py +0 -0
- {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/launcher_install.py +0 -0
- {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/preferences.py +0 -0
- {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/protocol_v2.py +0 -0
- {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/run_agent.py +0 -0
- {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/secrets.py +0 -0
- {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/self_update.py +0 -0
- {meshcode-2.10.22 → meshcode-2.10.25}/meshcode/setup_clients.py +0 -0
- {meshcode-2.10.22 → meshcode-2.10.25}/meshcode.egg-info/SOURCES.txt +0 -0
- {meshcode-2.10.22 → meshcode-2.10.25}/meshcode.egg-info/dependency_links.txt +0 -0
- {meshcode-2.10.22 → meshcode-2.10.25}/meshcode.egg-info/entry_points.txt +0 -0
- {meshcode-2.10.22 → meshcode-2.10.25}/meshcode.egg-info/requires.txt +0 -0
- {meshcode-2.10.22 → meshcode-2.10.25}/meshcode.egg-info/top_level.txt +0 -0
- {meshcode-2.10.22 → meshcode-2.10.25}/setup.cfg +0 -0
- {meshcode-2.10.22 → meshcode-2.10.25}/tests/test_status_enum_coverage.py +0 -0
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
"""MeshCode — Real-time communication between AI agents."""
|
|
2
|
-
__version__ = "2.10.
|
|
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
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
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 =
|
|
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
|
|
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)),
|
|
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
|
-
|
|
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]:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|