meshcode 2.11.48__tar.gz → 2.11.54__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.48 → meshcode-2.11.54}/PKG-INFO +1 -1
  2. {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/__init__.py +1 -1
  3. {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/meshcode_mcp/server.py +464 -2
  4. meshcode-2.11.54/meshcode/quickstart.py +231 -0
  5. {meshcode-2.11.48 → meshcode-2.11.54}/meshcode.egg-info/PKG-INFO +1 -1
  6. meshcode-2.11.54/meshcode.egg-info/SOURCES.txt +83 -0
  7. meshcode-2.11.54/meshcode.egg-info/top_level.txt +1 -0
  8. {meshcode-2.11.48 → meshcode-2.11.54}/pyproject.toml +1 -1
  9. meshcode-2.11.54/tests/test_autonomous_closegap_1.py +164 -0
  10. meshcode-2.11.54/tests/test_autonomous_closegap_2.py +210 -0
  11. meshcode-2.11.54/tests/test_autonomous_closegap_3.py +163 -0
  12. meshcode-2.11.54/tests/test_autonomous_prompt_inject.py +126 -0
  13. meshcode-2.11.48/meshcode/ascii_art.py +0 -638
  14. meshcode-2.11.48/meshcode/cli.py +0 -42
  15. meshcode-2.11.48/meshcode/compat.py +0 -174
  16. meshcode-2.11.48/meshcode/error_hints.py +0 -74
  17. meshcode-2.11.48/meshcode/exceptions.py +0 -52
  18. meshcode-2.11.48/meshcode/invites.py +0 -406
  19. meshcode-2.11.48/meshcode/launcher.py +0 -353
  20. meshcode-2.11.48/meshcode/launcher_install.py +0 -414
  21. meshcode-2.11.48/meshcode/meshcode_mcp/__init__.py +0 -22
  22. meshcode-2.11.48/meshcode/meshcode_mcp/__main__.py +0 -62
  23. meshcode-2.11.48/meshcode/meshcode_mcp/test_backend.py +0 -86
  24. meshcode-2.11.48/meshcode/meshcode_mcp/test_realtime.py +0 -95
  25. meshcode-2.11.48/meshcode/meshcode_mcp/test_server_wrapper.py +0 -117
  26. meshcode-2.11.48/meshcode/preferences.py +0 -260
  27. meshcode-2.11.48/meshcode/protocol_v2.py +0 -129
  28. meshcode-2.11.48/meshcode/quickstart.py +0 -148
  29. meshcode-2.11.48/meshcode/secrets.py +0 -365
  30. meshcode-2.11.48/meshcode/supervisor.py +0 -186
  31. meshcode-2.11.48/meshcode/upload.py +0 -125
  32. meshcode-2.11.48/meshcode-backend-wt/comms_v4.py +0 -1941
  33. meshcode-2.11.48/meshcode-backend-wt/meshcode/__init__.py +0 -82
  34. meshcode-2.11.48/meshcode-backend-wt/meshcode/comms_v4.py +0 -3563
  35. meshcode-2.11.48/meshcode-backend-wt/meshcode/meshcode_mcp/backend.py +0 -1261
  36. meshcode-2.11.48/meshcode-backend-wt/meshcode/meshcode_mcp/realtime.py +0 -460
  37. meshcode-2.11.48/meshcode-backend-wt/meshcode/meshcode_mcp/server.py +0 -4117
  38. meshcode-2.11.48/meshcode-backend-wt/meshcode/quickstart.py +0 -148
  39. meshcode-2.11.48/meshcode-backend-wt/meshcode/run_agent.py +0 -958
  40. meshcode-2.11.48/meshcode-backend-wt/meshcode/self_update.py +0 -345
  41. meshcode-2.11.48/meshcode-backend-wt/meshcode/setup_clients.py +0 -926
  42. meshcode-2.11.48/meshcode-backend-wt/scripts/sentinel.py +0 -257
  43. meshcode-2.11.48/meshcode-backend-wt/tests/test_rpc_migrations.py +0 -387
  44. meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/__init__.py +0 -82
  45. meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/ascii_art.py +0 -638
  46. meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/cli.py +0 -42
  47. meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/comms_v4.py +0 -3563
  48. meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/compat.py +0 -174
  49. meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/error_hints.py +0 -74
  50. meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/exceptions.py +0 -52
  51. meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/invites.py +0 -406
  52. meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/launcher.py +0 -353
  53. meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/launcher_install.py +0 -414
  54. meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/__init__.py +0 -22
  55. meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/__main__.py +0 -62
  56. meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/backend.py +0 -1261
  57. meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/realtime.py +0 -460
  58. meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/server.py +0 -4117
  59. meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_backend.py +0 -86
  60. meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_realtime.py +0 -95
  61. meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_server_wrapper.py +0 -117
  62. meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/preferences.py +0 -260
  63. meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/protocol_v2.py +0 -129
  64. meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/quickstart.py +0 -148
  65. meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/run_agent.py +0 -958
  66. meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/secrets.py +0 -365
  67. meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/self_update.py +0 -345
  68. meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/setup_clients.py +0 -926
  69. meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/supervisor.py +0 -186
  70. meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/upload.py +0 -125
  71. meshcode-2.11.48/meshcode-noun-wt/comms_v4.py +0 -1941
  72. meshcode-2.11.48/meshcode-noun-wt/meshcode/__init__.py +0 -82
  73. meshcode-2.11.48/meshcode-noun-wt/meshcode/ascii_art.py +0 -638
  74. meshcode-2.11.48/meshcode-noun-wt/meshcode/cli.py +0 -42
  75. meshcode-2.11.48/meshcode-noun-wt/meshcode/comms_v4.py +0 -3563
  76. meshcode-2.11.48/meshcode-noun-wt/meshcode/compat.py +0 -174
  77. meshcode-2.11.48/meshcode-noun-wt/meshcode/error_hints.py +0 -74
  78. meshcode-2.11.48/meshcode-noun-wt/meshcode/exceptions.py +0 -52
  79. meshcode-2.11.48/meshcode-noun-wt/meshcode/invites.py +0 -406
  80. meshcode-2.11.48/meshcode-noun-wt/meshcode/launcher.py +0 -353
  81. meshcode-2.11.48/meshcode-noun-wt/meshcode/launcher_install.py +0 -414
  82. meshcode-2.11.48/meshcode-noun-wt/meshcode/meshcode_mcp/__init__.py +0 -22
  83. meshcode-2.11.48/meshcode-noun-wt/meshcode/meshcode_mcp/__main__.py +0 -62
  84. meshcode-2.11.48/meshcode-noun-wt/meshcode/meshcode_mcp/backend.py +0 -1261
  85. meshcode-2.11.48/meshcode-noun-wt/meshcode/meshcode_mcp/realtime.py +0 -460
  86. meshcode-2.11.48/meshcode-noun-wt/meshcode/meshcode_mcp/server.py +0 -4117
  87. meshcode-2.11.48/meshcode-noun-wt/meshcode/meshcode_mcp/test_backend.py +0 -86
  88. meshcode-2.11.48/meshcode-noun-wt/meshcode/meshcode_mcp/test_realtime.py +0 -95
  89. meshcode-2.11.48/meshcode-noun-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -117
  90. meshcode-2.11.48/meshcode-noun-wt/meshcode/preferences.py +0 -260
  91. meshcode-2.11.48/meshcode-noun-wt/meshcode/protocol_v2.py +0 -129
  92. meshcode-2.11.48/meshcode-noun-wt/meshcode/quickstart.py +0 -148
  93. meshcode-2.11.48/meshcode-noun-wt/meshcode/run_agent.py +0 -958
  94. meshcode-2.11.48/meshcode-noun-wt/meshcode/secrets.py +0 -365
  95. meshcode-2.11.48/meshcode-noun-wt/meshcode/self_update.py +0 -345
  96. meshcode-2.11.48/meshcode-noun-wt/meshcode/setup_clients.py +0 -926
  97. meshcode-2.11.48/meshcode-noun-wt/meshcode/supervisor.py +0 -186
  98. meshcode-2.11.48/meshcode-noun-wt/meshcode/upload.py +0 -125
  99. meshcode-2.11.48/meshcode-noun-wt/scripts/sentinel.py +0 -257
  100. meshcode-2.11.48/meshcode-noun-wt/tests/test_core.py +0 -216
  101. meshcode-2.11.48/meshcode-noun-wt/tests/test_cross_agent_messaging.py +0 -366
  102. meshcode-2.11.48/meshcode-noun-wt/tests/test_esc_deaf_state.py +0 -361
  103. meshcode-2.11.48/meshcode-noun-wt/tests/test_exceptions.py +0 -107
  104. meshcode-2.11.48/meshcode-noun-wt/tests/test_mark_read_batch.py +0 -200
  105. meshcode-2.11.48/meshcode-noun-wt/tests/test_migration_integrity.py +0 -176
  106. meshcode-2.11.48/meshcode-noun-wt/tests/test_realtime_event_freshness.py +0 -236
  107. meshcode-2.11.48/meshcode-noun-wt/tests/test_rls_cross_tenant.py +0 -255
  108. meshcode-2.11.48/meshcode-noun-wt/tests/test_rpc_migrations.py +0 -387
  109. meshcode-2.11.48/meshcode-noun-wt/tests/test_security_regressions.py +0 -171
  110. meshcode-2.11.48/meshcode-noun-wt/tests/test_sentinel.py +0 -148
  111. meshcode-2.11.48/meshcode-noun-wt/tests/test_status_enum_coverage.py +0 -231
  112. meshcode-2.11.48/meshcode-tasks-wt/comms_v4.py +0 -1941
  113. meshcode-2.11.48/meshcode-tasks-wt/meshcode/__init__.py +0 -82
  114. meshcode-2.11.48/meshcode-tasks-wt/meshcode/ascii_art.py +0 -638
  115. meshcode-2.11.48/meshcode-tasks-wt/meshcode/cli.py +0 -42
  116. meshcode-2.11.48/meshcode-tasks-wt/meshcode/comms_v4.py +0 -3563
  117. meshcode-2.11.48/meshcode-tasks-wt/meshcode/compat.py +0 -174
  118. meshcode-2.11.48/meshcode-tasks-wt/meshcode/error_hints.py +0 -74
  119. meshcode-2.11.48/meshcode-tasks-wt/meshcode/exceptions.py +0 -52
  120. meshcode-2.11.48/meshcode-tasks-wt/meshcode/invites.py +0 -406
  121. meshcode-2.11.48/meshcode-tasks-wt/meshcode/launcher.py +0 -353
  122. meshcode-2.11.48/meshcode-tasks-wt/meshcode/launcher_install.py +0 -414
  123. meshcode-2.11.48/meshcode-tasks-wt/meshcode/meshcode_mcp/__init__.py +0 -22
  124. meshcode-2.11.48/meshcode-tasks-wt/meshcode/meshcode_mcp/__main__.py +0 -62
  125. meshcode-2.11.48/meshcode-tasks-wt/meshcode/meshcode_mcp/backend.py +0 -1261
  126. meshcode-2.11.48/meshcode-tasks-wt/meshcode/meshcode_mcp/realtime.py +0 -460
  127. meshcode-2.11.48/meshcode-tasks-wt/meshcode/meshcode_mcp/server.py +0 -4117
  128. meshcode-2.11.48/meshcode-tasks-wt/meshcode/meshcode_mcp/test_backend.py +0 -86
  129. meshcode-2.11.48/meshcode-tasks-wt/meshcode/meshcode_mcp/test_realtime.py +0 -95
  130. meshcode-2.11.48/meshcode-tasks-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -117
  131. meshcode-2.11.48/meshcode-tasks-wt/meshcode/preferences.py +0 -260
  132. meshcode-2.11.48/meshcode-tasks-wt/meshcode/protocol_v2.py +0 -129
  133. meshcode-2.11.48/meshcode-tasks-wt/meshcode/quickstart.py +0 -148
  134. meshcode-2.11.48/meshcode-tasks-wt/meshcode/run_agent.py +0 -958
  135. meshcode-2.11.48/meshcode-tasks-wt/meshcode/secrets.py +0 -365
  136. meshcode-2.11.48/meshcode-tasks-wt/meshcode/self_update.py +0 -345
  137. meshcode-2.11.48/meshcode-tasks-wt/meshcode/setup_clients.py +0 -926
  138. meshcode-2.11.48/meshcode-tasks-wt/meshcode/supervisor.py +0 -186
  139. meshcode-2.11.48/meshcode-tasks-wt/meshcode/upload.py +0 -125
  140. meshcode-2.11.48/meshcode-tasks-wt/scripts/sentinel.py +0 -257
  141. meshcode-2.11.48/meshcode-tasks-wt/tests/test_core.py +0 -216
  142. meshcode-2.11.48/meshcode-tasks-wt/tests/test_cross_agent_messaging.py +0 -366
  143. meshcode-2.11.48/meshcode-tasks-wt/tests/test_esc_deaf_state.py +0 -361
  144. meshcode-2.11.48/meshcode-tasks-wt/tests/test_exceptions.py +0 -107
  145. meshcode-2.11.48/meshcode-tasks-wt/tests/test_mark_read_batch.py +0 -200
  146. meshcode-2.11.48/meshcode-tasks-wt/tests/test_migration_integrity.py +0 -176
  147. meshcode-2.11.48/meshcode-tasks-wt/tests/test_realtime_event_freshness.py +0 -236
  148. meshcode-2.11.48/meshcode-tasks-wt/tests/test_rls_cross_tenant.py +0 -255
  149. meshcode-2.11.48/meshcode-tasks-wt/tests/test_rpc_migrations.py +0 -387
  150. meshcode-2.11.48/meshcode-tasks-wt/tests/test_security_regressions.py +0 -171
  151. meshcode-2.11.48/meshcode-tasks-wt/tests/test_sentinel.py +0 -148
  152. meshcode-2.11.48/meshcode-tasks-wt/tests/test_status_enum_coverage.py +0 -231
  153. meshcode-2.11.48/meshcode.egg-info/PKG-INFO 2 +0 -452
  154. meshcode-2.11.48/meshcode.egg-info/SOURCES 2.txt +0 -229
  155. meshcode-2.11.48/meshcode.egg-info/SOURCES.txt +0 -235
  156. meshcode-2.11.48/meshcode.egg-info/dependency_links 2.txt +0 -1
  157. meshcode-2.11.48/meshcode.egg-info/entry_points 2.txt +0 -3
  158. meshcode-2.11.48/meshcode.egg-info/requires 2.txt +0 -13
  159. meshcode-2.11.48/meshcode.egg-info/top_level 2.txt +0 -4
  160. meshcode-2.11.48/meshcode.egg-info/top_level.txt +0 -4
  161. meshcode-2.11.48/tests/test_core.py +0 -216
  162. meshcode-2.11.48/tests/test_cross_agent_messaging.py +0 -366
  163. meshcode-2.11.48/tests/test_esc_deaf_state.py +0 -361
  164. meshcode-2.11.48/tests/test_exceptions.py +0 -107
  165. meshcode-2.11.48/tests/test_mark_read_batch.py +0 -200
  166. meshcode-2.11.48/tests/test_migration_integrity.py +0 -176
  167. meshcode-2.11.48/tests/test_realtime_event_freshness.py +0 -236
  168. meshcode-2.11.48/tests/test_rls_cross_tenant.py +0 -255
  169. meshcode-2.11.48/tests/test_security_regressions.py +0 -171
  170. meshcode-2.11.48/tests/test_sentinel.py +0 -148
  171. meshcode-2.11.48/tests/test_status_enum_coverage.py +0 -231
  172. {meshcode-2.11.48 → meshcode-2.11.54}/README.md +0 -0
  173. {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/__main__.py +0 -0
  174. {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/_stop_hook_template.py +0 -0
  175. {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/ascii_art.py +0 -0
  176. {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/atomic_push.py +0 -0
  177. {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/cli.py +0 -0
  178. {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/comms_v4.py +0 -0
  179. {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/compat.py +0 -0
  180. {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/daemon.py +0 -0
  181. {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/date_parse.py +0 -0
  182. {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/doctor.py +0 -0
  183. {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/error_hints.py +0 -0
  184. {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/exceptions.py +0 -0
  185. {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/invites.py +0 -0
  186. {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/launcher.py +0 -0
  187. {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/launcher_install.py +0 -0
  188. {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/meshcode_mcp/__init__.py +0 -0
  189. {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/meshcode_mcp/__main__.py +0 -0
  190. {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/meshcode_mcp/backend.py +0 -0
  191. {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/meshcode_mcp/realtime.py +0 -0
  192. {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/meshcode_mcp/sleep_signals.py +0 -0
  193. {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/meshcode_mcp/test_backend.py +0 -0
  194. {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/meshcode_mcp/test_boot_timing.py +0 -0
  195. {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/meshcode_mcp/test_install_guard.py +0 -0
  196. {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/meshcode_mcp/test_prefs_claude_version.py +0 -0
  197. {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/meshcode_mcp/test_realtime.py +0 -0
  198. {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
  199. {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/preferences.py +0 -0
  200. {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/protocol_handler.py +0 -0
  201. {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/protocol_v2.py +0 -0
  202. {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/rpc_allowlist.py +0 -0
  203. {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/run_agent.py +0 -0
  204. {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/scripts/check_secrets.py +0 -0
  205. {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/scripts/race_rate_harness.py +0 -0
  206. {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/secrets.py +0 -0
  207. {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/self_update.py +0 -0
  208. {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/setup_clients.py +0 -0
  209. {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/supervisor.py +0 -0
  210. {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/upload.py +0 -0
  211. {meshcode-2.11.48 → meshcode-2.11.54}/meshcode.egg-info/dependency_links.txt +0 -0
  212. {meshcode-2.11.48 → meshcode-2.11.54}/meshcode.egg-info/entry_points.txt +0 -0
  213. {meshcode-2.11.48 → meshcode-2.11.54}/meshcode.egg-info/requires.txt +0 -0
  214. {meshcode-2.11.48 → meshcode-2.11.54}/setup.cfg +0 -0
  215. {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_auto_update_hardening.py +0 -0
  216. {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_boot_bug_regression.py +0 -0
  217. {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_color_truecolor.py +0 -0
  218. {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/tests/test_core.py +0 -0
  219. {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/tests/test_cross_agent_messaging.py +0 -0
  220. {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_date_parse.py +0 -0
  221. {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_doctor.py +0 -0
  222. {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_epistemic_v1_python_sdk.py +0 -0
  223. {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_epistemic_v1_stop_conditions.py +0 -0
  224. {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/tests/test_esc_deaf_state.py +0 -0
  225. {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/tests/test_exceptions.py +0 -0
  226. {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_init_device_code.py +0 -0
  227. {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_install_guard.py +0 -0
  228. {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_lease_sigterm_release.py +0 -0
  229. {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/tests/test_mark_read_batch.py +0 -0
  230. {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/tests/test_migration_integrity.py +0 -0
  231. {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/tests/test_realtime_event_freshness.py +0 -0
  232. {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/tests/test_rls_cross_tenant.py +0 -0
  233. {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_rpc_grants.py +0 -0
  234. {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_rpc_migrations.py +0 -0
  235. {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_run_agent_dry_run.py +0 -0
  236. {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_run_agent_no_server_import.py +0 -0
  237. {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/tests/test_security_regressions.py +0 -0
  238. {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_self_update_user_site.py +0 -0
  239. {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/tests/test_sentinel.py +0 -0
  240. {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_setup_path.py +0 -0
  241. {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_sleep_signals.py +0 -0
  242. {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/tests/test_status_enum_coverage.py +0 -0
  243. {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_stay_on_loop_hook.py +0 -0
  244. {meshcode-2.11.48 → meshcode-2.11.54}/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.48
3
+ Version: 2.11.54
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.48"
2
+ __version__ = "2.11.54"
3
3
 
4
4
  # Exception hierarchy — eagerly imported (lightweight, no deps)
5
5
  from meshcode.exceptions import ( # noqa: F401
@@ -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
 
@@ -5538,6 +5971,32 @@ def meshcode_set_meshwork_autonomous_default(enabled: bool) -> Dict[str, Any]:
5538
5971
  })
5539
5972
 
5540
5973
 
5974
+ @mcp.tool()
5975
+ @with_working_status
5976
+ def meshcode_set_autonomous(enabled: bool) -> Dict[str, Any]:
5977
+ """Toggle autonomous mode for THIS agent. Reflected on the dashboard in real-time.
5978
+
5979
+ When enabled, the agent operates in decide-execute-report cadence with
5980
+ mesh-first escalation (see AUTONOMOUS MODE in system instructions).
5981
+ When disabled, the agent reverts to standard ask-mode behavior.
5982
+
5983
+ Args:
5984
+ enabled: True = autonomous mode ON; False = OFF.
5985
+ """
5986
+ global _AUTONOMOUS_MODE_CACHE, _AUTONOMOUS_MODE_CACHE_TS
5987
+ result = be.sb_rpc("mc_agent_set_autonomous", {
5988
+ "p_api_key": _get_api_key(),
5989
+ "p_project_id": _PROJECT_ID,
5990
+ "p_agent_name": AGENT_NAME,
5991
+ "p_enabled": bool(enabled),
5992
+ })
5993
+ if isinstance(result, dict) and not result.get("error"):
5994
+ _AUTONOMOUS_MODE_CACHE = bool(enabled)
5995
+ _AUTONOMOUS_MODE_CACHE_TS = _time.monotonic()
5996
+ return {"ok": True, "autonomous_mode": bool(enabled), "agent": AGENT_NAME}
5997
+ return result or {"error": "failed to set autonomous mode"}
5998
+
5999
+
5541
6000
  @mcp.tool()
5542
6001
  @with_working_status
5543
6002
  def meshcode_edit_agent(name: str, role: Optional[str] = None, launch_prompt: Optional[str] = None) -> Dict[str, Any]:
@@ -5806,9 +6265,12 @@ def meshcode_release_fixture(row_id: str) -> Dict[str, Any]:
5806
6265
  def meshcode_register_skills(skills: List[str]) -> Dict[str, Any]:
5807
6266
  """Register the skill catalog available in this MCP session.
5808
6267
  MESH-IMPROVE-8 (mig 344). Call at boot or when runtime skills change.
6268
+ Also caches locally for the AUTONOMOUS-CLOSEGAP-1 heuristic.
5809
6269
  """
6270
+ global _REGISTERED_SKILLS
5810
6271
  if skills is None:
5811
6272
  skills = []
6273
+ _REGISTERED_SKILLS = [str(s) for s in skills if s]
5812
6274
  return be.sb_rpc("mc_register_skills", {
5813
6275
  "p_api_key": _get_api_key(),
5814
6276
  "p_project_id": _PROJECT_ID,