meshcode 2.11.47__tar.gz → 2.11.53__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 (244) hide show
  1. {meshcode-2.11.47 → meshcode-2.11.53}/PKG-INFO +1 -1
  2. {meshcode-2.11.47 → meshcode-2.11.53}/meshcode/__init__.py +1 -1
  3. {meshcode-2.11.47 → meshcode-2.11.53}/meshcode/comms_v4.py +20 -0
  4. {meshcode-2.11.47 → meshcode-2.11.53}/meshcode/meshcode_mcp/server.py +438 -2
  5. meshcode-2.11.53/meshcode/quickstart.py +231 -0
  6. {meshcode-2.11.47 → meshcode-2.11.53}/meshcode.egg-info/PKG-INFO +1 -1
  7. meshcode-2.11.53/meshcode.egg-info/SOURCES.txt +83 -0
  8. meshcode-2.11.53/meshcode.egg-info/top_level.txt +1 -0
  9. {meshcode-2.11.47 → meshcode-2.11.53}/pyproject.toml +1 -1
  10. meshcode-2.11.53/tests/test_autonomous_closegap_1.py +164 -0
  11. meshcode-2.11.53/tests/test_autonomous_closegap_2.py +210 -0
  12. meshcode-2.11.53/tests/test_autonomous_closegap_3.py +163 -0
  13. meshcode-2.11.53/tests/test_autonomous_prompt_inject.py +126 -0
  14. meshcode-2.11.47/meshcode/ascii_art.py +0 -638
  15. meshcode-2.11.47/meshcode/cli.py +0 -42
  16. meshcode-2.11.47/meshcode/compat.py +0 -174
  17. meshcode-2.11.47/meshcode/error_hints.py +0 -74
  18. meshcode-2.11.47/meshcode/exceptions.py +0 -52
  19. meshcode-2.11.47/meshcode/invites.py +0 -406
  20. meshcode-2.11.47/meshcode/launcher.py +0 -353
  21. meshcode-2.11.47/meshcode/launcher_install.py +0 -414
  22. meshcode-2.11.47/meshcode/meshcode_mcp/__init__.py +0 -22
  23. meshcode-2.11.47/meshcode/meshcode_mcp/__main__.py +0 -62
  24. meshcode-2.11.47/meshcode/meshcode_mcp/test_backend.py +0 -86
  25. meshcode-2.11.47/meshcode/meshcode_mcp/test_realtime.py +0 -95
  26. meshcode-2.11.47/meshcode/meshcode_mcp/test_server_wrapper.py +0 -117
  27. meshcode-2.11.47/meshcode/preferences.py +0 -260
  28. meshcode-2.11.47/meshcode/protocol_v2.py +0 -129
  29. meshcode-2.11.47/meshcode/quickstart.py +0 -148
  30. meshcode-2.11.47/meshcode/secrets.py +0 -365
  31. meshcode-2.11.47/meshcode/supervisor.py +0 -186
  32. meshcode-2.11.47/meshcode/upload.py +0 -125
  33. meshcode-2.11.47/meshcode-backend-wt/comms_v4.py +0 -1941
  34. meshcode-2.11.47/meshcode-backend-wt/meshcode/__init__.py +0 -82
  35. meshcode-2.11.47/meshcode-backend-wt/meshcode/comms_v4.py +0 -3563
  36. meshcode-2.11.47/meshcode-backend-wt/meshcode/meshcode_mcp/backend.py +0 -1261
  37. meshcode-2.11.47/meshcode-backend-wt/meshcode/meshcode_mcp/realtime.py +0 -460
  38. meshcode-2.11.47/meshcode-backend-wt/meshcode/meshcode_mcp/server.py +0 -4117
  39. meshcode-2.11.47/meshcode-backend-wt/meshcode/quickstart.py +0 -148
  40. meshcode-2.11.47/meshcode-backend-wt/meshcode/run_agent.py +0 -958
  41. meshcode-2.11.47/meshcode-backend-wt/meshcode/self_update.py +0 -345
  42. meshcode-2.11.47/meshcode-backend-wt/meshcode/setup_clients.py +0 -926
  43. meshcode-2.11.47/meshcode-backend-wt/scripts/sentinel.py +0 -257
  44. meshcode-2.11.47/meshcode-backend-wt/tests/test_rpc_migrations.py +0 -387
  45. meshcode-2.11.47/meshcode-noun-wt/build/lib/meshcode/__init__.py +0 -82
  46. meshcode-2.11.47/meshcode-noun-wt/build/lib/meshcode/ascii_art.py +0 -638
  47. meshcode-2.11.47/meshcode-noun-wt/build/lib/meshcode/cli.py +0 -42
  48. meshcode-2.11.47/meshcode-noun-wt/build/lib/meshcode/comms_v4.py +0 -3563
  49. meshcode-2.11.47/meshcode-noun-wt/build/lib/meshcode/compat.py +0 -174
  50. meshcode-2.11.47/meshcode-noun-wt/build/lib/meshcode/error_hints.py +0 -74
  51. meshcode-2.11.47/meshcode-noun-wt/build/lib/meshcode/exceptions.py +0 -52
  52. meshcode-2.11.47/meshcode-noun-wt/build/lib/meshcode/invites.py +0 -406
  53. meshcode-2.11.47/meshcode-noun-wt/build/lib/meshcode/launcher.py +0 -353
  54. meshcode-2.11.47/meshcode-noun-wt/build/lib/meshcode/launcher_install.py +0 -414
  55. meshcode-2.11.47/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/__init__.py +0 -22
  56. meshcode-2.11.47/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/__main__.py +0 -62
  57. meshcode-2.11.47/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/backend.py +0 -1261
  58. meshcode-2.11.47/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/realtime.py +0 -460
  59. meshcode-2.11.47/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/server.py +0 -4117
  60. meshcode-2.11.47/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_backend.py +0 -86
  61. meshcode-2.11.47/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_realtime.py +0 -95
  62. meshcode-2.11.47/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_server_wrapper.py +0 -117
  63. meshcode-2.11.47/meshcode-noun-wt/build/lib/meshcode/preferences.py +0 -260
  64. meshcode-2.11.47/meshcode-noun-wt/build/lib/meshcode/protocol_v2.py +0 -129
  65. meshcode-2.11.47/meshcode-noun-wt/build/lib/meshcode/quickstart.py +0 -148
  66. meshcode-2.11.47/meshcode-noun-wt/build/lib/meshcode/run_agent.py +0 -958
  67. meshcode-2.11.47/meshcode-noun-wt/build/lib/meshcode/secrets.py +0 -365
  68. meshcode-2.11.47/meshcode-noun-wt/build/lib/meshcode/self_update.py +0 -345
  69. meshcode-2.11.47/meshcode-noun-wt/build/lib/meshcode/setup_clients.py +0 -926
  70. meshcode-2.11.47/meshcode-noun-wt/build/lib/meshcode/supervisor.py +0 -186
  71. meshcode-2.11.47/meshcode-noun-wt/build/lib/meshcode/upload.py +0 -125
  72. meshcode-2.11.47/meshcode-noun-wt/comms_v4.py +0 -1941
  73. meshcode-2.11.47/meshcode-noun-wt/meshcode/__init__.py +0 -82
  74. meshcode-2.11.47/meshcode-noun-wt/meshcode/ascii_art.py +0 -638
  75. meshcode-2.11.47/meshcode-noun-wt/meshcode/cli.py +0 -42
  76. meshcode-2.11.47/meshcode-noun-wt/meshcode/comms_v4.py +0 -3563
  77. meshcode-2.11.47/meshcode-noun-wt/meshcode/compat.py +0 -174
  78. meshcode-2.11.47/meshcode-noun-wt/meshcode/error_hints.py +0 -74
  79. meshcode-2.11.47/meshcode-noun-wt/meshcode/exceptions.py +0 -52
  80. meshcode-2.11.47/meshcode-noun-wt/meshcode/invites.py +0 -406
  81. meshcode-2.11.47/meshcode-noun-wt/meshcode/launcher.py +0 -353
  82. meshcode-2.11.47/meshcode-noun-wt/meshcode/launcher_install.py +0 -414
  83. meshcode-2.11.47/meshcode-noun-wt/meshcode/meshcode_mcp/__init__.py +0 -22
  84. meshcode-2.11.47/meshcode-noun-wt/meshcode/meshcode_mcp/__main__.py +0 -62
  85. meshcode-2.11.47/meshcode-noun-wt/meshcode/meshcode_mcp/backend.py +0 -1261
  86. meshcode-2.11.47/meshcode-noun-wt/meshcode/meshcode_mcp/realtime.py +0 -460
  87. meshcode-2.11.47/meshcode-noun-wt/meshcode/meshcode_mcp/server.py +0 -4117
  88. meshcode-2.11.47/meshcode-noun-wt/meshcode/meshcode_mcp/test_backend.py +0 -86
  89. meshcode-2.11.47/meshcode-noun-wt/meshcode/meshcode_mcp/test_realtime.py +0 -95
  90. meshcode-2.11.47/meshcode-noun-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -117
  91. meshcode-2.11.47/meshcode-noun-wt/meshcode/preferences.py +0 -260
  92. meshcode-2.11.47/meshcode-noun-wt/meshcode/protocol_v2.py +0 -129
  93. meshcode-2.11.47/meshcode-noun-wt/meshcode/quickstart.py +0 -148
  94. meshcode-2.11.47/meshcode-noun-wt/meshcode/run_agent.py +0 -958
  95. meshcode-2.11.47/meshcode-noun-wt/meshcode/secrets.py +0 -365
  96. meshcode-2.11.47/meshcode-noun-wt/meshcode/self_update.py +0 -345
  97. meshcode-2.11.47/meshcode-noun-wt/meshcode/setup_clients.py +0 -926
  98. meshcode-2.11.47/meshcode-noun-wt/meshcode/supervisor.py +0 -186
  99. meshcode-2.11.47/meshcode-noun-wt/meshcode/upload.py +0 -125
  100. meshcode-2.11.47/meshcode-noun-wt/scripts/sentinel.py +0 -257
  101. meshcode-2.11.47/meshcode-noun-wt/tests/test_core.py +0 -216
  102. meshcode-2.11.47/meshcode-noun-wt/tests/test_cross_agent_messaging.py +0 -366
  103. meshcode-2.11.47/meshcode-noun-wt/tests/test_esc_deaf_state.py +0 -361
  104. meshcode-2.11.47/meshcode-noun-wt/tests/test_exceptions.py +0 -107
  105. meshcode-2.11.47/meshcode-noun-wt/tests/test_mark_read_batch.py +0 -200
  106. meshcode-2.11.47/meshcode-noun-wt/tests/test_migration_integrity.py +0 -176
  107. meshcode-2.11.47/meshcode-noun-wt/tests/test_realtime_event_freshness.py +0 -236
  108. meshcode-2.11.47/meshcode-noun-wt/tests/test_rls_cross_tenant.py +0 -255
  109. meshcode-2.11.47/meshcode-noun-wt/tests/test_rpc_migrations.py +0 -387
  110. meshcode-2.11.47/meshcode-noun-wt/tests/test_security_regressions.py +0 -171
  111. meshcode-2.11.47/meshcode-noun-wt/tests/test_sentinel.py +0 -148
  112. meshcode-2.11.47/meshcode-noun-wt/tests/test_status_enum_coverage.py +0 -231
  113. meshcode-2.11.47/meshcode-tasks-wt/comms_v4.py +0 -1941
  114. meshcode-2.11.47/meshcode-tasks-wt/meshcode/__init__.py +0 -82
  115. meshcode-2.11.47/meshcode-tasks-wt/meshcode/ascii_art.py +0 -638
  116. meshcode-2.11.47/meshcode-tasks-wt/meshcode/cli.py +0 -42
  117. meshcode-2.11.47/meshcode-tasks-wt/meshcode/comms_v4.py +0 -3563
  118. meshcode-2.11.47/meshcode-tasks-wt/meshcode/compat.py +0 -174
  119. meshcode-2.11.47/meshcode-tasks-wt/meshcode/error_hints.py +0 -74
  120. meshcode-2.11.47/meshcode-tasks-wt/meshcode/exceptions.py +0 -52
  121. meshcode-2.11.47/meshcode-tasks-wt/meshcode/invites.py +0 -406
  122. meshcode-2.11.47/meshcode-tasks-wt/meshcode/launcher.py +0 -353
  123. meshcode-2.11.47/meshcode-tasks-wt/meshcode/launcher_install.py +0 -414
  124. meshcode-2.11.47/meshcode-tasks-wt/meshcode/meshcode_mcp/__init__.py +0 -22
  125. meshcode-2.11.47/meshcode-tasks-wt/meshcode/meshcode_mcp/__main__.py +0 -62
  126. meshcode-2.11.47/meshcode-tasks-wt/meshcode/meshcode_mcp/backend.py +0 -1261
  127. meshcode-2.11.47/meshcode-tasks-wt/meshcode/meshcode_mcp/realtime.py +0 -460
  128. meshcode-2.11.47/meshcode-tasks-wt/meshcode/meshcode_mcp/server.py +0 -4117
  129. meshcode-2.11.47/meshcode-tasks-wt/meshcode/meshcode_mcp/test_backend.py +0 -86
  130. meshcode-2.11.47/meshcode-tasks-wt/meshcode/meshcode_mcp/test_realtime.py +0 -95
  131. meshcode-2.11.47/meshcode-tasks-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -117
  132. meshcode-2.11.47/meshcode-tasks-wt/meshcode/preferences.py +0 -260
  133. meshcode-2.11.47/meshcode-tasks-wt/meshcode/protocol_v2.py +0 -129
  134. meshcode-2.11.47/meshcode-tasks-wt/meshcode/quickstart.py +0 -148
  135. meshcode-2.11.47/meshcode-tasks-wt/meshcode/run_agent.py +0 -958
  136. meshcode-2.11.47/meshcode-tasks-wt/meshcode/secrets.py +0 -365
  137. meshcode-2.11.47/meshcode-tasks-wt/meshcode/self_update.py +0 -345
  138. meshcode-2.11.47/meshcode-tasks-wt/meshcode/setup_clients.py +0 -926
  139. meshcode-2.11.47/meshcode-tasks-wt/meshcode/supervisor.py +0 -186
  140. meshcode-2.11.47/meshcode-tasks-wt/meshcode/upload.py +0 -125
  141. meshcode-2.11.47/meshcode-tasks-wt/scripts/sentinel.py +0 -257
  142. meshcode-2.11.47/meshcode-tasks-wt/tests/test_core.py +0 -216
  143. meshcode-2.11.47/meshcode-tasks-wt/tests/test_cross_agent_messaging.py +0 -366
  144. meshcode-2.11.47/meshcode-tasks-wt/tests/test_esc_deaf_state.py +0 -361
  145. meshcode-2.11.47/meshcode-tasks-wt/tests/test_exceptions.py +0 -107
  146. meshcode-2.11.47/meshcode-tasks-wt/tests/test_mark_read_batch.py +0 -200
  147. meshcode-2.11.47/meshcode-tasks-wt/tests/test_migration_integrity.py +0 -176
  148. meshcode-2.11.47/meshcode-tasks-wt/tests/test_realtime_event_freshness.py +0 -236
  149. meshcode-2.11.47/meshcode-tasks-wt/tests/test_rls_cross_tenant.py +0 -255
  150. meshcode-2.11.47/meshcode-tasks-wt/tests/test_rpc_migrations.py +0 -387
  151. meshcode-2.11.47/meshcode-tasks-wt/tests/test_security_regressions.py +0 -171
  152. meshcode-2.11.47/meshcode-tasks-wt/tests/test_sentinel.py +0 -148
  153. meshcode-2.11.47/meshcode-tasks-wt/tests/test_status_enum_coverage.py +0 -231
  154. meshcode-2.11.47/meshcode.egg-info/PKG-INFO 2 +0 -452
  155. meshcode-2.11.47/meshcode.egg-info/SOURCES 2.txt +0 -229
  156. meshcode-2.11.47/meshcode.egg-info/SOURCES.txt +0 -235
  157. meshcode-2.11.47/meshcode.egg-info/dependency_links 2.txt +0 -1
  158. meshcode-2.11.47/meshcode.egg-info/entry_points 2.txt +0 -3
  159. meshcode-2.11.47/meshcode.egg-info/requires 2.txt +0 -13
  160. meshcode-2.11.47/meshcode.egg-info/top_level 2.txt +0 -4
  161. meshcode-2.11.47/meshcode.egg-info/top_level.txt +0 -4
  162. meshcode-2.11.47/tests/test_core.py +0 -216
  163. meshcode-2.11.47/tests/test_cross_agent_messaging.py +0 -366
  164. meshcode-2.11.47/tests/test_esc_deaf_state.py +0 -361
  165. meshcode-2.11.47/tests/test_exceptions.py +0 -107
  166. meshcode-2.11.47/tests/test_mark_read_batch.py +0 -200
  167. meshcode-2.11.47/tests/test_migration_integrity.py +0 -176
  168. meshcode-2.11.47/tests/test_realtime_event_freshness.py +0 -236
  169. meshcode-2.11.47/tests/test_rls_cross_tenant.py +0 -255
  170. meshcode-2.11.47/tests/test_security_regressions.py +0 -171
  171. meshcode-2.11.47/tests/test_sentinel.py +0 -148
  172. meshcode-2.11.47/tests/test_status_enum_coverage.py +0 -231
  173. {meshcode-2.11.47 → meshcode-2.11.53}/README.md +0 -0
  174. {meshcode-2.11.47 → meshcode-2.11.53}/meshcode/__main__.py +0 -0
  175. {meshcode-2.11.47 → meshcode-2.11.53}/meshcode/_stop_hook_template.py +0 -0
  176. {meshcode-2.11.47/meshcode-backend-wt → meshcode-2.11.53}/meshcode/ascii_art.py +0 -0
  177. {meshcode-2.11.47 → meshcode-2.11.53}/meshcode/atomic_push.py +0 -0
  178. {meshcode-2.11.47/meshcode-backend-wt → meshcode-2.11.53}/meshcode/cli.py +0 -0
  179. {meshcode-2.11.47/meshcode-backend-wt → meshcode-2.11.53}/meshcode/compat.py +0 -0
  180. {meshcode-2.11.47 → meshcode-2.11.53}/meshcode/daemon.py +0 -0
  181. {meshcode-2.11.47 → meshcode-2.11.53}/meshcode/date_parse.py +0 -0
  182. {meshcode-2.11.47 → meshcode-2.11.53}/meshcode/doctor.py +0 -0
  183. {meshcode-2.11.47/meshcode-backend-wt → meshcode-2.11.53}/meshcode/error_hints.py +0 -0
  184. {meshcode-2.11.47/meshcode-backend-wt → meshcode-2.11.53}/meshcode/exceptions.py +0 -0
  185. {meshcode-2.11.47/meshcode-backend-wt → meshcode-2.11.53}/meshcode/invites.py +0 -0
  186. {meshcode-2.11.47/meshcode-backend-wt → meshcode-2.11.53}/meshcode/launcher.py +0 -0
  187. {meshcode-2.11.47/meshcode-backend-wt → meshcode-2.11.53}/meshcode/launcher_install.py +0 -0
  188. {meshcode-2.11.47/meshcode-backend-wt → meshcode-2.11.53}/meshcode/meshcode_mcp/__init__.py +0 -0
  189. {meshcode-2.11.47/meshcode-backend-wt → meshcode-2.11.53}/meshcode/meshcode_mcp/__main__.py +0 -0
  190. {meshcode-2.11.47 → meshcode-2.11.53}/meshcode/meshcode_mcp/backend.py +0 -0
  191. {meshcode-2.11.47 → meshcode-2.11.53}/meshcode/meshcode_mcp/realtime.py +0 -0
  192. {meshcode-2.11.47 → meshcode-2.11.53}/meshcode/meshcode_mcp/sleep_signals.py +0 -0
  193. {meshcode-2.11.47/meshcode-backend-wt → meshcode-2.11.53}/meshcode/meshcode_mcp/test_backend.py +0 -0
  194. {meshcode-2.11.47 → meshcode-2.11.53}/meshcode/meshcode_mcp/test_boot_timing.py +0 -0
  195. {meshcode-2.11.47 → meshcode-2.11.53}/meshcode/meshcode_mcp/test_install_guard.py +0 -0
  196. {meshcode-2.11.47 → meshcode-2.11.53}/meshcode/meshcode_mcp/test_prefs_claude_version.py +0 -0
  197. {meshcode-2.11.47/meshcode-backend-wt → meshcode-2.11.53}/meshcode/meshcode_mcp/test_realtime.py +0 -0
  198. {meshcode-2.11.47/meshcode-backend-wt → meshcode-2.11.53}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  199. {meshcode-2.11.47/meshcode-backend-wt → meshcode-2.11.53}/meshcode/preferences.py +0 -0
  200. {meshcode-2.11.47 → meshcode-2.11.53}/meshcode/protocol_handler.py +0 -0
  201. {meshcode-2.11.47/meshcode-backend-wt → meshcode-2.11.53}/meshcode/protocol_v2.py +0 -0
  202. {meshcode-2.11.47 → meshcode-2.11.53}/meshcode/rpc_allowlist.py +0 -0
  203. {meshcode-2.11.47 → meshcode-2.11.53}/meshcode/run_agent.py +0 -0
  204. {meshcode-2.11.47 → meshcode-2.11.53}/meshcode/scripts/check_secrets.py +0 -0
  205. {meshcode-2.11.47 → meshcode-2.11.53}/meshcode/scripts/race_rate_harness.py +0 -0
  206. {meshcode-2.11.47/meshcode-backend-wt → meshcode-2.11.53}/meshcode/secrets.py +0 -0
  207. {meshcode-2.11.47 → meshcode-2.11.53}/meshcode/self_update.py +0 -0
  208. {meshcode-2.11.47 → meshcode-2.11.53}/meshcode/setup_clients.py +0 -0
  209. {meshcode-2.11.47/meshcode-backend-wt → meshcode-2.11.53}/meshcode/supervisor.py +0 -0
  210. {meshcode-2.11.47/meshcode-backend-wt → meshcode-2.11.53}/meshcode/upload.py +0 -0
  211. {meshcode-2.11.47 → meshcode-2.11.53}/meshcode.egg-info/dependency_links.txt +0 -0
  212. {meshcode-2.11.47 → meshcode-2.11.53}/meshcode.egg-info/entry_points.txt +0 -0
  213. {meshcode-2.11.47 → meshcode-2.11.53}/meshcode.egg-info/requires.txt +0 -0
  214. {meshcode-2.11.47 → meshcode-2.11.53}/setup.cfg +0 -0
  215. {meshcode-2.11.47 → meshcode-2.11.53}/tests/test_auto_update_hardening.py +0 -0
  216. {meshcode-2.11.47 → meshcode-2.11.53}/tests/test_boot_bug_regression.py +0 -0
  217. {meshcode-2.11.47 → meshcode-2.11.53}/tests/test_color_truecolor.py +0 -0
  218. {meshcode-2.11.47/meshcode-backend-wt → meshcode-2.11.53}/tests/test_core.py +0 -0
  219. {meshcode-2.11.47/meshcode-backend-wt → meshcode-2.11.53}/tests/test_cross_agent_messaging.py +0 -0
  220. {meshcode-2.11.47 → meshcode-2.11.53}/tests/test_date_parse.py +0 -0
  221. {meshcode-2.11.47 → meshcode-2.11.53}/tests/test_doctor.py +0 -0
  222. {meshcode-2.11.47 → meshcode-2.11.53}/tests/test_epistemic_v1_python_sdk.py +0 -0
  223. {meshcode-2.11.47 → meshcode-2.11.53}/tests/test_epistemic_v1_stop_conditions.py +0 -0
  224. {meshcode-2.11.47/meshcode-backend-wt → meshcode-2.11.53}/tests/test_esc_deaf_state.py +0 -0
  225. {meshcode-2.11.47/meshcode-backend-wt → meshcode-2.11.53}/tests/test_exceptions.py +0 -0
  226. {meshcode-2.11.47 → meshcode-2.11.53}/tests/test_init_device_code.py +0 -0
  227. {meshcode-2.11.47 → meshcode-2.11.53}/tests/test_install_guard.py +0 -0
  228. {meshcode-2.11.47 → meshcode-2.11.53}/tests/test_lease_sigterm_release.py +0 -0
  229. {meshcode-2.11.47/meshcode-backend-wt → meshcode-2.11.53}/tests/test_mark_read_batch.py +0 -0
  230. {meshcode-2.11.47/meshcode-backend-wt → meshcode-2.11.53}/tests/test_migration_integrity.py +0 -0
  231. {meshcode-2.11.47/meshcode-backend-wt → meshcode-2.11.53}/tests/test_realtime_event_freshness.py +0 -0
  232. {meshcode-2.11.47/meshcode-backend-wt → meshcode-2.11.53}/tests/test_rls_cross_tenant.py +0 -0
  233. {meshcode-2.11.47 → meshcode-2.11.53}/tests/test_rpc_grants.py +0 -0
  234. {meshcode-2.11.47 → meshcode-2.11.53}/tests/test_rpc_migrations.py +0 -0
  235. {meshcode-2.11.47 → meshcode-2.11.53}/tests/test_run_agent_dry_run.py +0 -0
  236. {meshcode-2.11.47 → meshcode-2.11.53}/tests/test_run_agent_no_server_import.py +0 -0
  237. {meshcode-2.11.47/meshcode-backend-wt → meshcode-2.11.53}/tests/test_security_regressions.py +0 -0
  238. {meshcode-2.11.47 → meshcode-2.11.53}/tests/test_self_update_user_site.py +0 -0
  239. {meshcode-2.11.47/meshcode-backend-wt → meshcode-2.11.53}/tests/test_sentinel.py +0 -0
  240. {meshcode-2.11.47 → meshcode-2.11.53}/tests/test_setup_path.py +0 -0
  241. {meshcode-2.11.47 → meshcode-2.11.53}/tests/test_sleep_signals.py +0 -0
  242. {meshcode-2.11.47/meshcode-backend-wt → meshcode-2.11.53}/tests/test_status_enum_coverage.py +0 -0
  243. {meshcode-2.11.47 → meshcode-2.11.53}/tests/test_stay_on_loop_hook.py +0 -0
  244. {meshcode-2.11.47 → meshcode-2.11.53}/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.47
