meshcode 2.10.90__tar.gz → 2.10.93__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 (200) hide show
  1. {meshcode-2.10.90 → meshcode-2.10.93}/PKG-INFO +1 -1
  2. meshcode-2.10.93/meshcode/__init__.py +82 -0
  3. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode/meshcode_mcp/server.py +226 -121
  4. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode.egg-info/PKG-INFO +1 -1
  5. {meshcode-2.10.90 → meshcode-2.10.93}/pyproject.toml +1 -1
  6. meshcode-2.10.90/meshcode/__init__.py +0 -82
  7. {meshcode-2.10.90 → meshcode-2.10.93}/README.md +0 -0
  8. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode/ascii_art.py +0 -0
  9. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode/cli.py +0 -0
  10. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode/comms_v4.py +0 -0
  11. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode/compat.py +0 -0
  12. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode/error_hints.py +0 -0
  13. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode/exceptions.py +0 -0
  14. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode/invites.py +0 -0
  15. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode/launcher.py +0 -0
  16. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode/launcher_install.py +0 -0
  17. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode/meshcode_mcp/__init__.py +0 -0
  18. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode/meshcode_mcp/__main__.py +0 -0
  19. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode/meshcode_mcp/backend.py +0 -0
  20. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode/meshcode_mcp/realtime.py +0 -0
  21. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode/meshcode_mcp/test_backend.py +0 -0
  22. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode/meshcode_mcp/test_realtime.py +0 -0
  23. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  24. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode/preferences.py +0 -0
  25. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode/protocol_v2.py +0 -0
  26. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode/quickstart.py +0 -0
  27. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode/run_agent.py +0 -0
  28. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode/secrets.py +0 -0
  29. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode/self_update.py +0 -0
  30. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode/setup_clients.py +0 -0
  31. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode/supervisor.py +0 -0
  32. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode/upload.py +0 -0
  33. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/comms_v4.py +0 -0
  34. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/meshcode/__init__.py +0 -0
  35. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/meshcode/ascii_art.py +0 -0
  36. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/meshcode/cli.py +0 -0
  37. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/meshcode/comms_v4.py +0 -0
  38. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/meshcode/compat.py +0 -0
  39. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/meshcode/error_hints.py +0 -0
  40. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/meshcode/exceptions.py +0 -0
  41. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/meshcode/invites.py +0 -0
  42. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/meshcode/launcher.py +0 -0
  43. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/meshcode/launcher_install.py +0 -0
  44. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/meshcode/meshcode_mcp/__init__.py +0 -0
  45. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/meshcode/meshcode_mcp/__main__.py +0 -0
  46. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/meshcode/meshcode_mcp/backend.py +0 -0
  47. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/meshcode/meshcode_mcp/realtime.py +0 -0
  48. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/meshcode/meshcode_mcp/server.py +0 -0
  49. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/meshcode/meshcode_mcp/test_backend.py +0 -0
  50. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/meshcode/meshcode_mcp/test_realtime.py +0 -0
  51. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  52. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/meshcode/preferences.py +0 -0
  53. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/meshcode/protocol_v2.py +0 -0
  54. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/meshcode/quickstart.py +0 -0
  55. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/meshcode/run_agent.py +0 -0
  56. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/meshcode/secrets.py +0 -0
  57. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/meshcode/self_update.py +0 -0
  58. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/meshcode/setup_clients.py +0 -0
  59. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/meshcode/supervisor.py +0 -0
  60. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/meshcode/upload.py +0 -0
  61. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/scripts/sentinel.py +0 -0
  62. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/tests/test_core.py +0 -0
  63. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/tests/test_cross_agent_messaging.py +0 -0
  64. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/tests/test_esc_deaf_state.py +0 -0
  65. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/tests/test_exceptions.py +0 -0
  66. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/tests/test_mark_read_batch.py +0 -0
  67. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/tests/test_migration_integrity.py +0 -0
  68. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/tests/test_realtime_event_freshness.py +0 -0
  69. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/tests/test_rls_cross_tenant.py +0 -0
  70. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/tests/test_rpc_migrations.py +0 -0
  71. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/tests/test_security_regressions.py +0 -0
  72. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/tests/test_sentinel.py +0 -0
  73. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-backend-wt/tests/test_status_enum_coverage.py +0 -0
  74. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/build/lib/meshcode/__init__.py +0 -0
  75. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/build/lib/meshcode/ascii_art.py +0 -0
  76. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/build/lib/meshcode/cli.py +0 -0
  77. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/build/lib/meshcode/comms_v4.py +0 -0
  78. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/build/lib/meshcode/compat.py +0 -0
  79. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/build/lib/meshcode/error_hints.py +0 -0
  80. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/build/lib/meshcode/exceptions.py +0 -0
  81. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/build/lib/meshcode/invites.py +0 -0
  82. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/build/lib/meshcode/launcher.py +0 -0
  83. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/build/lib/meshcode/launcher_install.py +0 -0
  84. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/__init__.py +0 -0
  85. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/__main__.py +0 -0
  86. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/backend.py +0 -0
  87. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/realtime.py +0 -0
  88. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/server.py +0 -0
  89. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_backend.py +0 -0
  90. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_realtime.py +0 -0
  91. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  92. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/build/lib/meshcode/preferences.py +0 -0
  93. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/build/lib/meshcode/protocol_v2.py +0 -0
  94. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/build/lib/meshcode/quickstart.py +0 -0
  95. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/build/lib/meshcode/run_agent.py +0 -0
  96. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/build/lib/meshcode/secrets.py +0 -0
  97. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/build/lib/meshcode/self_update.py +0 -0
  98. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/build/lib/meshcode/setup_clients.py +0 -0
  99. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/build/lib/meshcode/supervisor.py +0 -0
  100. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/build/lib/meshcode/upload.py +0 -0
  101. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/comms_v4.py +0 -0
  102. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/meshcode/__init__.py +0 -0
  103. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/meshcode/ascii_art.py +0 -0
  104. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/meshcode/cli.py +0 -0
  105. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/meshcode/comms_v4.py +0 -0
  106. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/meshcode/compat.py +0 -0
  107. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/meshcode/error_hints.py +0 -0
  108. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/meshcode/exceptions.py +0 -0
  109. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/meshcode/invites.py +0 -0
  110. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/meshcode/launcher.py +0 -0
  111. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/meshcode/launcher_install.py +0 -0
  112. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/meshcode/meshcode_mcp/__init__.py +0 -0
  113. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/meshcode/meshcode_mcp/__main__.py +0 -0
  114. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/meshcode/meshcode_mcp/backend.py +0 -0
  115. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/meshcode/meshcode_mcp/realtime.py +0 -0
  116. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/meshcode/meshcode_mcp/server.py +0 -0
  117. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/meshcode/meshcode_mcp/test_backend.py +0 -0
  118. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/meshcode/meshcode_mcp/test_realtime.py +0 -0
  119. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  120. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/meshcode/preferences.py +0 -0
  121. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/meshcode/protocol_v2.py +0 -0
  122. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/meshcode/quickstart.py +0 -0
  123. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/meshcode/run_agent.py +0 -0
  124. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/meshcode/secrets.py +0 -0
  125. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/meshcode/self_update.py +0 -0
  126. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/meshcode/setup_clients.py +0 -0
  127. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/meshcode/supervisor.py +0 -0
  128. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/meshcode/upload.py +0 -0
  129. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/scripts/sentinel.py +0 -0
  130. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/tests/test_core.py +0 -0
  131. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/tests/test_cross_agent_messaging.py +0 -0
  132. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/tests/test_esc_deaf_state.py +0 -0
  133. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/tests/test_exceptions.py +0 -0
  134. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/tests/test_mark_read_batch.py +0 -0
  135. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/tests/test_migration_integrity.py +0 -0
  136. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/tests/test_realtime_event_freshness.py +0 -0
  137. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/tests/test_rls_cross_tenant.py +0 -0
  138. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/tests/test_rpc_migrations.py +0 -0
  139. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/tests/test_security_regressions.py +0 -0
  140. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/tests/test_sentinel.py +0 -0
  141. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-noun-wt/tests/test_status_enum_coverage.py +0 -0
  142. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/comms_v4.py +0 -0
  143. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/meshcode/__init__.py +0 -0
  144. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/meshcode/ascii_art.py +0 -0
  145. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/meshcode/cli.py +0 -0
  146. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/meshcode/comms_v4.py +0 -0
  147. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/meshcode/compat.py +0 -0
  148. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/meshcode/error_hints.py +0 -0
  149. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/meshcode/exceptions.py +0 -0
  150. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/meshcode/invites.py +0 -0
  151. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/meshcode/launcher.py +0 -0
  152. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/meshcode/launcher_install.py +0 -0
  153. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/meshcode/meshcode_mcp/__init__.py +0 -0
  154. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/meshcode/meshcode_mcp/__main__.py +0 -0
  155. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/meshcode/meshcode_mcp/backend.py +0 -0
  156. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/meshcode/meshcode_mcp/realtime.py +0 -0
  157. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/meshcode/meshcode_mcp/server.py +0 -0
  158. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/meshcode/meshcode_mcp/test_backend.py +0 -0
  159. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/meshcode/meshcode_mcp/test_realtime.py +0 -0
  160. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  161. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/meshcode/preferences.py +0 -0
  162. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/meshcode/protocol_v2.py +0 -0
  163. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/meshcode/quickstart.py +0 -0
  164. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/meshcode/run_agent.py +0 -0
  165. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/meshcode/secrets.py +0 -0
  166. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/meshcode/self_update.py +0 -0
  167. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/meshcode/setup_clients.py +0 -0
  168. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/meshcode/supervisor.py +0 -0
  169. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/meshcode/upload.py +0 -0
  170. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/scripts/sentinel.py +0 -0
  171. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/tests/test_core.py +0 -0
  172. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/tests/test_cross_agent_messaging.py +0 -0
  173. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/tests/test_esc_deaf_state.py +0 -0
  174. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/tests/test_exceptions.py +0 -0
  175. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/tests/test_mark_read_batch.py +0 -0
  176. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/tests/test_migration_integrity.py +0 -0
  177. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/tests/test_realtime_event_freshness.py +0 -0
  178. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/tests/test_rls_cross_tenant.py +0 -0
  179. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/tests/test_rpc_migrations.py +0 -0
  180. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/tests/test_security_regressions.py +0 -0
  181. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/tests/test_sentinel.py +0 -0
  182. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode-tasks-wt/tests/test_status_enum_coverage.py +0 -0
  183. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode.egg-info/SOURCES.txt +0 -0
  184. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode.egg-info/dependency_links.txt +0 -0
  185. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode.egg-info/entry_points.txt +0 -0
  186. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode.egg-info/requires.txt +0 -0
  187. {meshcode-2.10.90 → meshcode-2.10.93}/meshcode.egg-info/top_level.txt +0 -0
  188. {meshcode-2.10.90 → meshcode-2.10.93}/setup.cfg +0 -0
  189. {meshcode-2.10.90 → meshcode-2.10.93}/tests/test_core.py +0 -0
  190. {meshcode-2.10.90 → meshcode-2.10.93}/tests/test_cross_agent_messaging.py +0 -0
  191. {meshcode-2.10.90 → meshcode-2.10.93}/tests/test_esc_deaf_state.py +0 -0
  192. {meshcode-2.10.90 → meshcode-2.10.93}/tests/test_exceptions.py +0 -0
  193. {meshcode-2.10.90 → meshcode-2.10.93}/tests/test_mark_read_batch.py +0 -0
  194. {meshcode-2.10.90 → meshcode-2.10.93}/tests/test_migration_integrity.py +0 -0
  195. {meshcode-2.10.90 → meshcode-2.10.93}/tests/test_realtime_event_freshness.py +0 -0
  196. {meshcode-2.10.90 → meshcode-2.10.93}/tests/test_rls_cross_tenant.py +0 -0
  197. {meshcode-2.10.90 → meshcode-2.10.93}/tests/test_rpc_migrations.py +0 -0
  198. {meshcode-2.10.90 → meshcode-2.10.93}/tests/test_security_regressions.py +0 -0
  199. {meshcode-2.10.90 → meshcode-2.10.93}/tests/test_sentinel.py +0 -0
  200. {meshcode-2.10.90 → meshcode-2.10.93}/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.90
