meshcode 2.10.45__tar.gz → 2.10.47__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.45 → meshcode-2.10.47}/PKG-INFO +1 -1
  2. {meshcode-2.10.45 → meshcode-2.10.47}/meshcode/__init__.py +1 -1
  3. {meshcode-2.10.45 → meshcode-2.10.47}/meshcode/meshcode_mcp/server.py +230 -4
  4. {meshcode-2.10.45 → meshcode-2.10.47}/meshcode/run_agent.py +17 -5
  5. {meshcode-2.10.45 → meshcode-2.10.47}/meshcode.egg-info/PKG-INFO +1 -1
  6. {meshcode-2.10.45 → meshcode-2.10.47}/pyproject.toml +1 -1
  7. {meshcode-2.10.45 → meshcode-2.10.47}/README.md +0 -0
  8. {meshcode-2.10.45 → meshcode-2.10.47}/meshcode/ascii_art.py +0 -0
  9. {meshcode-2.10.45 → meshcode-2.10.47}/meshcode/cli.py +0 -0
  10. {meshcode-2.10.45 → meshcode-2.10.47}/meshcode/comms_v4.py +0 -0
  11. {meshcode-2.10.45 → meshcode-2.10.47}/meshcode/invites.py +0 -0
  12. {meshcode-2.10.45 → meshcode-2.10.47}/meshcode/launcher.py +0 -0
  13. {meshcode-2.10.45 → meshcode-2.10.47}/meshcode/launcher_install.py +0 -0
  14. {meshcode-2.10.45 → meshcode-2.10.47}/meshcode/meshcode_mcp/__init__.py +0 -0
  15. {meshcode-2.10.45 → meshcode-2.10.47}/meshcode/meshcode_mcp/__main__.py +0 -0
  16. {meshcode-2.10.45 → meshcode-2.10.47}/meshcode/meshcode_mcp/backend.py +0 -0
  17. {meshcode-2.10.45 → meshcode-2.10.47}/meshcode/meshcode_mcp/realtime.py +0 -0
  18. {meshcode-2.10.45 → meshcode-2.10.47}/meshcode/meshcode_mcp/test_backend.py +0 -0
  19. {meshcode-2.10.45 → meshcode-2.10.47}/meshcode/meshcode_mcp/test_realtime.py +0 -0
  20. {meshcode-2.10.45 → meshcode-2.10.47}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  21. {meshcode-2.10.45 → meshcode-2.10.47}/meshcode/preferences.py +0 -0
  22. {meshcode-2.10.45 → meshcode-2.10.47}/meshcode/protocol_v2.py +0 -0
  23. {meshcode-2.10.45 → meshcode-2.10.47}/meshcode/secrets.py +0 -0
  24. {meshcode-2.10.45 → meshcode-2.10.47}/meshcode/self_update.py +0 -0
  25. {meshcode-2.10.45 → meshcode-2.10.47}/meshcode/setup_clients.py +0 -0
  26. {meshcode-2.10.45 → meshcode-2.10.47}/meshcode.egg-info/SOURCES.txt +0 -0
  27. {meshcode-2.10.45 → meshcode-2.10.47}/meshcode.egg-info/dependency_links.txt +0 -0
  28. {meshcode-2.10.45 → meshcode-2.10.47}/meshcode.egg-info/entry_points.txt +0 -0
  29. {meshcode-2.10.45 → meshcode-2.10.47}/meshcode.egg-info/requires.txt +0 -0
  30. {meshcode-2.10.45 → meshcode-2.10.47}/meshcode.egg-info/top_level.txt +0 -0
  31. {meshcode-2.10.45 → meshcode-2.10.47}/setup.cfg +0 -0
  32. {meshcode-2.10.45 → meshcode-2.10.47}/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.45
3
+ Version: 2.10.47
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.45"
2
+ __version__ = "2.10.47"
@@ -635,6 +635,12 @@ def with_working_status(func):
635
635
  _record_event_bg("tool_call", {"tool": name, "args_keys": list(kwargs.keys()), "estimated_tokens": _est_tokens})
636
636
  try:
637
637
  return await func(*args, **kwargs)
