meshcode 2.11.4__tar.gz → 2.11.6__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 (210) hide show
  1. {meshcode-2.11.4 → meshcode-2.11.6}/PKG-INFO +1 -1
  2. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/__init__.py +1 -1
  3. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/meshcode_mcp/server.py +317 -216
  4. meshcode-2.11.6/meshcode/meshcode_mcp/sleep_signals.py +110 -0
  5. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode.egg-info/PKG-INFO +1 -1
  6. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode.egg-info/SOURCES.txt +2 -0
  7. {meshcode-2.11.4 → meshcode-2.11.6}/pyproject.toml +1 -1
  8. meshcode-2.11.6/tests/test_sleep_signals.py +160 -0
  9. {meshcode-2.11.4 → meshcode-2.11.6}/README.md +0 -0
  10. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/_stop_hook_template.py +0 -0
  11. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/ascii_art.py +0 -0
  12. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/cli.py +0 -0
  13. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/comms_v4.py +0 -0
  14. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/compat.py +0 -0
  15. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/daemon.py +0 -0
  16. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/error_hints.py +0 -0
  17. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/exceptions.py +0 -0
  18. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/invites.py +0 -0
  19. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/launcher.py +0 -0
  20. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/launcher_install.py +0 -0
  21. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/meshcode_mcp/__init__.py +0 -0
  22. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/meshcode_mcp/__main__.py +0 -0
  23. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/meshcode_mcp/backend.py +0 -0
  24. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/meshcode_mcp/realtime.py +0 -0
  25. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/meshcode_mcp/test_backend.py +0 -0
  26. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/meshcode_mcp/test_realtime.py +0 -0
  27. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  28. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/preferences.py +0 -0
  29. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/protocol_handler.py +0 -0
  30. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/protocol_v2.py +0 -0
  31. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/quickstart.py +0 -0
  32. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/run_agent.py +0 -0
  33. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/scripts/check_secrets.py +0 -0
  34. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/scripts/race_rate_harness.py +0 -0
  35. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/secrets.py +0 -0
  36. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/self_update.py +0 -0
  37. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/setup_clients.py +0 -0
  38. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/supervisor.py +0 -0
  39. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode/upload.py +0 -0
  40. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/comms_v4.py +0 -0
  41. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/meshcode/__init__.py +0 -0
  42. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/meshcode/ascii_art.py +0 -0
  43. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/meshcode/cli.py +0 -0
  44. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/meshcode/comms_v4.py +0 -0
  45. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/meshcode/compat.py +0 -0
  46. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/meshcode/error_hints.py +0 -0
  47. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/meshcode/exceptions.py +0 -0
  48. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/meshcode/invites.py +0 -0
  49. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/meshcode/launcher.py +0 -0
  50. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/meshcode/launcher_install.py +0 -0
  51. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/meshcode/meshcode_mcp/__init__.py +0 -0
  52. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/meshcode/meshcode_mcp/__main__.py +0 -0
  53. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/meshcode/meshcode_mcp/backend.py +0 -0
  54. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/meshcode/meshcode_mcp/realtime.py +0 -0
  55. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/meshcode/meshcode_mcp/server.py +0 -0
  56. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/meshcode/meshcode_mcp/test_backend.py +0 -0
  57. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/meshcode/meshcode_mcp/test_realtime.py +0 -0
  58. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  59. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/meshcode/preferences.py +0 -0
  60. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/meshcode/protocol_v2.py +0 -0
  61. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/meshcode/quickstart.py +0 -0
  62. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/meshcode/run_agent.py +0 -0
  63. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/meshcode/secrets.py +0 -0
  64. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/meshcode/self_update.py +0 -0
  65. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/meshcode/setup_clients.py +0 -0
  66. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/meshcode/supervisor.py +0 -0
  67. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/meshcode/upload.py +0 -0
  68. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/scripts/sentinel.py +0 -0
  69. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/tests/test_core.py +0 -0
  70. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/tests/test_cross_agent_messaging.py +0 -0
  71. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/tests/test_esc_deaf_state.py +0 -0
  72. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/tests/test_exceptions.py +0 -0
  73. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/tests/test_mark_read_batch.py +0 -0
  74. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/tests/test_migration_integrity.py +0 -0
  75. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/tests/test_realtime_event_freshness.py +0 -0
  76. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/tests/test_rls_cross_tenant.py +0 -0
  77. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/tests/test_rpc_migrations.py +0 -0
  78. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/tests/test_security_regressions.py +0 -0
  79. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/tests/test_sentinel.py +0 -0
  80. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-backend-wt/tests/test_status_enum_coverage.py +0 -0
  81. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/build/lib/meshcode/__init__.py +0 -0
  82. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/build/lib/meshcode/ascii_art.py +0 -0
  83. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/build/lib/meshcode/cli.py +0 -0
  84. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/build/lib/meshcode/comms_v4.py +0 -0
  85. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/build/lib/meshcode/compat.py +0 -0
  86. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/build/lib/meshcode/error_hints.py +0 -0
  87. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/build/lib/meshcode/exceptions.py +0 -0
  88. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/build/lib/meshcode/invites.py +0 -0
  89. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/build/lib/meshcode/launcher.py +0 -0
  90. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/build/lib/meshcode/launcher_install.py +0 -0
  91. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/__init__.py +0 -0
  92. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/__main__.py +0 -0
  93. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/backend.py +0 -0
  94. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/realtime.py +0 -0
  95. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/server.py +0 -0
  96. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_backend.py +0 -0
  97. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_realtime.py +0 -0
  98. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  99. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/build/lib/meshcode/preferences.py +0 -0
  100. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/build/lib/meshcode/protocol_v2.py +0 -0
  101. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/build/lib/meshcode/quickstart.py +0 -0
  102. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/build/lib/meshcode/run_agent.py +0 -0
  103. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/build/lib/meshcode/secrets.py +0 -0
  104. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/build/lib/meshcode/self_update.py +0 -0
  105. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/build/lib/meshcode/setup_clients.py +0 -0
  106. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/build/lib/meshcode/supervisor.py +0 -0
  107. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/build/lib/meshcode/upload.py +0 -0
  108. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/comms_v4.py +0 -0
  109. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/meshcode/__init__.py +0 -0
  110. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/meshcode/ascii_art.py +0 -0
  111. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/meshcode/cli.py +0 -0
  112. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/meshcode/comms_v4.py +0 -0
  113. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/meshcode/compat.py +0 -0
  114. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/meshcode/error_hints.py +0 -0
  115. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/meshcode/exceptions.py +0 -0
  116. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/meshcode/invites.py +0 -0
  117. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/meshcode/launcher.py +0 -0
  118. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/meshcode/launcher_install.py +0 -0
  119. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/meshcode/meshcode_mcp/__init__.py +0 -0
  120. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/meshcode/meshcode_mcp/__main__.py +0 -0
  121. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/meshcode/meshcode_mcp/backend.py +0 -0
  122. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/meshcode/meshcode_mcp/realtime.py +0 -0
  123. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/meshcode/meshcode_mcp/server.py +0 -0
  124. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/meshcode/meshcode_mcp/test_backend.py +0 -0
  125. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/meshcode/meshcode_mcp/test_realtime.py +0 -0
  126. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  127. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/meshcode/preferences.py +0 -0
  128. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/meshcode/protocol_v2.py +0 -0
  129. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/meshcode/quickstart.py +0 -0
  130. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/meshcode/run_agent.py +0 -0
  131. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/meshcode/secrets.py +0 -0
  132. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/meshcode/self_update.py +0 -0
  133. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/meshcode/setup_clients.py +0 -0
  134. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/meshcode/supervisor.py +0 -0
  135. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/meshcode/upload.py +0 -0
  136. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/scripts/sentinel.py +0 -0
  137. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/tests/test_core.py +0 -0
  138. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/tests/test_cross_agent_messaging.py +0 -0
  139. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/tests/test_esc_deaf_state.py +0 -0
  140. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/tests/test_exceptions.py +0 -0
  141. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/tests/test_mark_read_batch.py +0 -0
  142. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/tests/test_migration_integrity.py +0 -0
  143. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/tests/test_realtime_event_freshness.py +0 -0
  144. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/tests/test_rls_cross_tenant.py +0 -0
  145. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/tests/test_rpc_migrations.py +0 -0
  146. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/tests/test_security_regressions.py +0 -0
  147. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/tests/test_sentinel.py +0 -0
  148. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-noun-wt/tests/test_status_enum_coverage.py +0 -0
  149. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/comms_v4.py +0 -0
  150. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/meshcode/__init__.py +0 -0
  151. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/meshcode/ascii_art.py +0 -0
  152. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/meshcode/cli.py +0 -0
  153. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/meshcode/comms_v4.py +0 -0
  154. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/meshcode/compat.py +0 -0
  155. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/meshcode/error_hints.py +0 -0
  156. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/meshcode/exceptions.py +0 -0
  157. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/meshcode/invites.py +0 -0
  158. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/meshcode/launcher.py +0 -0
  159. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/meshcode/launcher_install.py +0 -0
  160. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/meshcode/meshcode_mcp/__init__.py +0 -0
  161. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/meshcode/meshcode_mcp/__main__.py +0 -0
  162. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/meshcode/meshcode_mcp/backend.py +0 -0
  163. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/meshcode/meshcode_mcp/realtime.py +0 -0
  164. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/meshcode/meshcode_mcp/server.py +0 -0
  165. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/meshcode/meshcode_mcp/test_backend.py +0 -0
  166. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/meshcode/meshcode_mcp/test_realtime.py +0 -0
  167. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  168. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/meshcode/preferences.py +0 -0
  169. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/meshcode/protocol_v2.py +0 -0
  170. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/meshcode/quickstart.py +0 -0
  171. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/meshcode/run_agent.py +0 -0
  172. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/meshcode/secrets.py +0 -0
  173. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/meshcode/self_update.py +0 -0
  174. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/meshcode/setup_clients.py +0 -0
  175. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/meshcode/supervisor.py +0 -0
  176. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/meshcode/upload.py +0 -0
  177. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/scripts/sentinel.py +0 -0
  178. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/tests/test_core.py +0 -0
  179. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/tests/test_cross_agent_messaging.py +0 -0
  180. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/tests/test_esc_deaf_state.py +0 -0
  181. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/tests/test_exceptions.py +0 -0
  182. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/tests/test_mark_read_batch.py +0 -0
  183. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/tests/test_migration_integrity.py +0 -0
  184. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/tests/test_realtime_event_freshness.py +0 -0
  185. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/tests/test_rls_cross_tenant.py +0 -0
  186. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/tests/test_rpc_migrations.py +0 -0
  187. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/tests/test_security_regressions.py +0 -0
  188. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/tests/test_sentinel.py +0 -0
  189. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode-tasks-wt/tests/test_status_enum_coverage.py +0 -0
  190. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode.egg-info/dependency_links.txt +0 -0
  191. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode.egg-info/entry_points.txt +0 -0
  192. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode.egg-info/requires.txt +0 -0
  193. {meshcode-2.11.4 → meshcode-2.11.6}/meshcode.egg-info/top_level.txt +0 -0
  194. {meshcode-2.11.4 → meshcode-2.11.6}/setup.cfg +0 -0
  195. {meshcode-2.11.4 → meshcode-2.11.6}/tests/test_auto_update_hardening.py +0 -0
  196. {meshcode-2.11.4 → meshcode-2.11.6}/tests/test_core.py +0 -0
  197. {meshcode-2.11.4 → meshcode-2.11.6}/tests/test_cross_agent_messaging.py +0 -0
  198. {meshcode-2.11.4 → meshcode-2.11.6}/tests/test_esc_deaf_state.py +0 -0
  199. {meshcode-2.11.4 → meshcode-2.11.6}/tests/test_exceptions.py +0 -0
  200. {meshcode-2.11.4 → meshcode-2.11.6}/tests/test_lease_sigterm_release.py +0 -0
  201. {meshcode-2.11.4 → meshcode-2.11.6}/tests/test_mark_read_batch.py +0 -0
  202. {meshcode-2.11.4 → meshcode-2.11.6}/tests/test_migration_integrity.py +0 -0
  203. {meshcode-2.11.4 → meshcode-2.11.6}/tests/test_realtime_event_freshness.py +0 -0
  204. {meshcode-2.11.4 → meshcode-2.11.6}/tests/test_rls_cross_tenant.py +0 -0
  205. {meshcode-2.11.4 → meshcode-2.11.6}/tests/test_rpc_migrations.py +0 -0
  206. {meshcode-2.11.4 → meshcode-2.11.6}/tests/test_security_regressions.py +0 -0
  207. {meshcode-2.11.4 → meshcode-2.11.6}/tests/test_sentinel.py +0 -0
  208. {meshcode-2.11.4 → meshcode-2.11.6}/tests/test_status_enum_coverage.py +0 -0
  209. {meshcode-2.11.4 → meshcode-2.11.6}/tests/test_stay_on_loop_hook.py +0 -0
  210. {meshcode-2.11.4 → meshcode-2.11.6}/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.4