3
+ Version: 2.10.93
4
4
  Summary: Real-time communication between AI agents — Supabase-backed CLI
5
5
  Author-email: MeshCode <hello@meshcode.io>
6
6
  License: MIT
@@ -0,0 +1,82 @@
1
+ """MeshCode — Real-time communication between AI agents."""
2
+ __version__ = "2.10.93"
3
+
4
+ # Exception hierarchy — eagerly imported (lightweight, no deps)
5
+ from meshcode.exceptions import ( # noqa: F401
6
+ MeshCodeError,
7
+ AuthError,
8
+ RPCError,
9
+ MeshCodeTimeoutError,
10
+ MeshCodeConnectionError,
11
+ )
12
+
13
+ # Public API — lazy imports to avoid heavy deps at import time
14
+ def __getattr__(name):
15
+ if name == "backend":
16
+ from meshcode.meshcode_mcp import backend
17
+ return backend
18
+ if name in _BACKEND_EXPORTS:
19
+ from meshcode.meshcode_mcp import backend
20
+ return getattr(backend, name)
21
+ if name in _SECRETS_EXPORTS:
22
+ from meshcode import secrets
23
+ return getattr(secrets, name)
24
+ raise AttributeError(f"module 'meshcode' has no attribute {name!r}")
25
+
26
+
27
+ # Backend: core messaging & agent management
28
+ _BACKEND_EXPORTS = {
29
+ "send_message",
30
+ "read_inbox",
31
+ "count_pending",
32
+ "get_board",
33
+ "heartbeat",
34
+ "set_status",
35
+ "register_agent",
36
+ "get_project_id",
37
+ "sb_rpc",
38
+ "task_create",
39
+ "task_list",
40
+ "encrypt_payload",
41
+ "decrypt_payload",
42
+ }
43
+
44
+ # Secrets: credential management
45
+ _SECRETS_EXPORTS = {
46
+ "get_api_key",
47
+ "set_api_key",
48
+ "list_profiles",
49
+ }
50
+
51
+ __all__ = [
52
+ "__version__",
53
+ "backend",
54
+ # Exceptions
55
+ "MeshCodeError",
56
+ "AuthError",
57
+ "RPCError",
58
+ "MeshCodeTimeoutError",
59
+ "MeshCodeConnectionError",
60
+ # Messaging
61
+ "send_message",
62
+ "read_inbox",
63
+ "count_pending",
64
+ # Agent management
65
+ "register_agent",
66
+ "get_project_id",
67
+ "get_board",
68
+ "heartbeat",
69
+ "set_status",
70
+ # Tasks
71
+ "task_create",
72
+ "task_list",
73
+ # Low-level
74
+ "sb_rpc",
75
+ # Encryption
76
+ "encrypt_payload",
77
+ "decrypt_payload",
78
+ # Credentials
79
+ "get_api_key",
80
+ "set_api_key",
81
+ "list_profiles",
82
+ ]
@@ -333,8 +333,50 @@ def _filter_and_mark(messages: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
333
333
  return out
334
334
 
335
335
 
336
+ _SLEEP_PAYLOAD_TYPES = {"sleep", "go_to_sleep", "shutdown", "got_done", "done",
337
+ "exit", "stop", "kill", "terminate"}
338
+ # Spanish + English markers (es-MX hot path per SDK-S6.1). Matched as
339
+ # substrings, lowercased before compare. Keep exclusive enough to not
340
+ # false-positive on casual chatter (e.g. "no quiero dormir" still matches —
341
+ # accepted tradeoff for user-control reliability).
342
+ _SLEEP_TEXT_MARKERS = (
343
+ # English
344
+ "go to sleep", "all sleep now", "sleep now", "got_done", "go_done",
345
+ "shut down", "shutdown", "stop now", "exit now",
346
+ # Spanish (es-MX, sammybenu's primary)
347
+ "a dormir", "todos a dormir", "duerme", "duerman", "dormir ahora",
348
+ "salir", "terminar", "para la sesion", "exit y duerme",
349
+ )
350
+
351
+
352
+ def _looks_like_sleep_signal(m: Dict[str, Any]) -> bool:
353
+ """Detect commander broadcasts / DMs that authorize the wait-loop exit.
354
+
355
+ Catches three encodings (BE-S5.11): structured payload.type, top-level
356
+ text marker, and broadcast-with-sleep-type. Single source of truth so
357
+ every receive path (PRODUCT RULE 2 + inner _meshcode_wait_inner) routes
358
+ sleep authorizations into done_signals consistently.
359
+ """
360
+ pl = m.get("payload") or {}
361
+ if isinstance(pl, dict):
362
+ if str(pl.get("type", "")).lower() in _SLEEP_PAYLOAD_TYPES:
363
+ return True
364
+ if str(pl.get("directive", "")).lower() in _SLEEP_PAYLOAD_TYPES:
365
+ return True
366
+ text = str(pl.get("text", "")).lower()
367
+ if any(marker in text for marker in _SLEEP_TEXT_MARKERS):
368
+ return True
369
+ return False
370
+
371
+
336
372
  def _split_messages(messages: List[Dict[str, Any]]) -> Dict[str, Any]:
337
- """Split a list of normalized message dicts into messages / acks / done_signals."""
373
+ """Split a list of normalized message dicts into messages / acks / done_signals.
374
+
375
+ BE-S5.11: classify ANY message (including broadcasts) carrying a sleep
376
+ intent into done_signals so meshcode_wait surfaces them with the
377
+ must_exit flag. Previous logic only matched type='done' literally,
378
+ which the broadcast path could never produce.
379
+ """
338
380
  real: List[Dict[str, Any]] = []
339
381
  acks: List[Dict[str, Any]] = []
340
382
  dones: List[Dict[str, Any]] = []
@@ -342,7 +384,7 @@ def _split_messages(messages: List[Dict[str, Any]]) -> Dict[str, Any]:
342
384
  t = m.get("type", "msg")
343
385
  if t == "ack":
344
386
  acks.append(m)
345
- elif t == "done":
387
+ elif t == "done" or _looks_like_sleep_signal(m):
346
388
  dones.append(m)
347
389
  else:
348
390
  real.append(m)
@@ -812,6 +854,42 @@ _INSTANCE_ID = f"mcp-{_uuid.uuid4().hex[:12]}"
812
854
  # launch via launchd/systemd) the orphan check is a no-op and we never
813
855
  # false-terminate a legitimately-detached process.
814
856
  _BOOT_PPID = os.getppid()
857
+
858
+
859
+ def _stdin_peer_dead() -> bool:
860
+ """Non-destructively check whether stdin's peer has closed.
861
+
862
+ Used by the orphan-MCP guard when PPID == 1 to distinguish the
863
+ parent-died-during-import race (peer dead → exit) from a legitimate
864
+ detached daemon launch (peer alive → tolerate).
865
+
866
+ Uses MSG_PEEK so any pending JSON-RPC bytes stay in the kernel buffer
867
+ for FastMCP's stdio reader. socket.detach() releases the fd without
868
+ closing it. Returns False on any error so we never falsely kill an
869
+ otherwise-healthy process.
870
+ """
871
+ try:
872
+ import socket as _sock
873
+ s = _sock.socket(fileno=0)
874
+ try:
875
+ s.setblocking(False)
876
+ try:
877
+ data = s.recv(1, _sock.MSG_PEEK)
878
+ # Empty bytes from a non-blocking recv == EOF == peer closed.
879
+ return data == b""
880
+ except BlockingIOError:
881
+ # Data not yet available but socket is healthy.
882
+ return False
883
+ except OSError:
884
+ # ENOTCONN / ENOTSOCK on a real pipe/tty → not a dead unix
885
+ # socket; treat as alive.
886
+ return False
887
+ finally:
888
+ s.detach()
889
+ except Exception:
890
+ return False
891
+
892
+
815
893
  # Boot-context bundle (S1-2.F2): mc_agent_boot_context result cached at
816
894
  # lifespan startup so the first 30s of tool calls can short-circuit
817
895
  # repeated queries. Tools that read this fall back to live RPCs after the
@@ -1137,64 +1215,15 @@ def _build_instructions() -> str:
1137
1215
 
1138
1216
  base = f"""You are agent "{AGENT_NAME}" in meshwork "{PROJECT_NAME}".{role_block}{launch_block}
1139
1217
 
1140
- {_db_loop}PERMANENT LOOP (MANDATORY — #1 RULE):
1141
- You MUST stay in meshcode_wait() forever. After EVERY action, call meshcode_wait().
1142
- NEVER exit. NEVER stop. NEVER say "standing by" without calling meshcode_wait().
1143
- The ONLY exits: user says "stop", got_done signal, or fatal error.
1144
- Timeout? Call meshcode_wait() again with 2x timeout (cap 1800s).
1145
-
1146
- LOOP: act → meshcode_send if needed → meshcode_wait() → repeat.
1147
-
1148
- RULES:
1149
- - Use MCP tools only (never CLI commands in bash).
1150
- - Tasks > messages. Claim tasks via meshcode_tasks/task_claim/task_complete.
1151
- - Messages <100 tokens. Long content → create task.
1152
- - No empty acks. JSON reports only.
1153
- - Threading: pass in_reply_to.
1154
- - sensitive=True for secrets/PII.
1155
-
1156
- SESSION START (do these IMMEDIATELY — don't wait for user input):
1157
- The moment you see these instructions — whether the user said "boot", "hi",
1158
- nothing at all, or anything else — run the full protocol. Never reply with
1159
- only a memory summary and then idle. If your client defers MCP tool schemas
1160
- (shows tool names without parameters), your FIRST action is to load them:
1218
+ {_db_loop}LOOP (#1 rule): act → meshcode_wait() → repeat. Never exit/stop without calling meshcode_wait(). EXIT IMMEDIATELY when wait returns must_exit=True (set status=sleeping then end session). Other exits: user says stop, fatal error. Timeout → re-call with 2x (cap 1800s).
1219
+
1220
+ RULES: MCP tools only. Tasks > messages. Messages <100 tokens (long → task). No empty acks, JSON only. Thread via in_reply_to. sensitive=True for secrets.
1221
+
1222
+ SESSION START (run NOW, don't wait for input). If tool schemas are deferred:
1161
1223
  ToolSearch(query="select:meshcode_set_status,meshcode_check,meshcode_tasks,meshcode_auto_wake,meshcode_status,meshcode_wait,meshcode_send,meshcode_task_claim,meshcode_task_complete,meshcode_remember,meshcode_recall")
1162
- Then execute:
1163
- 1. meshcode_set_status(status="online", task="ready")
1164
- 2. meshcode_check() read NEW messages only (last_seen is auto-restored from mesh memory on boot, old messages are skipped automatically)
1165
- 3. meshcode_tasks() — check for assigned/pending tasks and claim any unclaimed ones
1166
- 4. meshcode_auto_wake() — scan meshwork health, create tasks for issues found
1167
- 5. meshcode_status() — see who's online
1168
- 6. If other agents are online → meshcode_send them a greeting
1169
- 7. meshcode_wait() — enter the loop
1170
- DO NOT ASK THE USER "what do you want to work on" — the mesh tells you via tasks/messages.
1171
-
1172
- CRITICAL: You communicate by CALLING TOOLS, not by thinking or writing text.
1173
- To talk to another agent → call meshcode_send(to="agent", message="...")
1174
- To check who's online → call meshcode_status()
1175
- To wait for messages → call meshcode_wait()
1176
- NEVER just describe what you would do. DO IT by calling the tool.
1177
-
1178
- CROSS-MESH: meshcode_send(to="agent@meshwork") routes via active link.
1179
- meshcode_link(target) creates pending link, target accepts. Expand with
1180
- meshcode_expand_link(). No sensitive msgs cross-mesh.
1181
-
1182
- MEMORY: meshcode_remember(key, value) persists across sessions.
1183
- meshcode_recall(key?) retrieves. meshcode_forget(key) deletes.
1184
- Only remember reusable learnings: mistakes, feedback, patterns, preferences.
1185
- Do NOT save task summaries — tasks already persist in the task system.
1186
- Do NOT use memory for session state or ephemeral data.
1187
- Save reusable code patterns as template_* keys for instant recall.
1188
-
1189
- SCRATCHPAD: meshcode_scratchpad_set/get for shared meshwork-level context
1190
- (decisions, conventions, architecture notes). All agents can read/write.
1191
-
1192
- ACCOUNT MANAGEMENT: you can create meshworks (meshcode_create_meshwork),
1193
- add agents (meshcode_add_agent), edit roles/prompts (meshcode_edit_agent),
1194
- and edit other agents' memory (meshcode_edit_memory). Always tell the user
1195
- what CLI command to run next (e.g. "meshcode run backend in a new terminal").
1196
-
1197
- Setup help → README.md or https://meshcode.io/docs
1224
+ Then: set_status(online,ready) → check() → tasks() → auto_wake() → status() → wait(). Don't ask user "what to work on" — the mesh tells you.
1225
+
1226
+ COMMUNICATE BY CALLING TOOLS, not by thinking aloud. Cross-mesh: send(to="agent@meshwork"). Reference docs (memory/scratchpad/account ops) recall agent_protocol_quick_ref when needed.
1198
1227
  """
1199
1228
  # Inject commander protocol if this agent is a leader
1200
1229
  is_leader = _is_leader_agent()
@@ -1370,6 +1399,47 @@ _STASHED_SESSION = None
1370
1399
  _MAIN_LOOP: asyncio.AbstractEventLoop | None = None
1371
1400
 
1372
1401
  _heartbeat_stop = _threading.Event()
1402
+ _orphan_watchdog_stop = _threading.Event()
1403
+
1404
+
1405
+ def _orphan_watchdog_fn():
1406
+ """Independent orphan detector — polls every 2s.
1407
+
1408
+ Defense-in-depth alongside the heartbeat-loop orphan check. The
1409
+ heartbeat thread can stall on Supabase HTTP calls (5-30s), and the
1410
+ interval itself is 5-15s. This watchdog gives sub-2s detection of
1411
+ "parent terminal closed" so phantom MCPs can't accumulate Supabase
1412
+ HTTP traffic or appear alive on the dashboard.
1413
+
1414
+ Exits via os._exit(0) — SIG_IGN handlers in run_server() make
1415
+ signal-based shutdown unreliable.
1416
+ """
1417
+ import platform as _pl_wd
1418
+ if _pl_wd.system() == "Windows":
1419
+ # Windows doesn't auto-reparent to PID 1; rely on heartbeat path.
1420
+ return
1421
+ while not _orphan_watchdog_stop.is_set():
1422
+ try:
1423
+ ppid_now = os.getppid()
1424
+ if ppid_now == 1:
1425
+ # Parent exited (or boot was already orphaned). Confirm with
1426
+ # stdin peer probe to avoid killing intentional daemons.
1427
+ if _BOOT_PPID != 1 or _stdin_peer_dead():
1428
+ log.warning(
1429
+ f"orphan watchdog: parent gone "
1430
+ f"(boot_ppid={_BOOT_PPID} → ppid=1, "
1431
+ f"stdin_peer_dead={_stdin_peer_dead()}) — "
1432
+ f"force-exiting MCP for {AGENT_NAME}"
1433
+ )
1434
+ try:
1435
+ _release_lease()
1436
+ except Exception:
1437
+ pass
1438
+ _heartbeat_stop.set()
1439
+ os._exit(0)
1440
+ except Exception:
1441
+ pass
1442
+ _orphan_watchdog_stop.wait(2)
1373
1443
 
1374
1444
  # Windows CPU tracking: (Get-Process).CPU returns cumulative seconds, not a
1375
1445
  # real-time percentage like Unix `ps -o %cpu`. We track the previous reading
@@ -1470,29 +1540,37 @@ def _heartbeat_loop_inner():
1470
1540
  while not _heartbeat_stop.is_set():
1471
1541
  try:
1472
1542
  # Orphan-MCP guard (Unix): when the Claude Code parent exits without
1473
- # closing stdio, the MCP child gets reparented to PID 1 and would
1474
- # otherwise keep heartbeating forever the dashboard then shows the
1475
- # agent as alive ("sleeping") with no terminal behind it. Skip the
1476
- # check on Windows (no automatic reparenting) and skip when the
1477
- # boot PPID was already 1 (intentional daemon launch via launchd).
1478
- # Use os._exit(0) SIGTERM/SIGINT/SIGHUP are all installed as
1479
- # ignore handlers in run_server() so signaling ourselves is a no-op.
1543
+ # closing stdio, the MCP child gets reparented to PID 1. If we
1544
+ # heartbeat anyway, the dashboard shows the agent alive ("sleeping")
1545
+ # with no terminal behind it (incident 2026-05-04: front-2 phantom
1546
+ # ran 4h with PPID=1, fd 0/1/2 → unix:(none), ~5K HTTP calls).
1547
+ #
1548
+ # Two scenarios fire here:
1549
+ # (a) PPID went from real-parent 1: parent died after boot.
1550
+ # (b) PPID was already 1 at module import: parent died DURING
1551
+ # import (race), or it was intentionally daemon-launched.
1552
+ # For (b) we used to skip — that hid the race. Now we also peek
1553
+ # at stdin: if the unix-socket peer is gone, EOF is returned and
1554
+ # we exit. This catches the race without breaking real daemons
1555
+ # (a launchd plist that pipes a real stdin keeps blocking).
1480
1556
  try:
1481
1557
  import platform as _pl_orphan
1482
1558
  if (_pl_orphan.system() != "Windows"
1483
- and _BOOT_PPID != 1
1484
1559
  and os.getppid() == 1):
1485
- log.warning(
1486
- f"parent process exited (boot_ppid={_BOOT_PPID} → "
1487
- f"current_ppid=1) — orphan MCP for {AGENT_NAME}, "
1488
- f"releasing lease and exiting"
1489
- )
1490
- try:
1491
- _release_lease()
1492
- except Exception:
1493
- pass
1494
- _heartbeat_stop.set()
1495
- os._exit(0)
1560
+ _orphan = (_BOOT_PPID != 1) or _stdin_peer_dead()
1561
+ if _orphan:
1562
+ log.warning(
1563
+ f"parent process exited (boot_ppid={_BOOT_PPID} → "
1564
+ f"current_ppid=1, stdin_peer_dead={_BOOT_PPID == 1}) "
1565
+ f"— orphan MCP for {AGENT_NAME}, releasing lease "
1566
+ f"and exiting"
1567
+ )
1568
+ try:
1569
+ _release_lease()
1570
+ except Exception:
1571
+ pass
1572
+ _heartbeat_stop.set()
1573
+ os._exit(0)
1496
1574
  except Exception:
1497
1575
  pass
1498
1576
 
@@ -1726,7 +1804,16 @@ async def lifespan(_app):
1726
1804
  _heartbeat_stop.clear()
1727
1805
  hb_thread = _threading.Thread(target=_heartbeat_thread_fn, daemon=True, name="meshcode-heartbeat")
1728
1806
  hb_thread.start()
1729
- log.info(f"lifespan started — Realtime + heartbeat thread active for {AGENT_NAME}")
1807
+
1808
+ # Orphan watchdog — second layer of defense, independent of heartbeat
1809
+ # cadence. Polls every 2s for "PPID==1 AND stdin peer is dead" and
1810
+ # SIGKILLs self if so. Catches the close-terminal-while-MCP-is-paused
1811
+ # case faster than the 5-15s heartbeat tick (incident 2026-05-04).
1812
+ _orphan_watchdog_stop.clear()
1813
+ wd_thread = _threading.Thread(target=_orphan_watchdog_fn, daemon=True, name="meshcode-orphan-watchdog")
1814
+ wd_thread.start()
1815
+
1816
+ log.info(f"lifespan started — Realtime + heartbeat thread + orphan watchdog active for {AGENT_NAME}")
1730
1817
  # Enable session recording in backend.py (hot-reloadable)
1731
1818
  try:
1732
1819
  be.enable_recording(_get_api_key(), _PROJECT_ID, AGENT_NAME, _SESSION_ID)
@@ -1842,8 +1929,9 @@ async def meshcode_debug_sleep(seconds: int = 30) -> Dict[str, Any]:
1842
1929
  @mcp.tool()
1843
1930
  @with_working_status
1844
1931
  def meshcode_send(to: str, message: Any, in_reply_to: Optional[str] = None,
1845
- sensitive: bool = False, encrypted: bool = False) -> Dict[str, Any]:
1846
- """Send message. Use "agent@meshwork" for cross-mesh. sensitive=True hides from exports. Pass encrypted=True for secrets/credentials (AES-256-GCM)."""
1932
+ sensitive: bool = False, encrypted: bool = False,
1933
+ type: Optional[str] = None) -> Dict[str, Any]:
1934
+ """Send message. Use "agent@meshwork" for cross-mesh. sensitive=True hides from exports. encrypted=True for secrets (AES-256-GCM). type= optional typed-catalog tag; soft-validates, never refuses."""
1847
1935
  if not to or not to.strip():