638
+ except asyncio.CancelledError:
639
+ # ESC in Claude Code cancels the in-flight task. Catch here
640
+ # to prevent cascade through FastMCP into the event loop.
641
+ # Return an error dict instead of propagating BaseException.
642
+ log.debug(f"[meshcode] tool {name} cancelled by client (ESC)")
643
+ return {"error": "cancelled_by_client", "tool": name}
638
644
  except Exception as e:
639
645
  if not skip:
640
646
  _auto_learn_error(name, e, list(kwargs.keys()))
@@ -1665,7 +1671,11 @@ async def meshcode_call(to: str, function: str, args: Any = None, timeout_second
1665
1671
  poll_interval = 1.0
1666
1672
  elapsed = 0.0
1667
1673
  while elapsed < timeout_seconds:
1668
- await _asyncio.sleep(poll_interval)
1674
+ try:
1675
+ await _asyncio.sleep(poll_interval)
1676
+ except _asyncio.CancelledError:
1677
+ log.debug("[meshcode] meshcode_call poll cancelled by ESC")
1678
+ return {"error": "cancelled_by_client", "call_id": call_id, "ok": False}
1669
1679
  elapsed += poll_interval
1670
1680
 
1671
1681
  # Check Realtime buffer first
@@ -1898,7 +1908,13 @@ async def meshcode_wait(timeout_seconds: int = 20, include_acks: bool = False) -
1898
1908
  # Keep polling until something actionable arrives.
1899
1909
  # The agent (LLM) is NOT called between iterations — zero token cost.
1900
1910
  while True:
1901
- result = await _meshcode_wait_inner(actual_timeout=capped_timeout, include_acks=include_acks)
1911
+ try:
1912
+ result = await _meshcode_wait_inner(actual_timeout=capped_timeout, include_acks=include_acks)
1913
+ except asyncio.CancelledError:
1914
+ # Safety net: if CancelledError escapes _meshcode_wait_inner
1915
+ # despite the inner shield, catch it here to prevent cascade.
1916
+ log.debug("[meshcode] meshcode_wait outer loop caught CancelledError")
1917
+ result = {"timed_out": True, "reason": "cancelled_by_client"}
1902
1918
 
1903
1919
  if result.get("got_message"):
1904
1920
  # Real message arrived — return to agent for processing
@@ -2041,7 +2057,20 @@ async def _meshcode_wait_inner(actual_timeout: int, include_acks: bool) -> Dict[
2041
2057
 
2042
2058
  if _rt_live:
2043
2059
  # 2a) Real async wait — zero CPU, zero Supabase calls.
2044
- woke = await _REALTIME.wait_for_message(timeout=float(actual_timeout))
2060
+ # Shield from CancelledError: when Claude Code presses ESC, it cancels
2061
+ # the in-flight asyncio task. CancelledError is a BaseException that
2062
+ # bypasses most try/except, cascades through FastMCP's tool handler,
2063
+ # and unwinds the event loop — killing the MCP server. By shielding
2064
+ # the await and catching CancelledError, we return a clean timeout
2065
+ # result instead. The mcp.run() retry loop restarts the event loop
2066
+ # without the lifespan cascade.
2067
+ try:
2068
+ woke = await asyncio.shield(
2069
+ _REALTIME.wait_for_message(timeout=float(actual_timeout))
2070
+ )
2071
+ except (asyncio.CancelledError, Exception) as _cancel_exc:
2072
+ log.debug(f"[meshcode] wait_for_message cancelled/failed: {type(_cancel_exc).__name__}")
2073
+ return {"timed_out": True, "reason": "cancelled_by_client"}
2045
2074
  if woke:
2046
2075
  buffered = _REALTIME.drain()
2047
2076
  if buffered:
@@ -2054,7 +2083,11 @@ async def _meshcode_wait_inner(actual_timeout: int, include_acks: bool) -> Dict[
2054
2083
  _poll_interval = 5
2055
2084
  _elapsed = 0
2056
2085
  while _elapsed < actual_timeout:
2057
- await asyncio.sleep(min(_poll_interval, actual_timeout - _elapsed))
2086
+ try:
2087
+ await asyncio.sleep(min(_poll_interval, actual_timeout - _elapsed))
2088
+ except asyncio.CancelledError:
2089
+ log.debug("[meshcode] DB poll sleep cancelled by ESC")
2090
+ return {"timed_out": True, "reason": "cancelled_by_client"}
2058
2091
  _elapsed += _poll_interval
2059
2092
  try:
2060
2093
  api_key = _get_api_key()
@@ -2456,6 +2489,199 @@ def meshcode_task_reassign(task_id: str, new_assignee: str) -> Dict[str, Any]:
2456
2489
  })
2457
2490
 
2458
2491
 
2492
+ # ----------------- CROSS-MESH TASKS -----------------
2493
+
2494
+ @mcp.tool()
2495
+ @with_working_status
2496
+ def meshcode_create_cross_mesh_task(target_mesh: str, title: str,
2497
+ description: str = "",
2498
+ assignee: Optional[str] = None,
2499
+ priority: str = "normal") -> Dict[str, Any]:
2500
+ """Create a task in a linked meshwork (requires active mesh link).
2501
+
2502
+ Args:
2503
+ target_mesh: Name of the target meshwork.
2504
+ title: Task title.
2505
+ description: Task description.
2506
+ assignee: Agent name in target mesh to assign to.
2507
+ priority: low / normal / high / urgent.
2508
+ """
2509
+ return be.sb_rpc("mc_create_cross_mesh_task", {
2510
+ "p_api_key": _get_api_key(),
2511
+ "p_target_mesh": target_mesh,
2512
+ "p_title": title,
2513
+ "p_description": description,
2514
+ "p_assignee": assignee,
2515
+ "p_priority": priority,
2516
+ "p_agent_name": AGENT_NAME,
2517
+ })
2518
+
2519
+
2520
+ # ----------------- GOALS -----------------
2521
+
2522
+ @mcp.tool()
2523
+ @with_working_status
2524
+ def meshcode_set_goal(title: str, description: str = "",
2525
+ priority: str = "normal", assignee: Optional[str] = None,
2526
+ target_date: Optional[str] = None,
2527
+ parent_id: Optional[str] = None) -> Dict[str, Any]:
2528
+ """Create a new goal for the meshwork.
2529
+
2530
+ Args:
2531
+ title: Goal title.
2532
+ description: Optional detailed description.
2533
+ priority: low / normal / high / urgent.
2534
+ assignee: Agent name to assign the goal to.
2535
+ target_date: Target date (YYYY-MM-DD format).
2536
+ parent_id: UUID of parent goal for sub-goals.
2537
+ """
2538
+ return be.sb_rpc("mc_create_goal", {
2539
+ "p_api_key": _get_api_key(),
2540
+ "p_title": title,
2541
+ "p_description": description,
2542
+ "p_priority": priority,
2543
+ "p_assignee": assignee,
2544
+ "p_target_date": target_date,
2545
+ "p_parent_id": parent_id,
2546
+ "p_agent_name": AGENT_NAME,
2547
+ })
2548
+
2549
+
2550
+ @mcp.tool()
2551
+ @with_working_status
2552
+ def meshcode_goals() -> Dict[str, Any]:
2553
+ """List all goals for this meshwork."""
2554
+ return be.sb_rpc("mc_get_goals", {
2555
+ "p_api_key": _get_api_key(),
2556
+ })
2557
+
2558
+
2559
+ @mcp.tool()
2560
+ @with_working_status
2561
+ def meshcode_update_goal(goal_id: str, status: Optional[str] = None,
2562
+ title: Optional[str] = None,
2563
+ description: Optional[str] = None,
2564
+ priority: Optional[str] = None,
2565
+ assignee: Optional[str] = None) -> Dict[str, Any]:
2566
+ """Update a goal's status or details.
2567
+
2568
+ Args:
2569
+ goal_id: UUID of the goal to update.
2570
+ status: active / in_progress / achieved / abandoned.
2571
+ title: New title (optional).
2572
+ description: New description (optional).
2573
+ priority: New priority (optional).
2574
+ assignee: New assignee (optional).
2575
+ """
2576
+ return be.sb_rpc("mc_update_goal", {
2577
+ "p_api_key": _get_api_key(),
2578
+ "p_goal_id": goal_id,
2579
+ "p_status": status,
2580
+ "p_title": title,
2581
+ "p_description": description,
2582
+ "p_priority": priority,
2583
+ "p_assignee": assignee,
2584
+ })
2585
+
2586
+
2587
+ # ----------------- MARKETPLACE -----------------
2588
+
2589
+ @mcp.tool()
2590
+ @with_working_status
2591
+ def meshcode_marketplace_search(query: Optional[str] = None,
2592
+ category: Optional[str] = None) -> Dict[str, Any]:
2593
+ """Search the MarketMesh marketplace for agents and meshes.
2594
+
2595
+ Args:
2596
+ query: Search term (matches name, description, tags).
2597
+ category: Filter by category (Development, Content, Marketing, etc.).
2598
+ """
2599
+ return be.sb_rpc("mc_marketplace_list", {
2600
+ "p_category": category,
2601
+ "p_search": query,
2602
+ "p_status": "available",
2603
+ })
2604
+
2605
+
2606
+ @mcp.tool()
2607
+ @with_working_status
2608
+ def meshcode_marketplace_install(template_slug: str,
2609
+ meshwork_name: str) -> Dict[str, Any]:
2610
+ """Install a marketplace item — creates a new meshwork with pre-configured agents.
2611
+
2612
+ Args:
2613
+ template_slug: The slug of the marketplace item to install.
2614
+ meshwork_name: Name for the new meshwork (must be unique).
2615
+ """
2616
+ return be.sb_rpc("mc_marketplace_install_by_key", {
2617
+ "p_api_key": _get_api_key(),
2618
+ "p_template_slug": template_slug,
2619
+ "p_meshwork_name": meshwork_name,
2620
+ })
2621
+
2622
+
2623
+ @mcp.tool()
2624
+ @with_working_status
2625
+ def meshcode_marketplace_list_installed() -> Dict[str, Any]:
2626
+ """List marketplace items you have installed."""
2627
+ return be.sb_rpc("mc_marketplace_installed_by_key", {
2628
+ "p_api_key": _get_api_key(),
2629
+ })
2630
+
2631
+
2632
+ # ----------------- WEBHOOKS -----------------
2633
+
2634
+ @mcp.tool()
2635
+ @with_working_status
2636
+ def meshcode_webhook_register(url: str,
2637
+ events: Optional[list] = None,
2638
+ description: str = "") -> Dict[str, Any]:
2639
+ """Register a webhook URL to receive POST notifications for mesh events.
2640
+
2641
+ Args:
2642
+ url: The URL to POST events to.
2643
+ events: List of event types to subscribe to.
2644
+ Options: agent_online, agent_offline, task_created, task_completed,
2645
+ message_cross_mesh, goal_updated.
2646
+ Default: all events (agent_online, agent_offline, task_created,
2647
+ task_completed, message_cross_mesh, goal_created, goal_updated).
2648
+ description: Human-readable description of this webhook.
2649
+
2650
+ Returns the webhook_id and signing secret (save the secret — shown only once).
2651
+ """
2652
+ return be.sb_rpc("mc_webhook_register", {
2653
+ "p_api_key": _get_api_key(),
2654
+ "p_project_id": _PROJECT_ID,
2655
+ "p_url": url,
2656
+ "p_events": events,
2657
+ "p_description": description,
2658
+ })
2659
+
2660
+
2661
+ @mcp.tool()
2662
+ @with_working_status
2663
+ def meshcode_webhook_list() -> Dict[str, Any]:
2664
+ """List all registered webhooks for this meshwork."""
2665
+ return be.sb_rpc("mc_webhook_list", {
2666
+ "p_api_key": _get_api_key(),
2667
+ "p_project_id": _PROJECT_ID,
2668
+ })
2669
+
2670
+
2671
+ @mcp.tool()
2672
+ @with_working_status
2673
+ def meshcode_webhook_delete(webhook_id: str) -> Dict[str, Any]:
2674
+ """Delete a webhook by ID.
2675
+
2676
+ Args:
2677
+ webhook_id: UUID of the webhook to delete.
2678
+ """
2679
+ return be.sb_rpc("mc_webhook_delete", {
2680
+ "p_api_key": _get_api_key(),
2681
+ "p_webhook_id": webhook_id,
2682
+ })
2683
+
2684
+
2459
2685
  # ----------------- SCHEDULED TASKS -----------------
2460
2686
 
2461
2687
  @mcp.tool()
@@ -393,8 +393,8 @@ def _fetch_global_claude_version() -> Optional[str]:
393
393
  from urllib.request import Request, urlopen
394
394
  import json as _json
395
395
  req = Request(
396
- f"{url}/rest/v1/rpc/mc_get_global_config_by_key",
397
- data=_json.dumps({"p_api_key": api_key, "p_config_key": "claude_version"}).encode(),
396
+ f"{url}/rest/v1/rpc/mc_get_global_config",
397
+ data=_json.dumps({"p_key": "claude_code_version"}).encode(),
398
398
  headers={
399
399
  "Content-Type": "application/json",
400
400
  "apikey": anon_key,
@@ -434,8 +434,20 @@ def _resolve_pinned_claude(editor_cmd: str) -> str:
434
434
  print(f"[meshcode] Global admin pin: Claude Code v{pinned}", file=sys.stderr)
435
435
  if not pinned:
436
436
  pinned = load_prefs().get("claude_version", "")
437
+
438
+ # "latest" or "none" means skip pinning — use whatever is installed
439
+ if pinned and pinned.lower() in ("latest", "none", "skip"):
440
+ print(f"[meshcode] Version pin disabled ({pinned}) — using installed version", file=sys.stderr)
441
+ return editor_cmd
442
+
437
443
  if not pinned:
438
- return editor_cmd # No pin use installed version
444
+ # Default pin: v2.1.104 is the last Claude Code version where ESC
445
+ # doesn't kill the MCP server. Newer versions send CancelledError
446
+ # that cascades through the event loop. This default is overridden
447
+ # by any of the 3 pin sources above (set to "latest" to disable).
448
+ # Remove this default once Anthropic fixes the ESC bug upstream.
449
+ pinned = "2.1.104"
450
+ print(f"[meshcode] Default ESC-safe pin: Claude Code v{pinned}", file=sys.stderr)
439
451
 
440
452
  current = _get_claude_version(editor_cmd)
441
453
  if current == pinned:
@@ -707,7 +719,7 @@ def run(agent: str, project: Optional[str] = None, editor_override: Optional[str
707
719
  if not editor:
708
720
  print("[meshcode] ERROR: 'claude' not found in PATH", file=sys.stderr)
709
721
  print(f"[meshcode] Workspace is ready at: {ws}", file=sys.stderr)
710
- print("[meshcode] Install Claude Code: npm install -g @anthropic-ai/claude-code", file=sys.stderr)
722
+ print("[meshcode] Install Claude Code: npm install -g @anthropic-ai/claude-code@2.1.104", file=sys.stderr)
711
723
  if sys.platform == "win32":
712
724
  print("[meshcode] Then verify: where claude", file=sys.stderr)
713
725
  print("[meshcode] If 'where' finds it, open a NEW terminal and try again.", file=sys.stderr)
@@ -803,7 +815,7 @@ def run(agent: str, project: Optional[str] = None, editor_override: Optional[str
803
815
  else:
804
816
  print(f"[meshcode] Permission mode: bypass requested but --dangerously-skip-permissions")
805
817
  print(f"[meshcode] not supported by this Claude Code version. Agent will prompt for tools.")
806
- print(f"[meshcode] Upgrade Claude Code: npm install -g @anthropic-ai/claude-code@latest")
818
+ print(f"[meshcode] Upgrade Claude Code: npm install -g @anthropic-ai/claude-code@2.1.104")
807
819
  else:
808
820
  print(f"[meshcode] Permission mode: safe (Claude will prompt for every tool call)")
809
821
  print(f"[meshcode] Tip: change with `meshcode prefs permission-mode bypass`")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.10.45
3
+ Version: 2.10.47
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.45"
7
+ version = "2.10.47"
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