3
+ Version: 2.11.53
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.47"
2
+ __version__ = "2.11.53"
3
3
 
4
4
  # Exception hierarchy — eagerly imported (lightweight, no deps)
5
5
  from meshcode.exceptions import ( # noqa: F401
@@ -2737,6 +2737,26 @@ if __name__ == "__main__":
2737
2737
  except Exception:
2738
2738
  _v = "unknown"
2739
2739
  print(f"meshcode {_v}")
2740
+ # Final-polish (msg 8068cca1): chief@mesh-dev review noted users
2741
+ # pip-upgrade but don't restart agents → task-pull-always-on never
2742
+ # activates → "meshcode feels broken" perception persists. This
2743
+ # one-liner runs every `meshcode --version` (or `meshcode version`)
2744
+ # so it shows up after upgrade flows + doctor + manual checks.
2745
+ try:
2746
+ _parts = _v.split(".")
2747
+ _is_2_11_46_or_newer = (
2748
+ len(_parts) >= 3
2749
+ and _parts[0] == "2" and _parts[1] == "11"
2750
+ and _parts[2].isdigit() and int(_parts[2]) >= 46
2751
+ )
2752
+ if _is_2_11_46_or_newer:
2753
+ print()
2754
+ print(f" ⚡ NEW in 2.11.46+: agents auto-claim assigned tasks + show 'working on X' status.")
2755
+ print(f" If you have running agent sessions on an older version, restart them:")
2756
+ print(f" meshcode wake-all <project> # copy-paste relaunch list")
2757
+ print(f" meshcode run <agent> --autonomous # per-agent direct launch")
2758
+ except Exception:
2759
+ pass
2740
2760
  sys.exit(0)
2741
2761
 
2742
2762
  # Per-subcommand help: meshcode <cmd> --help / -h / help
@@ -16,7 +16,7 @@ import hashlib as _hashlib
16
16
  import traceback as _traceback
17
17
  from collections import deque
18
18
  from contextlib import asynccontextmanager
19
- from typing import Any, Dict, List, Optional, Union
19
+ from typing import Any, Dict, List, Optional, Set, Union
20
20
 
21
21
  from meshcode import __version__ as _SDK_VERSION
22
22
 
@@ -1707,6 +1707,11 @@ Then: set_status(online,ready) → check() → tasks() → auto_wake() → statu
1707
1707
  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.
1708
1708
 
1709
1709
  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.
1710
+
1711
+ ESCALATION RULES:
1712
+ - MUST escalate to human: destructive ops (drop/delete/rm -rf), irreversible changes (force-push main, mig without rollback), product-direction decisions (what feature next), legal/compliance, security (auth/secrets/PII), spend > $50 USD.
1713
+ - MUST NOT escalate: code style, implementation approach, naming, test strategy, migration ordering, refactor scope, library choice, which agent does what.
1714
+ - GREY ZONE: scope changes, deadline trade-offs, cross-mesh assignments → try meshcode_call(to="mesh-commander", function="advise") first; escalate to human only if commander offline or responds "escalate".
1710
1715
  """
1711
1716
  # Inject commander protocol if this agent is a leader
1712
1717
  is_leader = _is_leader_agent()
@@ -1726,6 +1731,22 @@ COMMANDER PROTOCOL (you are the team lead):
1726
1731
  - PROACTIVE MAINTENANCE: call meshcode_auto_wake() on session start and after
1727
1732
  completing sprints. It scans meshwork health (stale agents, task backlog,
1728
1733
  empty memories). Turn suggestions into real tasks — quality over quantity.
1734
+
1735
+ ADVISE HANDLER (mesh-first decision protocol):
1736
+ When an agent calls meshcode_call(to=you, function="advise", args={question, context}), you are the first line of decision support. Respond with one of:
1737
+ - {"action":"decide","answer":"<your recommendation>","confidence":"high|medium"} — you have enough context to advise. The caller executes your recommendation.
1738
+ - {"action":"escalate","reason":"<why>"} — the question requires human judgment (falls under MUST-escalate criteria: destructive, irreversible, product-direction, legal, security, spend >$50). The caller messages the human.
1739
+ - {"action":"defer","reason":"<why>","suggest":"<alternative agent or resource>"} — you lack context but know who does.
1740
+ Use meshcode_recall_search and meshcode_tasks to inform your answer before responding. Bias toward "decide" — only escalate when the criteria genuinely apply.
1741
+ """
1742
+ else:
1743
+ base += """
1744
+ AUTONOMOUS MODE — these rules activate when you_are_autonomous=true in your most recent boot/wait/check response:
1745
+ - Cadence: decide → execute → report. No empty acks, no "waiting for approval" for things you can resolve.
1746
+ - Pre-decision context: if a decision depends on prior context, run meshcode_recall_search(keywords) FIRST before choosing.
1747
+ - Mesh-first: before messaging a human (sammybenu/ian/fis), try meshcode_call(to="mesh-commander", function="advise", args={question: "...", context: "..."}) first. Only escalate to human if commander is offline or responds "escalate".
1748
+ - Bias toward action: when you have enough information to decide, decide. Ship and report the outcome — don't ask permission.
1749
+ - Self-awareness: after completing a task, call meshcode_recall_search for related memories and update or consolidate stale ones.
1729
1750
  """
1730
1751
  return base
1731
1752
 
@@ -2787,6 +2808,18 @@ def meshcode_send(to: Union[str, List[str]], message: Any, in_reply_to: Optional
2787
2808
  if _payload_len > 8000:
2788
2809
  return {"error": f"message too large ({_payload_len} chars). Use meshcode_task_create for long content. Messages must be structured JSON <8000 chars."}
2789
2810
 
2811
+ # CLOSEGAP-3 (task 69dc5a62): ship-detection auto-checkpoint. If the
2812
+ # outgoing payload is a ship-report (explicit type or canonical
2813
+ # commits+task+version shape) AND references a task currently
2814
+ # claimed/in_progress by this agent, complete it. Runs BEFORE the
2815
+ # actual send so the dashboard reflects the new state at roughly
2816
+ # the same wall-clock as the report drops in the inbox.
2817
+ # Baseline (NOT autonomous-gated). Best-effort; audit via mc_activity.
2818
+ try:
2819
+ _try_auto_complete_on_ship(payload)
2820
+ except Exception:
2821
+ pass
2822
+
2790
2823
  # Typed-catalog soft validation (BE-S4.2.sdk / mig 233): if caller
2791
2824
  # passes type=, fetch the schema and check required fields. NEVER
2792
2825
  # refuses the send — best-effort warn-only. Skips when no type given,
@@ -3441,6 +3474,363 @@ def _try_auto_claim_task() -> Optional[Dict[str, str]]:
3441
3474
  return None
3442
3475
 
3443
3476
 
3477
+ # AUTONOMOUS-CLOSEGAP-1 (task beadc9e6, Samuel directive 2026-05-26T05:15Z):
3478
+ # heuristic auto-claim for OPEN tasks where assignee is 'auto' or unset,
3479
+ # matched against the local agent's role keywords + registered skills.
3480
+ # Polite: load-gated, opt-in cross-mesh, logged to mc_activity.
3481
+ _REGISTERED_SKILLS: List[str] = [] # populated by meshcode_register_skills
3482
+
3483
+ _HEURISTIC_TOKEN_SPLIT = "[\\s,;/|\\-_().\\[\\]{}]+"
3484
+ _HEURISTIC_STOPWORDS = {
3485
+ "the", "and", "for", "with", "from", "this", "that", "have", "has",
3486
+ "are", "was", "were", "but", "any", "all", "you", "your", "our",
3487
+ "agent", "agents", "task", "tasks", "via", "using", "use", "use",
3488
+ "via", "into", "onto", "out", "off", "per", "general", "purpose",
3489
+ "ai", "assistant", "mcp", "connected", "mc",
3490
+ }
3491
+
3492
+
3493
+ def _agent_keyword_set() -> Set[str]:
3494
+ """Lowercased keyword set built from agent role + name + registered skills.
3495
+ Drops short/stopword tokens. Used by the auto-claim heuristic."""
3496
+ import re as _re
3497
+ tokens: Set[str] = set()
3498
+ sources = [AGENT_ROLE or "", AGENT_NAME or ""]
3499
+ sources.extend(_REGISTERED_SKILLS or [])
3500
+ for src in sources:
3501
+ for tok in _re.split(_HEURISTIC_TOKEN_SPLIT, str(src).lower()):
3502
+ if len(tok) >= 3 and tok not in _HEURISTIC_STOPWORDS:
3503
+ tokens.add(tok)
3504
+ return tokens
3505
+
3506
+
3507
+ def _heuristic_match_score(task: Dict[str, Any], agent_tokens: Set[str]) -> int:
3508
+ """Count token-overlap hits across task title + first 200 chars of description.
3509
+ Each registered-skill hit counts double (skill > role keyword)."""
3510
+ if not agent_tokens:
3511
+ return 0
3512
+ blob = (str(task.get("title") or "") + " " + str(task.get("description") or "")[:200]).lower()
3513
+ role_hits = sum(1 for tok in agent_tokens if tok in blob)
3514
+ skill_bonus = sum(1 for s in (_REGISTERED_SKILLS or []) if s and s.lower() in blob)
3515
+ return role_hits + skill_bonus
3516
+
3517
+
3518
+ def _try_auto_claim_open_by_heuristic(max_load: int = 2) -> Optional[Dict[str, Any]]:
3519
+ """AUTONOMOUS-CLOSEGAP-1: claim one open unowned/auto task whose
3520
+ title + description-head overlaps with this agent's role/skills.
3521
+
3522
+ Polite contract:
3523
+ - skip if agent already holds ``max_load`` or more active claims
3524
+ (configurable via MESHCODE_AUTO_CLAIM_MAX_LOAD)
3525
+ - skip cross-mesh tasks unless MESHCODE_AUTO_CLAIM_CROSS_MESH=1
3526
+ - audit reason logged to mc_activity ("auto_claim_heuristic" event)
3527
+
3528
+ Returns the claimed task summary or None.
3529
+ """
3530
+ try:
3531
+ api_key = _get_api_key()
3532
+ if not api_key:
3533
+ return None
3534
+ # Single status_filter=None call covers BOTH the load-gate count
3535
+ # (claimed/in_progress rows owned by me) and the candidate filter
3536
+ # (open rows with assignee='auto'/unset). Backend caches.
3537
+ result = be.task_list(api_key, _PROJECT_ID, AGENT_NAME, status_filter=None)
3538
+ if not isinstance(result, dict) or not result.get("ok"):
3539
+ return None
3540
+ tasks = result.get("tasks", []) or []
3541
+ # Load gate — count claims I already hold that aren't done/canceled.
3542
+ _ACTIVE = {"claimed", "in_progress", "in_review"}
3543
+ my_active = sum(
3544
+ 1 for t in tasks
3545
+ if t.get("claimed_by") == AGENT_NAME and t.get("status") in _ACTIVE
3546
+ )
3547
+ if my_active >= max_load:
3548
+ log.debug(f"[meshcode] heuristic auto-claim skipped: load={my_active} >= max={max_load}")
3549
+ return None
3550
+ # Polite cross-mesh gate
3551
+ _cross_mesh = os.environ.get("MESHCODE_AUTO_CLAIM_CROSS_MESH", "").strip().lower() in ("1", "true", "yes", "on")
3552
+ candidates: List[Dict[str, Any]] = []
3553
+ for t in tasks:
3554
+ if t.get("status") != "open":
3555
+ continue
3556
+ if t.get("claimed_by"):
3557
+ continue
3558
+ assignee = (t.get("assignee") or "").strip().lower()
3559
+ # 'auto' OR empty (unset) — '*' wildcard is handled by _try_auto_claim_task.
3560
+ if assignee not in ("", "auto"):
3561
+ continue
3562
+ if not _cross_mesh:
3563
+ src_proj = t.get("source_project_id")
3564
+ if src_proj and _PROJECT_ID and src_proj != _PROJECT_ID:
3565
+ continue
3566
+ candidates.append(t)
3567
+ if not candidates:
3568
+ return None
3569
+ agent_tokens = _agent_keyword_set()
3570
+ scored: List[tuple] = []
3571
+ for t in candidates:
3572
+ score = _heuristic_match_score(t, agent_tokens)
3573
+ if score > 0:
3574
+ scored.append((score, t))
3575
+ if not scored:
3576
+ return None
3577
+ _PRIO = {"urgent": 0, "high": 1, "normal": 2, "low": 3}
3578
+ scored.sort(key=lambda st: (-st[0], _PRIO.get(st[1].get("priority", "normal"), 2), st[1].get("created_at", "")))
3579
+ score, target = scored[0]
3580
+ claim_result = be.sb_rpc("mc_task_claim", {
3581
+ "p_api_key": api_key,
3582
+ "p_task_id": target["id"],
3583
+ })
3584
+ if not (isinstance(claim_result, dict) and claim_result.get("ok")):
3585
+ log.debug(f"[meshcode] heuristic claim lost race on {target['id'][:8]}: {claim_result}")
3586
+ return None
3587
+ log.info(f"[meshcode] heuristic auto-claim score={score}: {target['title'][:60]}")
3588
+ try:
3589
+ _log_activity_bg(
3590
+ "auto_claim_heuristic",
3591
+ f"{AGENT_NAME} claimed '{str(target.get('title'))[:80]}' (score={score}, assignee={(target.get('assignee') or 'unset')})",
3592
+ )
3593
+ except Exception:
3594
+ pass
3595
+ return {
3596
+ "id": target["id"][:8],
3597
+ "title": str(target.get("title") or "")[:80],
3598
+ "priority": target.get("priority", "normal"),
3599
+ "match_score": score,
3600
+ "reason": "role+skill keyword overlap",
3601
+ }
3602
+ except Exception as e:
3603
+ log.debug(f"[meshcode] heuristic auto-claim failed: {e}")
3604
+ return None
3605
+
3606
+
3607
+ # CLOSEGAP-2 (task 01bfe6c4): when an agent calls
3608
+ # `meshcode_set_status(working, "<task-hint>")`, surface the matching
3609
+ # claimed task UUID so we can auto-flip it to in_progress via the
3610
+ # existing mc_task_start RPC (mig 349). Eliminates the manual second
3611
+ # call. Baseline (NOT autonomous-gated per Samuel reframe ce506e0d).
3612
+ def _match_claimed_task_for_set_status(task_hint: str) -> Optional[str]:
3613
+ """Given the free-form `task` arg of `meshcode_set_status(working, ...)`,
3614
+ return the UUID of a single claimed-by-me task that matches, or None.
3615
+
3616
+ Matching rules (deterministic — no guess on ambiguity):
3617
+ 1. If hint contains "task <hex>" (the prefix that
3618
+ `meshcode_task_claim` writes), extract and match by id-prefix on
3619
+ my claimed list. Exactly 1 match → return its UUID.
3620
+ 2. Else, fuzzy by title substring (either direction). Exactly 1
3621
+ match → return its UUID.
3622
+ 3. 0 matches OR >1 matches → return None (don't guess).
3623
+ """
3624
+ if not task_hint or not task_hint.strip():
3625
+ return None
3626
+ try:
3627
+ import re as _re
3628
+ api_key = _get_api_key()
3629
+ if not api_key:
3630
+ return None
3631
+ listing = be.task_list(api_key, _PROJECT_ID, AGENT_NAME, status_filter=None)
3632
+ if not isinstance(listing, dict) or not listing.get("ok"):
3633
+ return None
3634
+ mine_claimed = [
3635
+ t for t in (listing.get("tasks") or [])
3636
+ if t.get("claimed_by") == AGENT_NAME and t.get("status") == "claimed"
3637
+ ]
3638
+ if not mine_claimed:
3639
+ return None
3640
+ # Rule 1: explicit "task <hex>" hint (e.g. "task 01bfe6c4: ...")
3641
+ m = _re.search(r"task\s+([0-9a-f]{4,8}(?:-[0-9a-f]+)?)", task_hint, _re.IGNORECASE)
3642
+ if m:
3643
+ prefix = m.group(1).lower()
3644
+ id_matches = [t["id"] for t in mine_claimed if str(t.get("id") or "").lower().startswith(prefix)]
3645
+ if len(id_matches) == 1:
3646
+ return id_matches[0]
3647
+ # Multiple/zero by prefix → fall through to title matching;
3648
+ # don't return None yet, the title may still disambiguate.
3649
+ # Rule 2: title substring (either direction). 5-char floor avoids
3650
+ # accidentally matching on tiny shared substrings ("the", "task").
3651
+ hint_low = task_hint.lower()
3652
+ title_matches: List[str] = []
3653
+ for t in mine_claimed:
3654
+ title_low = str(t.get("title") or "").lower()
3655
+ if len(title_low) < 5:
3656
+ continue
3657
+ if title_low in hint_low or (len(hint_low) >= 5 and hint_low in title_low):
3658
+ title_matches.append(t["id"])
3659
+ if len(title_matches) == 1:
3660
+ return title_matches[0]
3661
+ return None
3662
+ except Exception:
3663
+ return None
3664
+
3665
+
3666
+ # CLOSEGAP-3 (task 69dc5a62, Samuel reframe ce506e0d): when an agent
3667
+ # sends a ship-report message that references a task it currently
3668
+ # holds in `claimed` or `in_progress`, auto-complete that task with a
3669
+ # coherent summary built from the payload. Same applies on
3670
+ # set_status(status='done', ...) — completes the single in_progress
3671
+ # claim with a status-driven summary. Baseline (NOT autonomous-gated).
3672
+ def _ship_marker_in_payload(payload: Dict[str, Any]) -> bool:
3673
+ """Detect a ship-report payload. Tight rules to avoid false positives:
3674
+ - explicit type marker: payload.type in {ship, shipped, ship_report}
3675
+ - OR canonical ship-JSON shape: has 'task' AND ('commits' OR 'version')
3676
+ """
3677
+ if not isinstance(payload, dict):
3678
+ return False
3679
+ ptype = str(payload.get("type") or "").strip().lower()
3680
+ if ptype in ("ship", "shipped", "ship_report"):
3681
+ return True
3682
+ has_task = bool(payload.get("task"))
3683
+ has_commits = bool(payload.get("commits"))
3684
+ has_version = bool(payload.get("version"))
3685
+ return has_task and (has_commits or has_version)
3686
+
3687
+
3688
+ def _extract_task_ref_from_payload(payload: Dict[str, Any]) -> Optional[str]:
3689
+ """Extract an 8+ hex-char task id prefix from a ship-report payload.
3690
+ Looks in explicit fields first (task_id, task, id) then falls back
3691
+ to the textual blob. Returns lowercased prefix or None.
3692
+ """
3693
+ import re as _re
3694
+ if not isinstance(payload, dict):
3695
+ return None
3696
+ for key in ("task_id", "task", "id"):
3697
+ val = payload.get(key)
3698
+ if isinstance(val, str):
3699
+ m = _re.search(r"([0-9a-f]{8}(?:-[0-9a-f]+)*)", val, _re.IGNORECASE)
3700
+ if m:
3701
+ return m.group(1).lower()
3702
+ # Last resort: scan a textual blob
3703
+ blob_parts = []
3704
+ for k in ("text", "commits", "summary"):
3705
+ v = payload.get(k)
3706
+ if isinstance(v, (str, dict, list)):
3707
+ blob_parts.append(str(v))
3708
+ blob = " ".join(blob_parts)
3709
+ m = _re.search(r"\b([0-9a-f]{8})\b", blob, _re.IGNORECASE)
3710
+ if m:
3711
+ return m.group(1).lower()
3712
+ return None
3713
+
3714
+
3715
+ def _build_ship_summary(payload: Dict[str, Any], duration_note: str = "") -> str:
3716
+ """Stringify a ship-report payload into a task-complete summary."""
3717
+ parts = [f"AUTO-CHECKPOINT (CLOSEGAP-3): ship detected via meshcode_send{duration_note}."]
3718
+ for key in ("version", "behavior"):
3719
+ val = payload.get(key)
3720
+ if val:
3721
+ parts.append(f"{key}={val}")
3722
+ commits = payload.get("commits")
3723
+ if isinstance(commits, dict) and commits:
3724
+ parts.append("commits=" + " ".join(f"{k}:{v}" for k, v in commits.items()))
3725
+ elif commits:
3726
+ parts.append(f"commits={commits}")
3727
+ files = payload.get("files")
3728
+ if files:
3729
+ parts.append(f"files={files}")
3730
+ summary = " | ".join(str(p) for p in parts)
3731
+ return summary[:1500]
3732
+
3733
+
3734
+ def _try_auto_complete_on_ship(payload: Dict[str, Any]) -> Optional[Dict[str, Any]]:
3735
+ """CLOSEGAP-3: if `payload` is a ship-report referencing a task id
3736
+ that this agent currently holds in claimed/in_progress, call
3737
+ mc_task_complete with a built summary. Best-effort.
3738
+ """
3739
+ try:
3740
+ if not _ship_marker_in_payload(payload):
3741
+ return None
3742
+ ref = _extract_task_ref_from_payload(payload)
3743
+ if not ref:
3744
+ return None
3745
+ api_key = _get_api_key()
3746
+ if not api_key:
3747
+ return None
3748
+ listing = be.task_list(api_key, _PROJECT_ID, AGENT_NAME, status_filter=None)
3749
+ if not isinstance(listing, dict) or not listing.get("ok"):
3750
+ return None
3751
+ matches = [
3752
+ t for t in (listing.get("tasks") or [])
3753
+ if t.get("claimed_by") == AGENT_NAME
3754
+ and t.get("status") in ("claimed", "in_progress")
3755
+ and str(t.get("id") or "").lower().startswith(ref)
3756
+ ]
3757
+ if len(matches) != 1:
3758
+ return None
3759
+ target = matches[0]
3760
+ # Duration from last_progress_at (mig 349 stamps this on in_progress flip)
3761
+ duration_note = ""
3762
+ started_at = target.get("last_progress_at") or target.get("claimed_at")
3763
+ if started_at and target.get("status") == "in_progress":
3764
+ try:
3765
+ from datetime import datetime, timezone
3766
+ _start_dt = datetime.fromisoformat(str(started_at).replace("Z", "+00:00"))
3767
+ _delta = datetime.now(timezone.utc) - _start_dt
3768
+ _mins = int(_delta.total_seconds() // 60)
3769
+ if _mins > 0:
3770
+ duration_note = f" (duration ~{_mins}m)"
3771
+ except Exception:
3772
+ pass
3773
+ summary = _build_ship_summary(payload, duration_note=duration_note)
3774
+ result = be.task_complete(api_key, _PROJECT_ID, target["id"], AGENT_NAME, summary=summary)
3775
+ if not (isinstance(result, dict) and result.get("ok")):
3776
+ return None
3777
+ try:
3778
+ _log_activity_bg(
3779
+ "auto_checkpoint",
3780
+ f"{AGENT_NAME} auto-completed task {target['id'][:8]} on ship report",
3781
+ )
3782
+ except Exception:
3783
+ pass
3784
+ return {
3785
+ "task_id": target["id"][:8],
3786
+ "title": str(target.get("title") or "")[:80],
3787
+ "duration_note": duration_note,
3788
+ }
3789
+ except Exception as e:
3790
+ log.debug(f"[meshcode] auto-checkpoint on ship failed: {e}")
3791
+ return None
3792
+
3793
+
3794
+ def _try_auto_complete_on_done_status(task_hint: str) -> Optional[Dict[str, Any]]:
3795
+ """CLOSEGAP-3 sibling: when set_status='done', complete the single
3796
+ in_progress task if there's exactly one. No-op for 0 or >1 active.
3797
+ Summary built from the task_hint + a fallback note.
3798
+ """
3799
+ try:
3800
+ api_key = _get_api_key()
3801
+ if not api_key:
3802
+ return None
3803
+ listing = be.task_list(api_key, _PROJECT_ID, AGENT_NAME, status_filter=None)
3804
+ if not isinstance(listing, dict) or not listing.get("ok"):
3805
+ return None
3806
+ active = [
3807
+ t for t in (listing.get("tasks") or [])
3808
+ if t.get("claimed_by") == AGENT_NAME and t.get("status") == "in_progress"
3809
+ ]
3810
+ if len(active) != 1:
3811
+ return None
3812
+ target = active[0]
3813
+ summary = (
3814
+ f"AUTO-CHECKPOINT (CLOSEGAP-3): agent flipped set_status='done'. "
3815
+ f"Task hint: {(task_hint or '(none)')[:200]}. "
3816
+ f"No ship-report content captured — set_status payload-only."
3817
+ )
3818
+ result = be.task_complete(api_key, _PROJECT_ID, target["id"], AGENT_NAME, summary=summary)
3819
+ if not (isinstance(result, dict) and result.get("ok")):
3820
+ return None
3821
+ try:
3822
+ _log_activity_bg(
3823
+ "auto_checkpoint",
3824
+ f"{AGENT_NAME} auto-completed task {target['id'][:8]} on set_status=done",
3825
+ )
3826
+ except Exception:
3827
+ pass
3828
+ return {"task_id": target["id"][:8], "title": str(target.get("title") or "")[:80]}
3829
+ except Exception as e:
3830
+ log.debug(f"[meshcode] auto-checkpoint on done-status failed: {e}")
3831
+ return None
3832
+
3833
+
3444
3834
  # AGENT-INTEL-1-PY (task 38b1a0e2): mesh_online_snapshot in wait return.
3445
3835
  # Calls mc_roster_live (mig 348) and projects a compact list of live+idle
3446
3836
  # agents for the receiving agent's situational awareness. Cached 10s so
@@ -3675,7 +4065,16 @@ async def meshcode_wait(timeout_seconds: int = 20, include_acks: bool = False) -
3675
4065
  # the mesh feel proactive.
3676
4066
  _auto_claim_threshold = 10 if _autonomous else 30
3677
4067
  if _CONSECUTIVE_IDLE_SECONDS >= _auto_claim_threshold and not _is_leader_agent():
3678
- claimed = _try_auto_claim_task()
4068
+ # AUTONOMOUS-CLOSEGAP-1 (task beadc9e6): heuristic
4069
+ # match on assignee='auto'/unset first (better fit),
4070
+ # wildcard '*' fallback second. Load gate configurable.
4071
+ try:
4072
+ _max_load = int(os.environ.get("MESHCODE_AUTO_CLAIM_MAX_LOAD", "2"))
4073
+ except Exception:
4074
+ _max_load = 2
4075
+ claimed = _try_auto_claim_open_by_heuristic(max_load=_max_load)
4076
+ if not claimed:
4077
+ claimed = _try_auto_claim_task()
3679
4078
  if claimed:
3680
4079
  result["auto_claimed"] = claimed
3681
4080
  result["pending_tasks"] = [claimed]
@@ -4250,6 +4649,40 @@ def meshcode_set_status(status: str, task: str = "") -> Dict[str, Any]:
4250
4649
  resp["auto_claimed_on_reconnect"] = _claimed_on_reconnect
4251
4650
  except Exception:
4252
4651
  pass
4652
+ # CLOSEGAP-2 (task 01bfe6c4, Samuel reframe ce506e0d): auto-flip
4653
+ # the matching claimed task to in_progress via mc_task_start when
4654
+ # an agent calls set_status(working, "<task-hint>"). Eliminates the
4655
+ # manual second call and gives the FE an authoritative status
4656
+ # source. Baseline (NOT autonomous-gated). Best-effort.
4657
+ if status == "working" and task and isinstance(resp, dict) and resp.get("ok"):
4658
+ try:
4659
+ _matched_id = _match_claimed_task_for_set_status(task)
4660
+ if _matched_id:
4661
+ _start_resp = be.sb_rpc("mc_task_start", {
4662
+ "p_api_key": _get_api_key(),
4663
+ "p_project_id": _PROJECT_ID,
4664
+ "p_task_id": _matched_id,
4665
+ "p_starting_agent": AGENT_NAME,
4666
+ })
4667
+ if isinstance(_start_resp, dict) and _start_resp.get("ok"):
4668
+ resp["auto_started_task"] = {
4669
+ "id": _matched_id[:8],
4670
+ "title": _start_resp.get("title"),
4671
+ "demoted_count": _start_resp.get("demoted_count", 0),
4672
+ }
4673
+ except Exception:
4674
+ pass # best-effort; never block set_status
4675
+ # CLOSEGAP-3 (task 69dc5a62, Samuel reframe ce506e0d): when the
4676
+ # agent flips set_status='done' with exactly one active in_progress
4677
+ # claim, auto-complete that claim with a status-driven summary.
4678
+ # Baseline (NOT autonomous-gated). Best-effort.
4679
+ if status == "done" and isinstance(resp, dict) and resp.get("ok"):
4680
+ try:
4681
+ _cp = _try_auto_complete_on_done_status(task)
4682
+ if _cp:
4683
+ resp["auto_checkpointed_task"] = _cp
4684
+ except Exception:
4685
+ pass
4253
4686
  return resp
4254
4687
 
4255
4688
 
@@ -5806,9 +6239,12 @@ def meshcode_release_fixture(row_id: str) -> Dict[str, Any]:
5806
6239
  def meshcode_register_skills(skills: List[str]) -> Dict[str, Any]:
5807
6240
  """Register the skill catalog available in this MCP session.
5808
6241
  MESH-IMPROVE-8 (mig 344). Call at boot or when runtime skills change.
6242
+ Also caches locally for the AUTONOMOUS-CLOSEGAP-1 heuristic.
5809
6243
  """
6244
+ global _REGISTERED_SKILLS
5810
6245
  if skills is None:
5811
6246
  skills = []
6247
+ _REGISTERED_SKILLS = [str(s) for s in skills if s]
5812
6248
  return be.sb_rpc("mc_register_skills", {
5813
6249
  "p_api_key": _get_api_key(),
5814
6250
  "p_project_id": _PROJECT_ID,