1848
1936
  return {"error": "recipient 'to' cannot be empty"}
1849
1937
  to = to.strip()
@@ -1872,6 +1960,26 @@ def meshcode_send(to: str, message: Any, in_reply_to: Optional[str] = None,
1872
1960
  if _payload_len > 2000:
1873
1961
  return {"error": f"message too large ({_payload_len} chars). Use meshcode_task_create for long content. Messages must be structured JSON <2000 chars."}
1874
1962
 
1963
+ # Typed-catalog soft validation (BE-S4.2.sdk / mig 233): if caller
1964
+ # passes type=, fetch the schema and check required fields. NEVER
1965
+ # refuses the send — best-effort warn-only. Skips when no type given,
1966
+ # when schema fetch fails, or when the type is unknown.
1967
+ if type:
1968
+ try:
1969
+ _schema_resp = be.sb_rpc("mc_message_schema", {"p_type": type})
1970
+ if isinstance(_schema_resp, dict) and _schema_resp.get("ok"):
1971
+ _schema = _schema_resp.get("schema") or {}
1972
+ _required = (_schema.get("required") or []) if isinstance(_schema, dict) else []
1973
+ _missing = [k for k in _required if k not in payload]
1974
+ if _missing:
1975
+ log.warning(
1976
+ f"meshcode_send type={type!r} payload missing required "
1977
+ f"fields {_missing} per mc_message_schema. Sending anyway "
1978
+ f"(soft validation)."
1979
+ )
1980
+ except Exception as _vex:
1981
+ log.debug(f"type-schema validation skipped: {_vex}")
1982
+
1875
1983
  # Cross-mesh routing: if 'to' contains '@', parse as agent@meshwork
1876
1984
  if "@" in to:
1877
1985
  target_agent, target_meshwork = to.split("@", 1)
@@ -1904,13 +2012,14 @@ def meshcode_send(to: str, message: Any, in_reply_to: Optional[str] = None,
1904
2012
  "p_to_project": target_meshwork,
1905
2013
  "p_to_agent": target_agent,
1906
2014
  "p_payload": payload,
1907
- "p_type": "msg",
2015
+ "p_type": type or "msg",
1908
2016
  })
