meshcode 2.11.118__tar.gz → 2.11.120__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 (93) hide show
  1. {meshcode-2.11.118 → meshcode-2.11.120}/PKG-INFO +1 -1
  2. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/__init__.py +1 -1
  3. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/meshcode_mcp/server.py +68 -1
  4. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/upload.py +5 -0
  5. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode.egg-info/PKG-INFO +1 -1
  6. {meshcode-2.11.118 → meshcode-2.11.120}/pyproject.toml +1 -1
  7. {meshcode-2.11.118 → meshcode-2.11.120}/README.md +0 -0
  8. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/__main__.py +0 -0
  9. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/_session_handoff_template.py +0 -0
  10. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/_stop_hook_template.py +0 -0
  11. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/ascii_art.py +0 -0
  12. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/atomic_push.py +0 -0
  13. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/claude_update.py +0 -0
  14. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/cli.py +0 -0
  15. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/comms_v4.py +0 -0
  16. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/compat.py +0 -0
  17. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/daemon.py +0 -0
  18. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/date_parse.py +0 -0
  19. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/doctor.py +0 -0
  20. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/error_hints.py +0 -0
  21. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/exceptions.py +0 -0
  22. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/hooks/__init__.py +0 -0
  23. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/hooks/repo_path_lock.py +0 -0
  24. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/hostd.py +0 -0
  25. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/invites.py +0 -0
  26. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/launcher.py +0 -0
  27. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/launcher_install.py +0 -0
  28. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/meshcode_mcp/__init__.py +0 -0
  29. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/meshcode_mcp/__main__.py +0 -0
  30. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/meshcode_mcp/backend.py +0 -0
  31. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/meshcode_mcp/realtime.py +0 -0
  32. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/meshcode_mcp/sleep_signals.py +0 -0
  33. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/meshcode_mcp/test_backend.py +0 -0
  34. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/meshcode_mcp/test_boot_timing.py +0 -0
  35. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/meshcode_mcp/test_install_guard.py +0 -0
  36. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/meshcode_mcp/test_prefs_claude_version.py +0 -0
  37. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/meshcode_mcp/test_realtime.py +0 -0
  38. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  39. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/preferences.py +0 -0
  40. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/protocol_handler.py +0 -0
  41. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/protocol_v2.py +0 -0
  42. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/quickstart.py +0 -0
  43. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/rpc_allowlist.py +0 -0
  44. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/run_agent.py +0 -0
  45. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/scripts/check_secrets.py +0 -0
  46. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/scripts/race_rate_harness.py +0 -0
  47. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/secrets.py +0 -0
  48. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/self_update.py +0 -0
  49. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/setup_clients.py +0 -0
  50. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/supervisor.py +0 -0
  51. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode/up.py +0 -0
  52. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode.egg-info/SOURCES.txt +0 -0
  53. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode.egg-info/dependency_links.txt +0 -0
  54. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode.egg-info/entry_points.txt +0 -0
  55. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode.egg-info/requires.txt +0 -0
  56. {meshcode-2.11.118 → meshcode-2.11.120}/meshcode.egg-info/top_level.txt +0 -0
  57. {meshcode-2.11.118 → meshcode-2.11.120}/setup.cfg +0 -0
  58. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_auto_update_hardening.py +0 -0
  59. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_autonomous_closegap_1.py +0 -0
  60. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_autonomous_closegap_2.py +0 -0
  61. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_autonomous_closegap_3.py +0 -0
  62. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_autonomous_prompt_inject.py +0 -0
  63. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_boot_bug_regression.py +0 -0
  64. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_color_truecolor.py +0 -0
  65. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_core.py +0 -0
  66. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_cross_agent_messaging.py +0 -0
  67. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_date_parse.py +0 -0
  68. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_doctor.py +0 -0
  69. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_epistemic_v1_python_sdk.py +0 -0
  70. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_epistemic_v1_stop_conditions.py +0 -0
  71. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_esc_deaf_state.py +0 -0
  72. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_exceptions.py +0 -0
  73. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_file_upload.py +0 -0
  74. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_init_device_code.py +0 -0
  75. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_install_guard.py +0 -0
  76. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_lease_sigterm_release.py +0 -0
  77. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_mark_read_batch.py +0 -0
  78. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_marketplace_ratings.py +0 -0
  79. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_migration_integrity.py +0 -0
  80. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_realtime_event_freshness.py +0 -0
  81. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_rls_cross_tenant.py +0 -0
  82. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_rpc_grants.py +0 -0
  83. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_rpc_migrations.py +0 -0
  84. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_run_agent_dry_run.py +0 -0
  85. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_run_agent_no_server_import.py +0 -0
  86. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_security_regressions.py +0 -0
  87. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_self_update_user_site.py +0 -0
  88. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_sentinel.py +0 -0
  89. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_setup_path.py +0 -0
  90. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_sleep_signals.py +0 -0
  91. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_status_enum_coverage.py +0 -0
  92. {meshcode-2.11.118 → meshcode-2.11.120}/tests/test_stay_on_loop_hook.py +0 -0
  93. {meshcode-2.11.118 → meshcode-2.11.120}/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.118
3
+ Version: 2.11.120
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.118"
2
+ __version__ = "2.11.120"
3
3
 
4
4
  # Exception hierarchy — eagerly imported (lightweight, no deps)