3
+ Version: 2.11.6
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.4"
2
+ __version__ = "2.11.6"
3
3
 
4
4
  # Exception hierarchy — eagerly imported (lightweight, no deps)
5
5
  from meshcode.exceptions import ( # noqa: F401
@@ -499,73 +499,18 @@ def _filter_and_mark(messages: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
499
499
  return out
500
500
 
501
501
 
502
- _SLEEP_PAYLOAD_TYPES = {"sleep", "go_to_sleep", "shutdown", "got_done", "done",
503
- "exit", "stop", "kill", "terminate"}
504
- # Multi-word imperative markers ONLY. Single Spanish verbs like "salir" /
505
- # "terminar" / "duerme" were too loose and caught idiomatic prose like
506
- # "terminaron sus tasks" or "no puedo salir" (msg cfbd36b0 2026-05-14 alexa +
507
- # commander incidents — feedback_done_signal_false_positive_idiomatic
508
- # precedent). The parser now requires a directive-shaped phrase: a verb
509
- # combined with a temporal/object qualifier ("ahora" / "todos" / "to sleep")
510
- # so describing an event in prose can't trip must_exit.
511
- _SLEEP_TEXT_MARKERS = (
512
- # English directives
513
- "go to sleep", "all sleep now", "sleep now", "got_done", "go_done",
514
- "shut down", "shutdown", "stop now", "exit now",
515
- # Spanish directives (es-MX, sammybenu's primary). All require either a
516
- # leading "a"/"todos" or a trailing "ahora" so plain conjugations
517
- # ("salir", "terminar", "duerme") in body prose don't trigger.
518
- "a dormir", "todos a dormir", "duerme ahora", "duerman ahora",
519
- "dormir ahora", "para la sesion", "exit y duerme",
502
+ # Sleep-signal parsing lives in a separate pure module so it can be tested
503
+ # without booting the full MCP server. Re-export the public surface here
504
+ # for back-compat with any in-tree caller that imports from server.
505
+ from .sleep_signals import ( # noqa: F401
506
+ _SLEEP_PAYLOAD_TYPES,
507
+ _SLEEP_TEXT_MARKERS,
508
+ _KNOWN_HUMAN_HANDLES,
509
+ _is_human_authored,
510
+ _looks_like_sleep_signal,
511
+ _split_messages,
520
512
  )
521
513
 
522
-
523
- def _looks_like_sleep_signal(m: Dict[str, Any]) -> bool:
524
- """Detect commander broadcasts / DMs that authorize the wait-loop exit.
525
-
526
- Catches three encodings (BE-S5.11): structured payload.type, top-level
527
- text marker, and broadcast-with-sleep-type. Single source of truth so
528
- every receive path (PRODUCT RULE 2 + inner _meshcode_wait_inner) routes
529
- sleep authorizations into done_signals consistently.
530
- """
531
- pl = m.get("payload") or {}
532
- if isinstance(pl, dict):
533
- if str(pl.get("type", "")).lower() in _SLEEP_PAYLOAD_TYPES:
534
- return True
535
- if str(pl.get("directive", "")).lower() in _SLEEP_PAYLOAD_TYPES:
536
- return True
537
- text = str(pl.get("text", "")).lower()
538
- if any(marker in text for marker in _SLEEP_TEXT_MARKERS):
539
- return True
540
- return False
541
-
542
-
543
- def _split_messages(messages: List[Dict[str, Any]]) -> Dict[str, Any]:
544
- """Split a list of normalized message dicts into messages / acks / done_signals.
545
-
546
- BE-S5.11: classify ANY message (including broadcasts) carrying a sleep
547
- intent into done_signals so meshcode_wait surfaces them with the
548
- must_exit flag. Previous logic only matched type='done' literally,
549
- which the broadcast path could never produce.
550
- """
551
- real: List[Dict[str, Any]] = []
552
- acks: List[Dict[str, Any]] = []
553
- dones: List[Dict[str, Any]] = []
554
- for m in messages:
555
- t = m.get("type", "msg")
556
- if t == "ack":
557
- acks.append(m)
558
- elif t == "done" or _looks_like_sleep_signal(m):
559
- dones.append(m)
560
- else:
561
- real.append(m)
562
- return {
563
- "messages": real,
564
- "acks": acks,
565
- "done_signals": dones,
566
- "count": len(real),
567
- }
568
-
569
514
  from . import backend as be
570
515
  from .realtime import RealtimeListener
571
516
 
@@ -1493,18 +1438,14 @@ def _build_instructions() -> str:
1493
1438
  f"\nUSER-PROVIDED ROLE PROMPT (from the dashboard):\n---\n{_LAUNCH_PROMPT}\n---\n"
1494
1439
  if _LAUNCH_PROMPT else ""
1495
1440
  )
1496
- # Pull the canonical loop directive from the DB so product can tune it
1497
- # without a client release. Falls back to the baked-in version below if
1498
- # the RPC isn't available (older DB) or the call fails.
1441
+ # MIG-PLAN-A 2026-05-18 (boot bug 00d73223 second RC): module-import-time
1442
+ # be.sb_rpc calls block MCP InitializeResult on commander-class agents with
1443
+ # heavy memory payloads, even after mig 312 closed the SDK↔DDL CHECK retry
1444
+ # storm. Defer ALL network I/O out of _build_instructions(). The baked-in
1445
+ # static LOOP directive below is sufficient for boot; if product later
1446
+ # wants live tuning, fetch in BG and inject via memory_hints envelope on
1447
+ # first tool call. NO be.sb_rpc here.
1499
1448
  _db_loop = ""
1500
- try:
1501
- _r = be.sb_rpc("mc_get_loop_directive", {})
1502
- if isinstance(_r, list) and _r:
1503
- _r = _r[0]
1504
- if isinstance(_r, str) and _r.strip():
1505
- _db_loop = _r.strip() + "\n\n"
1506
- except Exception:
1507
- pass
1508
1449
 
1509
1450
  base = f"""You are agent "{AGENT_NAME}" in meshwork "{PROJECT_NAME}".{role_block}{launch_block}
1510
1451
 
@@ -1517,6 +1458,8 @@ SESSION START (run NOW, don't wait for input). If tool schemas are deferred:
1517
1458
  Then: set_status(online,ready) → check() → tasks() → auto_wake() → status() → wait(). Don't ask user "what to work on" — the mesh tells you.
1518
1459
 
1519
1460
  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.
1461
+
1462
+ DECISIONS: when you face a non-trivial pick (architectural choice, risky migration, scope decision), call meshcode_decide(decision, alternatives, evidence_refs) BEFORE acting — returns a trace_id. Call meshcode_outcome(trace_id, outcome, outcome_eval) when the result becomes observable. Use meshcode_explain(memory_key=…) or (decision_substring=…) to surface prior traces + open contradictions. Memory hints with open_contradictions take a -0.2 ranking penalty — treat as a flag, not a veto.
1520
1463
  """
1521
1464
  # Inject commander protocol if this agent is a leader
1522
1465
  is_leader = _is_leader_agent()
@@ -1540,67 +1483,68 @@ COMMANDER PROTOCOL (you are the team lead):
1540
1483
  return base
1541
1484
 
1542
1485
 
1486
+ # MIG-PLAN-A 2026-05-18 (boot bug 00d73223 part 2): timer-bracketed
1487
+ # _INSTRUCTIONS construction. If module-import-time work exceeds the budget,
1488
+ # the regression sentinel (CI) trips so we don't silently regress past the
1489
+ # Claude Code MCP-handshake window again. Caught the zylo-commander 17:28
1490
+ # repro after mig 312 had already closed the SDK↔DDL retry storm.
1491
+ _INSTRUCTIONS_BUILD_NS = _time.monotonic_ns()
1543
1492
  _INSTRUCTIONS = _build_instructions()
1493
+ _INSTRUCTIONS_BUILD_NS = _time.monotonic_ns() - _INSTRUCTIONS_BUILD_NS
1494
+ log.info(
1495
+ f"_INSTRUCTIONS built in {_INSTRUCTIONS_BUILD_NS/1_000_000:.1f}ms "
1496
+ f"(budget <500ms — MCP handshake window)"
1497
+ )
1498
+ if _INSTRUCTIONS_BUILD_NS > 500_000_000:
1499
+ log.warning(
1500
+ f"_INSTRUCTIONS build exceeded 500ms budget ({_INSTRUCTIONS_BUILD_NS/1_000_000:.1f}ms) "
1501
+ f"— commander-class agents may fail MCP handshake on slower networks. "
1502
+ f"Investigate any be.sb_rpc / network I/O added to _build_instructions()."
1503
+ )
1544
1504
 
1545
- # Pre-load persistent memories into instructions (tiered, ~10x cheaper than legacy
1546
- # load-all). Strategy:
1547
- # - critical: full content always preloaded (cap 10 keys per agent)
1548
- # - reference: key list only (fetch values on demand via meshcode_recall)
1549
- # - episodic: NEVER preloaded search via meshcode_recall_search instead
1550
- # Falls back to legacy load-all if migration 080 isn't applied yet (overload missing).
1551
- try:
1552
- _api_key = _get_api_key()
1553
- _crit = be.sb_rpc("mc_memory_list", {
1554
- "p_api_key": _api_key,
1555
- "p_agent_name": AGENT_NAME,
1556
- "p_tier": "critical",
1557
- "p_project_name": PROJECT_NAME,
1558
- })
1559
- _ref = be.sb_rpc("mc_memory_list", {
1560
- "p_api_key": _api_key,
1561
- "p_agent_name": AGENT_NAME,
1562
- "p_tier": "reference",
1563
- "p_project_name": PROJECT_NAME,
1564
- })
1565
-
1566
- _crit_ok = isinstance(_crit, dict) and _crit.get("ok")
1567
- _ref_ok = isinstance(_ref, dict) and _ref.get("ok")
1568
-
1569
- if _crit_ok or _ref_ok:
1570
- _sections = []
1571
- if _crit_ok and _crit.get("memories"):
1572
- _crit_lines = [f" {m['key']}: {json.dumps(m['value'], default=str)}"
1573
- for m in _crit["memories"]]
1574
- _sections.append("CRITICAL MEMORIES (always loaded):\n" + "\n".join(_crit_lines))
1575
- if _ref_ok and _ref.get("memories"):
1576
- _ref_keys = [m["key"] for m in _ref["memories"]]
1577
- _sections.append(
1578
- "REFERENCE MEMORIES (key list — fetch on demand via meshcode_recall(key)):\n "
1579
- + ", ".join(_ref_keys)
1580
- )
1581
- _sections.append(
1582
- "EPISODIC MEMORIES: not preloaded. Search via "
1583
- "meshcode_recall_search(query) when you need past incident context."
1584
- )
1585
- _INSTRUCTIONS += "\n" + "\n\n".join(_sections) + "\n"
1586
- log.info(
1587
- f"pre-loaded tiered memories: critical={len(_crit.get('memories', [])) if _crit_ok else 0}, "
1588
- f"reference={len(_ref.get('memories', [])) if _ref_ok else 0}"
1589
- )
1590
- else:
1591
- # Legacy fallback: migration 080 not applied → load all (old behavior)
1592
- _legacy = be.sb_rpc("mc_memory_list", {
1593
- "p_api_key": _api_key,
1505
+ # MIG-PLAN-A 2026-05-18: memory pre-load deferred to BG daemon thread.
1506
+ # Was synchronous mc_memory_list (critical+reference tiers + legacy fallback)
1507
+ # that appended to module-global _INSTRUCTIONS at import time. On commander-
1508
+ # class agents with heavy memory accumulation, this exceeded Claude Code's
1509
+ # MCP InitializeResult window "MCP tools not in deferred list" until
1510
+ # manual /mcp reconnect. Two prior partial fixes: fe57b7a5 mode-a (persona +
1511
+ # boot_context BG) and mig 312 (SDK↔DDL needs_launch CHECK). Plan A is the
1512
+ # remaining piece. Memory content is still surfaced — just via the
1513
+ # memory_hints envelope on the first meshcode_check / meshcode_boot tool
1514
+ # call instead of via MCP InitializeResult.instructions. Tradeoff: handshake
1515
+ # guaranteed <1s vs first-tool result ~1s heavier. Net wins handshake budget.
1516
+ _PRELOADED_CRITICAL: Optional[List[Dict[str, Any]]] = None
1517
+ _PRELOADED_REFERENCE_KEYS: Optional[List[str]] = None
1518
+
1519
+ def _preload_memories_bg() -> None:
1520
+ """Run by daemon thread spawned at lifespan start. Populates module-
1521
+ global _PRELOADED_* used by memory_hints envelope. Soft-fail; consumers
1522
+ fall through to live RPC paths if globals stay None."""
1523
+ global _PRELOADED_CRITICAL, _PRELOADED_REFERENCE_KEYS
1524
+ try:
1525
+ api_key = _get_api_key()
1526
+ crit = be.sb_rpc("mc_memory_list", {
1527
+ "p_api_key": api_key,
1594
1528
  "p_agent_name": AGENT_NAME,
1529
+ "p_tier": "critical",
1595
1530
  "p_project_name": PROJECT_NAME,
1596
1531
  })
1597
- if isinstance(_legacy, dict) and _legacy.get("ok") and _legacy.get("memories"):
1598
- _mem_lines = [f" {m['key']}: {json.dumps(m['value'], default=str)}"
1599
- for m in _legacy["memories"]]
1600
- _INSTRUCTIONS += "\nPRE-LOADED MEMORIES (legacy):\n" + "\n".join(_mem_lines) + "\n"
1601
- log.info(f"pre-loaded {len(_mem_lines)} memories (legacy fallback)")
1602
- except Exception as _e:
1603
- log.debug(f"memory pre-load skipped: {_e}")
1532
+ ref = be.sb_rpc("mc_memory_list", {
1533
+ "p_api_key": api_key,
1534
+ "p_agent_name": AGENT_NAME,
1535
+ "p_tier": "reference",
1536
+ "p_project_name": PROJECT_NAME,
1537
+ })
1538
+ if isinstance(crit, dict) and crit.get("ok"):
1539
+ _PRELOADED_CRITICAL = crit.get("memories") or []
1540
+ if isinstance(ref, dict) and ref.get("ok"):
1541
+ _PRELOADED_REFERENCE_KEYS = [m["key"] for m in (ref.get("memories") or []) if "key" in m]
1542
+ log.info(
1543
+ f"memory pre-load (bg): critical={len(_PRELOADED_CRITICAL or [])} "
1544
+ f"reference_keys={len(_PRELOADED_REFERENCE_KEYS or [])}"
1545
+ )
1546
+ except Exception as e:
1547
+ log.debug(f"memory pre-load (bg) skipped: {e}")
1604
1548
 
1605
1549
 
1606
1550
  # ============================================================
@@ -1718,8 +1662,26 @@ def _orphan_watchdog_fn():
1718
1662
  # Parent exited (or boot was already orphaned). Confirm with
1719
1663
  # stdin peer probe to avoid killing intentional daemons.
1720
1664
  if _BOOT_PPID != 1 or _stdin_peer_dead():
1665
+ # 2s confirmation grace before os._exit(0). Without this,
1666
+ # a transient ppid/stdin race (FastMCP stdio fd duplication,
1667
+ # brief reparent during macOS process-group shuffles) can
1668
+ # false-positive and kill an actively-attached MCP — observed
1669
+ # mode-b of fe57b7a5 (mid-session crash after ~10min normal
1670
+ # ops, Samuel 2026-05-14T19:31Z mesh-commander session).
1671
+ # We accept a +2s detection latency for legitimate orphans;
1672
+ # the heartbeat-path orphan check at line ~1798 still catches
1673
+ # them within one heartbeat tick (5-15s).
1674
+ _orphan_watchdog_stop.wait(2)
1675
+ if _orphan_watchdog_stop.is_set():
1676
+ break
1677
+ if os.getppid() != 1 or not (_BOOT_PPID != 1 or _stdin_peer_dead()):
1678
+ log.info(
1679
+ f"orphan watchdog: transient ppid==1 cleared after "
1680
+ f"2s grace (ppid_now={os.getppid()}) — continuing"
1681
+ )
1682
+ continue
1721
1683
  log.warning(
1722
- f"orphan watchdog: parent gone "
1684
+ f"orphan watchdog: parent gone (confirmed after 2s grace) "
1723
1685
  f"(boot_ppid={_BOOT_PPID} → ppid=1, "
1724
1686
  f"stdin_peer_dead={_stdin_peer_dead()}) — "
1725
1687
  f"force-exiting MCP for {AGENT_NAME}"
@@ -1852,18 +1814,34 @@ def _heartbeat_loop_inner():
1852
1814
  and os.getppid() == 1):
1853
1815
  _orphan = (_BOOT_PPID != 1) or _stdin_peer_dead()
1854
1816
  if _orphan:
1855
- log.warning(
1856
- f"parent process exited (boot_ppid={_BOOT_PPID} "
1857
- f"current_ppid=1, stdin_peer_dead={_BOOT_PPID == 1}) "
1858
- f"— orphan MCP for {AGENT_NAME}, releasing lease "
1859
- f"and exiting"
1860
- )
1861
- try:
1862
- _release_lease()
1863
- except Exception:
1864
- pass
1865
- _heartbeat_stop.set()
1866
- os._exit(0)
1817
+ # 2s confirmation grace — same rationale as the
1818
+ # orphan_watchdog above (fe57b7a5 mode-b). Heartbeat
1819
+ # ticks every 5-15s, so adding 2s here trades minimal
1820
+ # detection latency for false-positive resistance.
1821
+ _heartbeat_stop.wait(2)
1822
+ if _heartbeat_stop.is_set():
1823
+ break
1824
+ if os.getppid() != 1 or not ((_BOOT_PPID != 1) or _stdin_peer_dead()):
1825
+ log.info(
1826
+ f"heartbeat orphan check: transient ppid==1 "
1827
+ f"cleared after 2s grace (ppid_now={os.getppid()}) "
1828
+ f"— continuing"
1829
+ )
1830
+ else:
1831
+ log.warning(
1832
+ f"parent process exited "
1833
+ f"(confirmed after 2s grace) "
1834
+ f"(boot_ppid={_BOOT_PPID} → "
1835
+ f"current_ppid=1, stdin_peer_dead={_BOOT_PPID == 1}) "
1836
+ f"— orphan MCP for {AGENT_NAME}, releasing lease "
1837
+ f"and exiting"
1838
+ )
1839
+ try:
1840
+ _release_lease()
1841
+ except Exception:
1842
+ pass
1843
+ _heartbeat_stop.set()
1844
+ os._exit(0)
1867
1845
  except Exception:
1868
1846
  pass
1869
1847
 
@@ -2064,65 +2042,70 @@ async def lifespan(_app):
2064
2042
  log.warning(f"initial heartbeat attempt {_attempt+1} failed: {e}")
2065
2043
  import time; time.sleep(2)
2066
2044
 
2067
- # Per-agent persona (BE-S4.1.sdk / mig 232): tone/voice/policy that
2068
- # shapes how the agent talks. Loaded once per session; soft-fail if the
2069
- # RPC is unavailable on older projects.
2070
- global _PERSONA
2071
- try:
2072
- _persona_resp = be.sb_rpc("mc_agent_persona", {
2073
- "p_api_key": _get_api_key(),
2074
- "p_project_id": _PROJECT_ID,
2075
- "p_agent_name": AGENT_NAME,
2076
- })
2077
- if isinstance(_persona_resp, dict) and _persona_resp.get("ok"):
2078
- _PERSONA = _persona_resp.get("persona") or {}
2079
- log.info(
2080
- f"[meshcode] persona loaded: tone={_PERSONA.get('tone','default')} "
2081
- f"language={_PERSONA.get('language','en')} "
2082
- f"reply_max_tokens={_PERSONA.get('reply_max_tokens','unset')}"
2083
- )
2084
- else:
2085
- log.debug(f"persona load returned non-ok: {_persona_resp} — continuing without")
2086
- except Exception as e:
2087
- log.debug(f"persona RPC unavailable ({e}) — continuing without")
2088
-
2089
- # Boot-context bundle (S1-2.F2 / mig 217): single RPC returns last_seen
2090
- # + recent msgs + open tasks + relevant memories + peer state. Cached on
2091
- # the module global so the first ~30s of tool calls can short-circuit.
2092
- # Soft-fail: if the RPC isn't deployed (older project) or errors, log
2093
- # warn + continue with the legacy lazy-load path.
2094
- global _BOOT_CONTEXT_BUNDLE, _BOOT_CONTEXT_AT
2095
- try:
2096
- _ctx = be.sb_rpc("mc_agent_boot_context", {
2097
- "p_api_key": _get_api_key(),
2098
- "p_project_id": _PROJECT_ID,
2099
- "p_agent_name": AGENT_NAME,
2100
- })
2101
- if isinstance(_ctx, dict) and _ctx.get("ok"):
2102
- _BOOT_CONTEXT_BUNDLE = _ctx
2103
- _BOOT_CONTEXT_AT = _time.time()
2104
- log.info(
2105
- f"[meshcode] boot_context: "
2106
- f"{len(_ctx.get('recent_msgs', []))} unread, "
2107
- f"{len(_ctx.get('open_tasks', []))} open tasks, "
2108
- f"{len(_ctx.get('relevant_memories', []))} memories surfaced"
2109
- )
2110
- else:
2111
- # Defense-in-depth: if the failure is a missing GRANT (mig
2112
- # 270 regressed or older project), the legacy lazy-load
2113
- # path already fills the gap. WARN puts a stderr line that
2114
- # some Claude Code versions surface as "MCP server failed
2115
- # at launch". Downgrade only the GRANT case; other non-ok
2116
- # responses still warn.
2117
- if _is_grant_or_permission_error(_ctx):
2118
- log.debug(f"boot_context permission_denied (legacy boot ok): {_ctx}")
2045
+ # fe57b7a5 mode-a fix (2026-05-14): mc_agent_persona + mc_agent_boot_context
2046
+ # used to run synchronously here, adding ~1-4s to lifespan pre-yield latency.
2047
+ # Combined with the module-import RPCs (3-15s), commanders with large
2048
+ # _INSTRUCTIONS payloads + many memories exceeded Claude Code's MCP
2049
+ # handshake window under congestion → "MCP failed at launch". Both RPCs
2050
+ # produce module globals (_PERSONA, _BOOT_CONTEXT_BUNDLE) consumed lazily
2051
+ # by tools that already have soft-fail fallbacks, so deferring them into
2052
+ # a daemon thread is safe — first reads within ~1s after lifespan yield
2053
+ # may see None, in which case consumers fall back to live RPC paths.
2054
+ global _PERSONA, _BOOT_CONTEXT_BUNDLE, _BOOT_CONTEXT_AT
2055
+
2056
+ def _load_persona_bg():
2057
+ global _PERSONA
2058
+ try:
2059
+ _persona_resp = be.sb_rpc("mc_agent_persona", {
2060
+ "p_api_key": _get_api_key(),
2061
+ "p_project_id": _PROJECT_ID,
2062
+ "p_agent_name": AGENT_NAME,
2063
+ })
2064
+ if isinstance(_persona_resp, dict) and _persona_resp.get("ok"):
2065
+ _PERSONA = _persona_resp.get("persona") or {}
2066
+ log.info(
2067
+ f"[meshcode] persona loaded (bg): tone={_PERSONA.get('tone','default')} "
2068
+ f"language={_PERSONA.get('language','en')} "
2069
+ f"reply_max_tokens={_PERSONA.get('reply_max_tokens','unset')}"
2070
+ )
2119
2071
  else:
2120
- log.warning(f"boot_context returned non-ok: {_ctx} — continuing legacy boot")
2121
- except Exception as e:
2122
- if _is_grant_or_permission_error(str(e)):
2123
- log.debug(f"boot_context permission_denied (legacy boot ok): {e}")
2124
- else:
2125
- log.warning(f"boot_context RPC unavailable ({e}) — continuing legacy boot")
2072
+ log.debug(f"persona load returned non-ok (bg): {_persona_resp} — continuing without")
2073
+ except Exception as e:
2074
+ log.debug(f"persona RPC unavailable (bg) ({e}) — continuing without")
2075
+
2076
+ def _load_boot_context_bg():
2077
+ global _BOOT_CONTEXT_BUNDLE, _BOOT_CONTEXT_AT
2078
+ try:
2079
+ _ctx = be.sb_rpc("mc_agent_boot_context", {
2080
+ "p_api_key": _get_api_key(),
2081
+ "p_project_id": _PROJECT_ID,
2082
+ "p_agent_name": AGENT_NAME,
2083
+ })
2084
+ if isinstance(_ctx, dict) and _ctx.get("ok"):
2085
+ _BOOT_CONTEXT_BUNDLE = _ctx
2086
+ _BOOT_CONTEXT_AT = _time.time()
2087
+ log.info(
2088
+ f"[meshcode] boot_context (bg): "
2089
+ f"{len(_ctx.get('recent_msgs', []))} unread, "
2090
+ f"{len(_ctx.get('open_tasks', []))} open tasks, "
2091
+ f"{len(_ctx.get('relevant_memories', []))} memories surfaced"
2092
+ )
2093
+ else:
2094
+ if _is_grant_or_permission_error(_ctx):
2095
+ log.debug(f"boot_context permission_denied (bg, legacy boot ok): {_ctx}")
2096
+ else:
2097
+ log.warning(f"boot_context returned non-ok (bg): {_ctx} — continuing legacy boot")
2098
+ except Exception as e:
2099
+ if _is_grant_or_permission_error(str(e)):
2100
+ log.debug(f"boot_context permission_denied (bg, legacy boot ok): {e}")
2101
+ else:
2102
+ log.warning(f"boot_context RPC unavailable (bg) ({e}) — continuing legacy boot")
2103
+
2104
+ _threading.Thread(target=_load_persona_bg, daemon=True, name="meshcode-load-persona").start()
2105
+ _threading.Thread(target=_load_boot_context_bg, daemon=True, name="meshcode-load-boot-context").start()
2106
+ # MIG-PLAN-A 2026-05-18: memory pre-load deferred from module-import-time
2107
+ # to here. Soft-fail consumers read from _PRELOADED_CRITICAL / _PRELOADED_REFERENCE_KEYS.
2108
+ _threading.Thread(target=_preload_memories_bg, daemon=True, name="meshcode-preload-memories").start()
2126
2109
 
2127
2110
  # Heartbeat in daemon thread — independent of asyncio event loop.
2128
2111
  _heartbeat_stop.clear()
@@ -2726,21 +2709,43 @@ def meshcode_download_file(file_id: str) -> Dict[str, Any]:
2726
2709
  file_name = file_info.get("file_name", "download")
2727
2710
  signed_url = file_info.get("signed_url")
2728
2711
 
2729
- # Strategy 1: signed_url (no auth headers, no client service_role needed)
2712
+ sb_url = os.environ.get("SUPABASE_URL", be._sb_url if hasattr(be, '_sb_url') else "")
2713
+ anon_key = os.environ.get("SUPABASE_KEY", be._sb_key if hasattr(be, '_sb_key') else "")
2714
+ service_key = os.environ.get("MESHCODE_SUPABASE_SERVICE_KEY", "")
2715
+
2716
+ tried_labels: List[str] = []
2717
+ last_err: Optional[str] = None
2718
+
2719
+ # Strategy 1: signed_url (no auth headers, no client service_role needed).
2720
+ # Path B (235e85b6): server-side RPC mints the URL via backend GUCs and
2721
+ # returns it; client just GETs. Works for guest workspaces that
2722
+ # intentionally lack MESHCODE_SUPABASE_SERVICE_KEY in their .mcp.json.
2730
2723
  if signed_url:
2724
+ tried_labels.append("signed_url")
2731
2725
  try:
2732
2726
  with _req.urlopen(_req.Request(signed_url), timeout=30) as resp:
2733
2727
  content = resp.read()
2734
2728
  return _consume(content, mime_type, file_name)
2735
- except _uerr.HTTPError:
2736
- pass # fall through to legacy path
2737
- except Exception:
2738
- pass
2739
-
2740
- # Strategy 2: legacy service_role/anon/public on storage_path
2741
- sb_url = os.environ.get("SUPABASE_URL", be._sb_url if hasattr(be, '_sb_url') else "")
2742
- anon_key = os.environ.get("SUPABASE_KEY", be._sb_key if hasattr(be, '_sb_key') else "")
2743
- service_key = os.environ.get("MESHCODE_SUPABASE_SERVICE_KEY", "")
2729
+ except _uerr.HTTPError as e:
2730
+ last_err = f"HTTP {e.code} on signed_url: {e.reason}"
2731
+ except Exception as e:
2732
+ last_err = f"{type(e).__name__} on signed_url: {e}"
2733
+
2734
+ # Strategy 2 candidates: direct storage REST with available keys.
2735
+ # Guest workspaces (no MESHCODE_SUPABASE_SERVICE_KEY) cannot read a
2736
+ # private bucket via anon every Strategy-2 attempt will 400/403. Bail
2737
+ # early with a clear root-cause diagnosis instead of churning through
2738
+ # doomed attempts and surfacing a generic HTTP 400 to the user. Task
2739
+ # 8ecd56bb 2026-05-14: alexa + bob hit this path.
2740
+ if not signed_url and not service_key:
2741
+ return {
2742
+ "error": "server-side signed_url unavailable and this workspace has no service_role key",
2743
+ "error_code": "missing_server_guc",
2744
+ "tried": tried_labels,
2745
+ "storage_path": storage_path,
2746
+ "hint": "backend RPC mc_get_file_download did not return a signed_url. Likely the Supabase project is missing GUC `app.settings.service_role_key` + `app.settings.supabase_url` so the RPC body cannot mint a signed URL. Ask Samuel to apply both ALTER DATABASE settings on the meshcode Supabase project (sb_secret_... + https://gjinagyyjttyxnaoavnz.supabase.co), or distribute MESHCODE_SUPABASE_SERVICE_KEY into guest .mcp.json (security tradeoff — not recommended).",
2747
+ "diagnosis": "guest_workspace_pattern",
2748
+ }
2744
2749
 
2745
2750
  if not sb_url or not anon_key:
2746
2751
  return {"error": "storage not configured", "error_code": "config_error"}
@@ -2769,9 +2774,7 @@ def meshcode_download_file(file_id: str) -> Dict[str, Any]:
2769
2774
  attempts.append(("anon auth", auth_url, anon_key))
2770
2775
  attempts.append(("anon public", public_url, anon_key))
2771
2776
 
2772
- last_err = None
2773
2777
  content = None
2774
- tried_labels: List[str] = ["signed_url"] if signed_url else []
2775
2778
  for label, _url, _key in attempts:
2776
2779
  tried_labels.append(label)
2777
2780
  try:
@@ -2789,7 +2792,12 @@ def meshcode_download_file(file_id: str) -> Dict[str, Any]:
2789
2792
  "error_code": "download_error",
2790
2793
  "tried": tried_labels,
2791
2794
  "storage_path": storage_path,
2792
- "hint": "ask commander to apply backend GUC app.settings.service_role_key + supabase_url so the RPC can mint signed URLs" if not signed_url else None,
2795
+ "hint": (
2796
+ "every fallback failed. If 'signed_url' was tried and 400'd, the URL may be expired or "
2797
+ "signed against a stale key — re-request via meshcode_download_file. If signed_url was "
2798
+ "absent, the backend GUC `app.settings.service_role_key`/`app.settings.supabase_url` is "
2799
+ "missing on the Supabase project."
2800
+ ),
2793
2801
  }
2794
2802
 
2795
2803
  return _consume(content, mime_type, file_name)
@@ -4862,6 +4870,99 @@ def meshcode_recall_search(query: str) -> Dict[str, Any]:
4862
4870
  })
4863
4871
 
4864
4872
 
4873
+ # ----------------- EPISTEMIC V1 (decide / outcome / explain) -----------------
4874
+ # mig 305-308 SQL substrate; mig 313 memory_hints contradictions + ranking;
4875
+ # mig 314 public.* wrappers. Task b4befbfd.
4876
+
4877
+ @mcp.tool()
4878
+ @with_working_status
4879
+ def meshcode_decide(
4880
+ decision: str,
4881
+ alternatives: Optional[List[str]] = None,
4882
+ evidence_refs: Optional[List[Dict[str, Any]]] = None,
4883
+ tier: str = "episodic",
4884
+ ) -> Dict[str, Any]:
4885
+ """Log a non-trivial decision to mc_traces (architectural choice, risky migration,
4886
+ scope decision). Returns {trace_id}. Pair with meshcode_outcome later when the
4887
+ result becomes observable so the team can learn from outcomes.
4888
+
4889
+ Args:
4890
+ decision: What you decided (one sentence).
4891
+ alternatives: Other options you considered (list of strings).
4892
+ evidence_refs: Pointers to evidence (list of {type, id|key} dicts).
4893
+ type values: 'memory','msg','task','file','rpc'.
4894
+ tier: 'episodic' (default) | 'reference' | 'critical'.
4895
+ """
4896
+ if not decision or not decision.strip():
4897
+ return {"ok": False, "error": "decision required"}
4898
+ return be.sb_rpc("mc_decide", {
4899
+ "p_api_key": _get_api_key(),
4900
+ "p_project_id": _PROJECT_ID,
4901
+ "p_agent_name": AGENT_NAME,
4902
+ "p_decision": decision.strip(),
4903
+ "p_alternatives": json.dumps(alternatives or []),
4904
+ "p_evidence_refs": json.dumps(evidence_refs or []),
4905
+ "p_tier": tier,
4906
+ })
4907
+
4908
+
4909
+ @mcp.tool()
4910
+ @with_working_status
4911
+ def meshcode_outcome(
4912
+ trace_id: str,
4913
+ outcome: str,
4914
+ outcome_eval: str,
4915
+ ) -> Dict[str, Any]:
4916
+ """Record the observable outcome of a prior decision (logged via meshcode_decide).
4917
+ Call this when results become visible — even partial results help future-you and
4918
+ the team learn what worked.
4919
+
4920
+ Args:
4921
+ trace_id: UUID returned by meshcode_decide.
4922
+ outcome: Free-form description of what happened.
4923
+ outcome_eval: 'success' | 'failure' | 'partial' | 'unknown'.
4924
+ """
4925
+ if outcome_eval not in ("success", "failure", "partial", "unknown"):
4926
+ return {"ok": False, "error": "outcome_eval must be success|failure|partial|unknown"}
4927
+ return be.sb_rpc("mc_outcome", {
4928
+ "p_api_key": _get_api_key(),
4929
+ "p_project_id": _PROJECT_ID,
4930
+ "p_agent_name": AGENT_NAME,
4931
+ "p_trace_id": trace_id,
4932
+ "p_outcome": outcome,
4933
+ "p_outcome_eval": outcome_eval,
4934
+ })
4935
+
4936
+
4937
+ @mcp.tool()
4938
+ @with_working_status
4939
+ def meshcode_explain(
4940
+ memory_key: Optional[str] = None,
4941
+ decision_substring: Optional[str] = None,
4942
+ trace_limit: int = 20,
4943
+ ) -> Dict[str, Any]:
4944
+ """Surface recent traces + open contradictions tied to a memory key or decision
4945
+ substring. Use when you want to understand why a memory ranks the way it does,
4946
+ or recall the chain of reasoning around a past choice. Exactly one of
4947
+ (memory_key, decision_substring) is required.
4948
+
4949
+ Args:
4950
+ memory_key: Match traces whose evidence_refs reference this key.
4951
+ decision_substring: ILIKE match against the decision text.
4952
+ trace_limit: Cap on returned traces (1..200, default 20).
4953
+ """
4954
+ if not memory_key and not decision_substring:
4955
+ return {"ok": False, "error": "one of (memory_key, decision_substring) required"}
4956
+ return be.sb_rpc("mc_explain", {
4957
+ "p_api_key": _get_api_key(),
4958
+ "p_project_id": _PROJECT_ID,
4959
+ "p_agent_name": AGENT_NAME,
4960
+ "p_memory_key": memory_key,
4961
+ "p_decision_substring": decision_substring,
4962
+ "p_trace_limit": trace_limit,
4963
+ })
4964
+
4965
+
4865
4966
  # ----------------- BUG REPORTING -----------------
4866
4967
 
4867
4968
  @mcp.tool()