1909
2017
  if isinstance(result, dict) and result.get("ok") and encrypted:
1910
2018
  result["encrypted"] = True
1911
2019
  return result
1912
2020
 
1913
- return be.send_message(_PROJECT_ID, AGENT_NAME, to, payload, msg_type="msg",
2021
+ return be.send_message(_PROJECT_ID, AGENT_NAME, to, payload,
2022
+ msg_type=type or "msg",
1914
2023
  parent_msg_id=in_reply_to, sensitive=sensitive,
1915
2024
  api_key=_get_api_key(), encrypted=encrypted)
1916
2025
 
@@ -2331,20 +2440,7 @@ def _try_auto_claim_task() -> Optional[Dict[str, str]]:
2331
2440
  @mcp.tool()
2332
2441
  @with_working_status
2333
2442
  async def meshcode_wait(timeout_seconds: int = 20, include_acks: bool = False) -> Dict[str, Any]:
2334
- """Block until a mesh message arrives or a task needs attention.
2335
-
2336
- INTERNAL LOOP: This function loops internally and only returns when
2337
- there is real work (message, task, or done signal). The agent NEVER
2338
- needs to decide whether to call meshcode_wait() again — it just stays
2339
- blocked here until something happens. This prevents agents from
2340
- accidentally using ScheduleWakeup or exiting the loop.
2341
-
2342
- Args:
2343
- timeout_seconds: Max wait time per poll cycle (default 20, hard cap 20).
2344
- Short cap keeps the outer tool call bounded so the user can press
2345
- ESC in Claude Code without killing the MCP server — the inner
2346
- loop continues polling across cycles at zero token cost.
2347
- """
2443
+ """Block until a mesh message arrives or a task needs attention. Loops internally; agent never decides to re-call. timeout_seconds: per-cycle cap (default+max 20)."""
2348
2444
  global _IN_WAIT, _CONSECUTIVE_IDLE_SECONDS, _LAST_SEEN_TS
2349
2445
 
2350
2446
  # PRODUCT RULE 1: If agent has open tasks, refuse to wait. Work first.
@@ -2380,12 +2476,17 @@ async def meshcode_wait(timeout_seconds: int = 20, include_acks: bool = False) -
2380
2476
  split = _split_messages(deduped)
2381
2477
  # Only refuse for real messages — ack-only batches should not block wait
2382
2478
  if split["messages"] or split["done_signals"]:
2383
- return {
2479
+ resp = {
2384
2480
  "refused": True,
2385
2481
  "reason": f"You have {split['count']} unread messages. Process them before waiting.",
2386
2482
  "got_message": True,
2387
2483
  **split,
2388
2484
  }
2485
+ # BE-S5.11: surface explicit must_exit when sleep authorized.
2486
+ if split["done_signals"]:
2487
+ resp["must_exit"] = True
2488
+ resp["exit_reason"] = "sleep authorization received — set status=sleeping and end session"
2489
+ return resp
2389
2490
  # Ack-only batch — fall through to wait loop
2390
2491
  except Exception:
2391
2492
  pass
@@ -2497,6 +2598,13 @@ async def meshcode_wait(timeout_seconds: int = 20, include_acks: bool = False) -
2497
2598
  result["memory_hints"] = _hints
2498
2599
  except Exception:
2499
2600
  pass
2601
+ # BE-S5.11: surface must_exit + exit_reason at top level of inner-loop
2602
+ # results too, so commander broadcasts (which arrive via the inner
2603
+ # path, not PRODUCT RULE 2) authorize wait-loop exit consistently.
2604
+ if isinstance(result, dict) and result.get("done_signals"):
2605
+ result.setdefault("must_exit", True)
2606
+ result.setdefault("exit_reason",
2607
+ "sleep authorization received — set status=sleeping and end session")
2500
2608
  return result
2501
2609
  finally:
2502
2610
  _IN_WAIT = False
@@ -2742,16 +2850,7 @@ def meshcode_done(reason: str) -> Dict[str, Any]:
2742
2850
  @mcp.tool()
2743
2851
  @with_working_status
2744
2852
  def meshcode_check(include_acks: bool = False, since: Optional[str] = None, mark_read: bool = False) -> Dict[str, Any]:
2745
- """Peek at inbox (non-destructive). Returns pending count + new messages.
2746
-
2747
- Args:
2748
- include_acks: Include ack messages in response.
2749
- since: ISO-8601 timestamp. Only return messages newer than this.
2750
- Use meshcode_remember("last_seen", ts) to persist across sessions.
2751
- mark_read: When True, consume messages (mark as read in DB) instead of
2752
- just peeking. Useful during boot when meshcode_wait() refuses
2753
- to run because of open tasks.
2754
- """
2853
+ """Peek inbox (non-destructive). since=ISO ts (older skipped). mark_read=True consumes (use during boot when wait refuses on open tasks)."""
2755
2854
  global _LAST_SEEN_TS
2756
2855
  pending = be.count_pending(_PROJECT_ID, AGENT_NAME, api_key=_get_api_key())
2757
2856
  # Peek at realtime buffer WITHOUT draining — check is non-destructive
@@ -4264,22 +4363,28 @@ def _auto_update() -> None:
4264
4363
  log.debug(f"[meshcode] Auto-update failed: {e}")
4265
4364
  return
4266
4365
 
4267
- # 4. In MCP mode, NEVER re-exec it kills the stdio pipe to Claude Code.
4268
- # The new version will load on the next clean boot.
4269
- if os.environ.get("MESHCODE_MCP_SERVE") == "1":
4270
- print(f"[meshcode] Updated {current} {latest}. Will load on next boot (MCP mode — cannot restart).", file=sys.stderr)
4271
- return
4272
-
4273
- # CLI mode: safe to re-exec
4274
- print(f"[meshcode] Updated to {latest}, restarting...", file=sys.stderr)
4366
+ # 4. Re-exec so the upgraded code loads in THIS launch, not the next one.
4367
+ #
4368
+ # Unix (MCP + CLI mode): os.execv preserves the PID and stdio fds, so
4369
+ # Claude Code's pipe to us stays open. We're called from run_server()
4370
+ # BEFORE mcp.run() — no MCP protocol state has been negotiated yet, so
4371
+ # the handshake just happens with the new process image. This was
4372
+ # previously gated behind "NEVER re-exec in MCP mode" but field
4373
+ # evidence (Samuel 2026-05-04: "agente debe salir en la versión más
4374
+ # actualizada") shows the gate forces a two-launch upgrade cycle —
4375
+ # bug fixes shipped to PyPI never reach users on first relaunch.
4376
+ #
4377
+ # Windows: subprocess.Popen + sys.exit(0) on the parent DOES close the
4378
+ # stdio pipe to Claude Code (different PID). Keep the "next boot" path
4379
+ # there; ship a Windows execv replacement separately if it matters.
4380
+ print(f"[meshcode] Updated {current} → {latest}, re-exec'ing to load new code in this launch...", file=sys.stderr)
4275
4381
  os.environ["MESHCODE_UPDATED"] = "1"
4276
4382
  try:
4277
4383
  if sys.platform == "win32":
4278
- import subprocess as _sp_reexec
4279
- _sp_reexec.Popen([sys.executable] + sys.argv)
4280
- sys.exit(0)
4281
- else:
4282
- os.execv(sys.executable, [sys.executable] + sys.argv)
4384
+ # Windows can't preserve stdio across a re-exec. Defer to next boot.
4385
+ print(f"[meshcode] Windows MCP mode: new code loads on next boot.", file=sys.stderr)
4386
+ return
4387
+ os.execv(sys.executable, [sys.executable] + sys.argv)
4283
4388
  except Exception as e:
4284
4389
  log.debug(f"[meshcode] Re-exec failed: {e}, continuing with old version")
4285
4390
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: meshcode
3
- Version: 2.10.90
3
+ Version: 2.10.93
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.90"
7
+ version = "2.10.93"
8
8
  description = "Real-time communication between AI agents — Supabase-backed CLI"
9
9
  readme = "README.md"
10
10
  license = {text = "MIT"}
@@ -1,82 +0,0 @@
1
- """MeshCode — Real-time communication between AI agents."""
2
- __version__ = "2.10.90"
3
-
4
- # Exception hierarchy — eagerly imported (lightweight, no deps)
5
- from meshcode.exceptions import ( # noqa: F401
6
- MeshCodeError,
7
- AuthError,
8
- RPCError,
9
- MeshCodeTimeoutError,
10
- MeshCodeConnectionError,
11
- )
12
-
13
- # Public API — lazy imports to avoid heavy deps at import time
14
- def __getattr__(name):
15
- if name == "backend":
16
- from meshcode.meshcode_mcp import backend
17
- return backend
18
- if name in _BACKEND_EXPORTS:
19
- from meshcode.meshcode_mcp import backend
20
- return getattr(backend, name)
21
- if name in _SECRETS_EXPORTS:
22
- from meshcode import secrets
23
- return getattr(secrets, name)
24
- raise AttributeError(f"module 'meshcode' has no attribute {name!r}")
25
-
26
-
27
- # Backend: core messaging & agent management
28
- _BACKEND_EXPORTS = {
29
- "send_message",
30
- "read_inbox",
31
- "count_pending",
32
- "get_board",
33
- "heartbeat",
34
- "set_status",
35
- "register_agent",
36
- "get_project_id",
37
- "sb_rpc",
38
- "task_create",
39
- "task_list",
40
- "encrypt_payload",
41
- "decrypt_payload",
42
- }
43
-
44
- # Secrets: credential management
45
- _SECRETS_EXPORTS = {
46
- "get_api_key",
47
- "set_api_key",
48
- "list_profiles",
49
- }
50
-
51
- __all__ = [
52
- "__version__",
53
- "backend",
54
- # Exceptions
55
- "MeshCodeError",
56
- "AuthError",
57
- "RPCError",
58
- "MeshCodeTimeoutError",
59
- "MeshCodeConnectionError",
60
- # Messaging
61
- "send_message",
62
- "read_inbox",
63
- "count_pending",
64
- # Agent management
65
- "register_agent",
66
- "get_project_id",
67
- "get_board",
68
- "heartbeat",
69
- "set_status",
70
- # Tasks
71
- "task_create",
72
- "task_list",
73
- # Low-level
74
- "sb_rpc",
75
- # Encryption
76
- "encrypt_payload",
77
- "decrypt_payload",
78
- # Credentials
79
- "get_api_key",
80
- "set_api_key",
81
- "list_profiles",
82
- ]
File without changes
File without changes