5
5
  from meshcode.exceptions import ( # noqa: F401
@@ -2906,7 +2906,8 @@ def meshcode_send(to: Union[str, List[str]], message: Any, in_reply_to: Optional
2906
2906
  # mode-2 soft-fail (R2-3): a malformed in_reply_to (not a valid uuid) would hit
2907
2907
  # the uuid cast at the RPC boundary and reject the WHOLE message. Drop it so the
2908
2908
  # message still sends (pairs with mig463's nonexistent-but-valid-uuid soft-fail).
2909
- if in_reply_to is not None and not re.fullmatch(
2909
+ import re as _re # .119 regression: no module-level `re`; local like lines 3809/3942/4008
2910
+ if in_reply_to is not None and not _re.fullmatch(
2910
2911
  r"[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}",
2911
2912
  str(in_reply_to)):
2912
2913
  in_reply_to = None
@@ -3460,6 +3461,72 @@ def meshcode_download_file(file_id: str) -> Dict[str, Any]:
3460
3461
  return _consume(content, mime_type, file_name)
3461
3462
 
3462
3463
 
3464
+ @mcp.tool()
3465
+ @with_working_status
3466
+ def meshcode_send_file(to: Union[str, List[str]], file_path: str,
3467
+ caption: str = "", in_reply_to: Optional[str] = None,
3468
+ sensitive: bool = False) -> Dict[str, Any]:
3469
+ """Send a local file to other agent(s) as a mesh attachment — one call
3470
+ instead of the 3-call paste dance (chunked text sends).
3471
+
3472
+ Uploads file_path to mesh storage (mc_register_file + meshcode-files
3473
+ bucket, 50MB cap) and delivers a message whose payload carries the
3474
+ FE-standard attachment shape {file: {file_id, file_name, mime_type}}.
3475
+ The dashboard renders a file chip; agent recipients retrieve content via
3476
+ meshcode_download_file(file_id). `to` accepts the same forms as
3477
+ meshcode_send (agent / CSV / list / agent@meshwork).
3478
+
3479
+ Args:
3480
+ to: recipient(s), meshcode_send forms.
3481
+ file_path: local path of the file to attach.
3482
+ caption: optional message text shown with the chip (default:
3483
+ "file: <name> (<size> bytes)").
3484
+ in_reply_to: optional message UUID to thread under.
3485
+ sensitive: hide from exports (secret/PII payloads).
3486
+ """
3487
+ from pathlib import Path as _P
3488
+ api_key = _get_api_key()
3489
+ if not api_key:
3490
+ return {"error": "no api key", "error_code": "auth_failed"}
3491
+ p = _P(file_path).expanduser()
3492
+ if not p.is_file():
3493
+ return {"error": f"file not found: {file_path}", "error_code": "file_not_found"}
3494
+
3495
+ # Upload reuses the proven CLI path (meshcode/upload.py): mc_register_file
3496
+ # RPC (api_key-authorized, mints file_id + storage_path) then a storage
3497
+ # POST. The POST prefers this workspace's service key when present —
3498
+ # mirrors meshcode_download_file's fallback order; guest workspaces
3499
+ # without it fall back to the anon key (storage policy permitting).
3500
+ from meshcode.upload import upload_file as _upload_file
3501
+ _storage_key = os.environ.get("MESHCODE_SUPABASE_SERVICE_KEY", "") or be.SUPABASE_KEY
3502
+ up = _upload_file(str(p), api_key, be.SUPABASE_URL, _storage_key)
3503
+ if not up.get("ok"):
3504
+ out = {"error": up.get("error", "upload failed"), "error_code": "upload_failed"}
3505
+ if up.get("file_id"):
3506
+ # metadata row exists but the binary never landed — surface the id
3507
+ # so the caller doesn't re-register on retry confusion.
3508
+ out["file_id"] = up["file_id"]
3509
+ out["note"] = "metadata registered, binary upload failed — retry sends a fresh file_id"
3510
+ return out
3511
+
3512
+ payload = {
3513
+ "type": "file_attachment",
3514
+ "text": caption or f"file: {up['file_name']} ({up['size_bytes']} bytes)",
3515
+ "file": {"file_id": up["file_id"], "file_name": up["file_name"],
3516
+ "mime_type": up["mime_type"]},
3517
+ }
3518
+ # msg_type stays default 'msg' — mc_messages.type carries a DB CHECK
3519
+ # (chk_mc_messages_type) that REFUSES unknown tags (live-caught 2026-06-09;
3520
+ # the "soft-validate" is SDK-side only). The attachment semantic rides in
3521
+ # payload.type + payload.file, which is what the FE chip renderer keys on.
3522
+ res = meshcode_send(to, payload, in_reply_to=in_reply_to, sensitive=sensitive)
3523
+ if isinstance(res, dict) and not res.get("error"):
3524
+ res = dict(res)
3525
+ res.update({"file_id": up["file_id"], "file_name": up["file_name"],
3526
+ "size_bytes": up["size_bytes"], "mime_type": up["mime_type"]})
3527
+ return res
3528
+
3529
+
3463
3530
  def _detect_global_done(messages: List[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
3464
3531
  """Return {reason, from} if the list contains a global_done signal, else None."""
3465
3532
  for m in messages:
@@ -4,6 +4,11 @@ import sys
4
4
  import json
5
5
  import mimetypes
6
6
  from pathlib import Path
7
+
8
+ # Python's mimetypes table lacks markdown — without this, .md uploads land as
9
+ # application/octet-stream and meshcode_download_file hands recipients a temp
10
+ # file instead of inline text (live-caught in the send_file E2E 2026-06-09).
11
+ mimetypes.add_type("text/markdown", ".md")
7
12
  from urllib.request import Request, urlopen
8
13
  from urllib.error import HTTPError
9
14
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.11.118
3
+ Version: 2.11.120
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.118"
7
+ version = "2.11.120"
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