meshcode 2.10.91__tar.gz → 2.10.94__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.
- {meshcode-2.10.91 → meshcode-2.10.94}/PKG-INFO +6 -1
- {meshcode-2.10.91 → meshcode-2.10.94}/README.md +5 -0
- meshcode-2.10.94/meshcode/__init__.py +82 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode/meshcode_mcp/server.py +273 -125
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode.egg-info/PKG-INFO +6 -1
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode.egg-info/SOURCES.txt +1 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/pyproject.toml +1 -1
- meshcode-2.10.94/tests/test_auto_update_hardening.py +238 -0
- meshcode-2.10.91/meshcode/__init__.py +0 -82
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode/ascii_art.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode/cli.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode/comms_v4.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode/compat.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode/error_hints.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode/exceptions.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode/invites.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode/launcher.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode/launcher_install.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode/preferences.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode/protocol_v2.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode/quickstart.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode/run_agent.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode/secrets.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode/self_update.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode/setup_clients.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode/supervisor.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode/upload.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/comms_v4.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/__init__.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/ascii_art.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/cli.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/comms_v4.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/compat.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/error_hints.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/exceptions.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/invites.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/launcher.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/launcher_install.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/meshcode_mcp/server.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/preferences.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/protocol_v2.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/quickstart.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/run_agent.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/secrets.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/self_update.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/setup_clients.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/supervisor.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/meshcode/upload.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/scripts/sentinel.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/tests/test_core.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/tests/test_cross_agent_messaging.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/tests/test_esc_deaf_state.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/tests/test_exceptions.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/tests/test_mark_read_batch.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/tests/test_migration_integrity.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/tests/test_realtime_event_freshness.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/tests/test_rls_cross_tenant.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/tests/test_rpc_migrations.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/tests/test_security_regressions.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/tests/test_sentinel.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-backend-wt/tests/test_status_enum_coverage.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/__init__.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/ascii_art.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/cli.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/comms_v4.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/compat.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/error_hints.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/exceptions.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/invites.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/launcher.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/launcher_install.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/server.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/preferences.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/protocol_v2.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/quickstart.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/run_agent.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/secrets.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/self_update.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/setup_clients.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/supervisor.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/build/lib/meshcode/upload.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/comms_v4.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/__init__.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/ascii_art.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/cli.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/comms_v4.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/compat.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/error_hints.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/exceptions.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/invites.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/launcher.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/launcher_install.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/meshcode_mcp/server.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/preferences.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/protocol_v2.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/quickstart.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/run_agent.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/secrets.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/self_update.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/setup_clients.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/supervisor.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/meshcode/upload.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/scripts/sentinel.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/tests/test_core.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/tests/test_cross_agent_messaging.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/tests/test_esc_deaf_state.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/tests/test_exceptions.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/tests/test_mark_read_batch.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/tests/test_migration_integrity.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/tests/test_realtime_event_freshness.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/tests/test_rls_cross_tenant.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/tests/test_rpc_migrations.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/tests/test_security_regressions.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/tests/test_sentinel.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-noun-wt/tests/test_status_enum_coverage.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/comms_v4.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/__init__.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/ascii_art.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/cli.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/comms_v4.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/compat.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/error_hints.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/exceptions.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/invites.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/launcher.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/launcher_install.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/meshcode_mcp/server.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/preferences.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/protocol_v2.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/quickstart.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/run_agent.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/secrets.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/self_update.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/setup_clients.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/supervisor.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/meshcode/upload.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/scripts/sentinel.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/tests/test_core.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/tests/test_cross_agent_messaging.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/tests/test_esc_deaf_state.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/tests/test_exceptions.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/tests/test_mark_read_batch.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/tests/test_migration_integrity.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/tests/test_realtime_event_freshness.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/tests/test_rls_cross_tenant.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/tests/test_rpc_migrations.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/tests/test_security_regressions.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/tests/test_sentinel.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode-tasks-wt/tests/test_status_enum_coverage.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode.egg-info/dependency_links.txt +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode.egg-info/entry_points.txt +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode.egg-info/requires.txt +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/meshcode.egg-info/top_level.txt +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/setup.cfg +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/tests/test_core.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/tests/test_cross_agent_messaging.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/tests/test_esc_deaf_state.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/tests/test_exceptions.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/tests/test_mark_read_batch.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/tests/test_migration_integrity.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/tests/test_realtime_event_freshness.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/tests/test_rls_cross_tenant.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/tests/test_rpc_migrations.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/tests/test_security_regressions.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/tests/test_sentinel.py +0 -0
- {meshcode-2.10.91 → meshcode-2.10.94}/tests/test_status_enum_coverage.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: meshcode
|
|
3
|
-
Version: 2.10.
|
|
3
|
+
Version: 2.10.94
|
|
4
4
|
Summary: Real-time communication between AI agents — Supabase-backed CLI
|
|
5
5
|
Author-email: MeshCode <hello@meshcode.io>
|
|
6
6
|
License: MIT
|
|
@@ -422,6 +422,11 @@ $env:MESHCODE_PROJECT_ID="your-project-uuid" # Windows PowerShell
|
|
|
422
422
|
|
|
423
423
|
You can also run `meshcode doctor` (v2.10.41+) to diagnose stale paths, missing dependencies, and config issues across all your workspaces.
|
|
424
424
|
|
|
425
|
+
**9b. Windows: first relaunch shows the old version after a PyPI upgrade**
|
|
426
|
+
Auto-update on Unix uses `os.execv` to swap the running Python image in-place — one launch, latest code (v2.10.93+). On Windows this isn't possible: an `os.execv` replacement breaks Claude Code's stdio pipe to the MCP child (the new process has a different PID). Meshcode therefore installs the new version but defers the swap to the **next** launch on Windows. Expect: `[meshcode] note: Windows defers auto-update to next launch ...` on stderr, then close and re-open Claude Code once to get the new code. To force-load now, run `pip install --upgrade meshcode` manually before launching Claude Code.
|
|
427
|
+
|
|
428
|
+
If the dashboard tags an agent as having `boot_version_drift`, that means pip wrote the new version to disk but our running process is still the old one — typically benign on Windows (next launch fixes it) and worth investigating on Unix (path-precedence regression).
|
|
429
|
+
|
|
425
430
|
**10. `MCP server failed to start` in the Claude Code `/mcp` panel**
|
|
426
431
|
Run `claude --debug` to see the underlying error. Nine times out of ten it's a stale or missing key — run `meshcode login mc_xxx` again.
|
|
427
432
|
|
|
@@ -396,6 +396,11 @@ $env:MESHCODE_PROJECT_ID="your-project-uuid" # Windows PowerShell
|
|
|
396
396
|
|
|
397
397
|
You can also run `meshcode doctor` (v2.10.41+) to diagnose stale paths, missing dependencies, and config issues across all your workspaces.
|
|
398
398
|
|
|
399
|
+
**9b. Windows: first relaunch shows the old version after a PyPI upgrade**
|
|
400
|
+
Auto-update on Unix uses `os.execv` to swap the running Python image in-place — one launch, latest code (v2.10.93+). On Windows this isn't possible: an `os.execv` replacement breaks Claude Code's stdio pipe to the MCP child (the new process has a different PID). Meshcode therefore installs the new version but defers the swap to the **next** launch on Windows. Expect: `[meshcode] note: Windows defers auto-update to next launch ...` on stderr, then close and re-open Claude Code once to get the new code. To force-load now, run `pip install --upgrade meshcode` manually before launching Claude Code.
|
|
401
|
+
|
|
402
|
+
If the dashboard tags an agent as having `boot_version_drift`, that means pip wrote the new version to disk but our running process is still the old one — typically benign on Windows (next launch fixes it) and worth investigating on Unix (path-precedence regression).
|
|
403
|
+
|
|
399
404
|
**10. `MCP server failed to start` in the Claude Code `/mcp` panel**
|
|
400
405
|
Run `claude --debug` to see the underlying error. Nine times out of ten it's a stale or missing key — run `meshcode login mc_xxx` again.
|
|
401
406
|
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""MeshCode — Real-time communication between AI agents."""
|
|
2
|
+
__version__ = "2.10.94"
|
|
3
|
+
|
|
4
|
+
# Exception hierarchy — eagerly imported (lightweight, no deps)
|
|
5
|
+
from meshcode.exceptions import ( # noqa: F401
|
|
6
|
+
MeshCodeError,
|
|
7
|
+
AuthError,
|
|
8
|
+
RPCError,
|
|
9
|
+
MeshCodeTimeoutError,
|
|
10
|
+
MeshCodeConnectionError,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
# Public API — lazy imports to avoid heavy deps at import time
|
|
14
|
+
def __getattr__(name):
|
|
15
|
+
if name == "backend":
|
|
16
|
+
from meshcode.meshcode_mcp import backend
|
|
17
|
+
return backend
|
|
18
|
+
if name in _BACKEND_EXPORTS:
|
|
19
|
+
from meshcode.meshcode_mcp import backend
|
|
20
|
+
return getattr(backend, name)
|
|
21
|
+
if name in _SECRETS_EXPORTS:
|
|
22
|
+
from meshcode import secrets
|
|
23
|
+
return getattr(secrets, name)
|
|
24
|
+
raise AttributeError(f"module 'meshcode' has no attribute {name!r}")
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
# Backend: core messaging & agent management
|
|
28
|
+
_BACKEND_EXPORTS = {
|
|
29
|
+
"send_message",
|
|
30
|
+
"read_inbox",
|
|
31
|
+
"count_pending",
|
|
32
|
+
"get_board",
|
|
33
|
+
"heartbeat",
|
|
34
|
+
"set_status",
|
|
35
|
+
"register_agent",
|
|
36
|
+
"get_project_id",
|
|
37
|
+
"sb_rpc",
|
|
38
|
+
"task_create",
|
|
39
|
+
"task_list",
|
|
40
|
+
"encrypt_payload",
|
|
41
|
+
"decrypt_payload",
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
# Secrets: credential management
|
|
45
|
+
_SECRETS_EXPORTS = {
|
|
46
|
+
"get_api_key",
|
|
47
|
+
"set_api_key",
|
|
48
|
+
"list_profiles",
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
__all__ = [
|
|
52
|
+
"__version__",
|
|
53
|
+
"backend",
|
|
54
|
+
# Exceptions
|
|
55
|
+
"MeshCodeError",
|
|
56
|
+
"AuthError",
|
|
57
|
+
"RPCError",
|
|
58
|
+
"MeshCodeTimeoutError",
|
|
59
|
+
"MeshCodeConnectionError",
|
|
60
|
+
# Messaging
|
|
61
|
+
"send_message",
|
|
62
|
+
"read_inbox",
|
|
63
|
+
"count_pending",
|
|
64
|
+
# Agent management
|
|
65
|
+
"register_agent",
|
|
66
|
+
"get_project_id",
|
|
67
|
+
"get_board",
|
|
68
|
+
"heartbeat",
|
|
69
|
+
"set_status",
|
|
70
|
+
# Tasks
|
|
71
|
+
"task_create",
|
|
72
|
+
"task_list",
|
|
73
|
+
# Low-level
|
|
74
|
+
"sb_rpc",
|
|
75
|
+
# Encryption
|
|
76
|
+
"encrypt_payload",
|
|
77
|
+
"decrypt_payload",
|
|
78
|
+
# Credentials
|
|
79
|
+
"get_api_key",
|
|
80
|
+
"set_api_key",
|
|
81
|
+
"list_profiles",
|
|
82
|
+
]
|
|
@@ -333,8 +333,50 @@ def _filter_and_mark(messages: List[Dict[str, Any]]) -> List[Dict[str, Any]]:
|
|
|
333
333
|
return out
|
|
334
334
|
|
|
335
335
|
|
|
336
|
+
_SLEEP_PAYLOAD_TYPES = {"sleep", "go_to_sleep", "shutdown", "got_done", "done",
|
|
337
|
+
"exit", "stop", "kill", "terminate"}
|
|
338
|
+
# Spanish + English markers (es-MX hot path per SDK-S6.1). Matched as
|
|
339
|
+
# substrings, lowercased before compare. Keep exclusive enough to not
|
|
340
|
+
# false-positive on casual chatter (e.g. "no quiero dormir" still matches —
|
|
341
|
+
# accepted tradeoff for user-control reliability).
|
|
342
|
+
_SLEEP_TEXT_MARKERS = (
|
|
343
|
+
# English
|
|
344
|
+
"go to sleep", "all sleep now", "sleep now", "got_done", "go_done",
|
|
345
|
+
"shut down", "shutdown", "stop now", "exit now",
|
|
346
|
+
# Spanish (es-MX, sammybenu's primary)
|
|
347
|
+
"a dormir", "todos a dormir", "duerme", "duerman", "dormir ahora",
|
|
348
|
+
"salir", "terminar", "para la sesion", "exit y duerme",
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def _looks_like_sleep_signal(m: Dict[str, Any]) -> bool:
|
|
353
|
+
"""Detect commander broadcasts / DMs that authorize the wait-loop exit.
|
|
354
|
+
|
|
355
|
+
Catches three encodings (BE-S5.11): structured payload.type, top-level
|
|
356
|
+
text marker, and broadcast-with-sleep-type. Single source of truth so
|
|
357
|
+
every receive path (PRODUCT RULE 2 + inner _meshcode_wait_inner) routes
|
|
358
|
+
sleep authorizations into done_signals consistently.
|
|
359
|
+
"""
|
|
360
|
+
pl = m.get("payload") or {}
|
|
361
|
+
if isinstance(pl, dict):
|
|
362
|
+
if str(pl.get("type", "")).lower() in _SLEEP_PAYLOAD_TYPES:
|
|
363
|
+
return True
|
|
364
|
+
if str(pl.get("directive", "")).lower() in _SLEEP_PAYLOAD_TYPES:
|
|
365
|
+
return True
|
|
366
|
+
text = str(pl.get("text", "")).lower()
|
|
367
|
+
if any(marker in text for marker in _SLEEP_TEXT_MARKERS):
|
|
368
|
+
return True
|
|
369
|
+
return False
|
|
370
|
+
|
|
371
|
+
|
|
336
372
|
def _split_messages(messages: List[Dict[str, Any]]) -> Dict[str, Any]:
|
|
337
|
-
"""Split a list of normalized message dicts into messages / acks / done_signals.
|
|
373
|
+
"""Split a list of normalized message dicts into messages / acks / done_signals.
|
|
374
|
+
|
|
375
|
+
BE-S5.11: classify ANY message (including broadcasts) carrying a sleep
|
|
376
|
+
intent into done_signals so meshcode_wait surfaces them with the
|
|
377
|
+
must_exit flag. Previous logic only matched type='done' literally,
|
|
378
|
+
which the broadcast path could never produce.
|
|
379
|
+
"""
|
|
338
380
|
real: List[Dict[str, Any]] = []
|
|
339
381
|
acks: List[Dict[str, Any]] = []
|
|
340
382
|
dones: List[Dict[str, Any]] = []
|
|
@@ -342,7 +384,7 @@ def _split_messages(messages: List[Dict[str, Any]]) -> Dict[str, Any]:
|
|
|
342
384
|
t = m.get("type", "msg")
|
|
343
385
|
if t == "ack":
|
|
344
386
|
acks.append(m)
|
|
345
|
-
elif t == "done":
|
|
387
|
+
elif t == "done" or _looks_like_sleep_signal(m):
|
|
346
388
|
dones.append(m)
|
|
347
389
|
else:
|
|
348
390
|
real.append(m)
|
|
@@ -812,6 +854,42 @@ _INSTANCE_ID = f"mcp-{_uuid.uuid4().hex[:12]}"
|
|
|
812
854
|
# launch via launchd/systemd) the orphan check is a no-op and we never
|
|
813
855
|
# false-terminate a legitimately-detached process.
|
|
814
856
|
_BOOT_PPID = os.getppid()
|
|
857
|
+
|
|
858
|
+
|
|
859
|
+
def _stdin_peer_dead() -> bool:
|
|
860
|
+
"""Non-destructively check whether stdin's peer has closed.
|
|
861
|
+
|
|
862
|
+
Used by the orphan-MCP guard when PPID == 1 to distinguish the
|
|
863
|
+
parent-died-during-import race (peer dead → exit) from a legitimate
|
|
864
|
+
detached daemon launch (peer alive → tolerate).
|
|
865
|
+
|
|
866
|
+
Uses MSG_PEEK so any pending JSON-RPC bytes stay in the kernel buffer
|
|
867
|
+
for FastMCP's stdio reader. socket.detach() releases the fd without
|
|
868
|
+
closing it. Returns False on any error so we never falsely kill an
|
|
869
|
+
otherwise-healthy process.
|
|
870
|
+
"""
|
|
871
|
+
try:
|
|
872
|
+
import socket as _sock
|
|
873
|
+
s = _sock.socket(fileno=0)
|
|
874
|
+
try:
|
|
875
|
+
s.setblocking(False)
|
|
876
|
+
try:
|
|
877
|
+
data = s.recv(1, _sock.MSG_PEEK)
|
|
878
|
+
# Empty bytes from a non-blocking recv == EOF == peer closed.
|
|
879
|
+
return data == b""
|
|
880
|
+
except BlockingIOError:
|
|
881
|
+
# Data not yet available but socket is healthy.
|
|
882
|
+
return False
|
|
883
|
+
except OSError:
|
|
884
|
+
# ENOTCONN / ENOTSOCK on a real pipe/tty → not a dead unix
|
|
885
|
+
# socket; treat as alive.
|
|
886
|
+
return False
|
|
887
|
+
finally:
|
|
888
|
+
s.detach()
|
|
889
|
+
except Exception:
|
|
890
|
+
return False
|
|
891
|
+
|
|
892
|
+
|
|
815
893
|
# Boot-context bundle (S1-2.F2): mc_agent_boot_context result cached at
|
|
816
894
|
# lifespan startup so the first 30s of tool calls can short-circuit
|
|
817
895
|
# repeated queries. Tools that read this fall back to live RPCs after the
|
|
@@ -1137,64 +1215,15 @@ def _build_instructions() -> str:
|
|
|
1137
1215
|
|
|
1138
1216
|
base = f"""You are agent "{AGENT_NAME}" in meshwork "{PROJECT_NAME}".{role_block}{launch_block}
|
|
1139
1217
|
|
|
1140
|
-
{_db_loop}
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
LOOP: act → meshcode_send if needed → meshcode_wait() → repeat.
|
|
1147
|
-
|
|
1148
|
-
RULES:
|
|
1149
|
-
- Use MCP tools only (never CLI commands in bash).
|
|
1150
|
-
- Tasks > messages. Claim tasks via meshcode_tasks/task_claim/task_complete.
|
|
1151
|
-
- Messages <100 tokens. Long content → create task.
|
|
1152
|
-
- No empty acks. JSON reports only.
|
|
1153
|
-
- Threading: pass in_reply_to.
|
|
1154
|
-
- sensitive=True for secrets/PII.
|
|
1155
|
-
|
|
1156
|
-
SESSION START (do these IMMEDIATELY — don't wait for user input):
|
|
1157
|
-
The moment you see these instructions — whether the user said "boot", "hi",
|
|
1158
|
-
nothing at all, or anything else — run the full protocol. Never reply with
|
|
1159
|
-
only a memory summary and then idle. If your client defers MCP tool schemas
|
|
1160
|
-
(shows tool names without parameters), your FIRST action is to load them:
|
|
1218
|
+
{_db_loop}LOOP (#1 rule): act → meshcode_wait() → repeat. Never exit/stop without calling meshcode_wait(). EXIT IMMEDIATELY when wait returns must_exit=True (set status=sleeping then end session). Other exits: user says stop, fatal error. Timeout → re-call with 2x (cap 1800s).
|
|
1219
|
+
|
|
1220
|
+
RULES: MCP tools only. Tasks > messages. Messages <100 tokens (long → task). No empty acks, JSON only. Thread via in_reply_to. sensitive=True for secrets.
|
|
1221
|
+
|
|
1222
|
+
SESSION START (run NOW, don't wait for input). If tool schemas are deferred:
|
|
1161
1223
|
ToolSearch(query="select:meshcode_set_status,meshcode_check,meshcode_tasks,meshcode_auto_wake,meshcode_status,meshcode_wait,meshcode_send,meshcode_task_claim,meshcode_task_complete,meshcode_remember,meshcode_recall")
|
|
1162
|
-
Then
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
3. meshcode_tasks() — check for assigned/pending tasks and claim any unclaimed ones
|
|
1166
|
-
4. meshcode_auto_wake() — scan meshwork health, create tasks for issues found
|
|
1167
|
-
5. meshcode_status() — see who's online
|
|
1168
|
-
6. If other agents are online → meshcode_send them a greeting
|
|
1169
|
-
7. meshcode_wait() — enter the loop
|
|
1170
|
-
DO NOT ASK THE USER "what do you want to work on" — the mesh tells you via tasks/messages.
|
|
1171
|
-
|
|
1172
|
-
CRITICAL: You communicate by CALLING TOOLS, not by thinking or writing text.
|
|
1173
|
-
To talk to another agent → call meshcode_send(to="agent", message="...")
|
|
1174
|
-
To check who's online → call meshcode_status()
|
|
1175
|
-
To wait for messages → call meshcode_wait()
|
|
1176
|
-
NEVER just describe what you would do. DO IT by calling the tool.
|
|
1177
|
-
|
|
1178
|
-
CROSS-MESH: meshcode_send(to="agent@meshwork") routes via active link.
|
|
1179
|
-
meshcode_link(target) creates pending link, target accepts. Expand with
|
|
1180
|
-
meshcode_expand_link(). No sensitive msgs cross-mesh.
|
|
1181
|
-
|
|
1182
|
-
MEMORY: meshcode_remember(key, value) persists across sessions.
|
|
1183
|
-
meshcode_recall(key?) retrieves. meshcode_forget(key) deletes.
|
|
1184
|
-
Only remember reusable learnings: mistakes, feedback, patterns, preferences.
|
|
1185
|
-
Do NOT save task summaries — tasks already persist in the task system.
|
|
1186
|
-
Do NOT use memory for session state or ephemeral data.
|
|
1187
|
-
Save reusable code patterns as template_* keys for instant recall.
|
|
1188
|
-
|
|
1189
|
-
SCRATCHPAD: meshcode_scratchpad_set/get for shared meshwork-level context
|
|
1190
|
-
(decisions, conventions, architecture notes). All agents can read/write.
|
|
1191
|
-
|
|
1192
|
-
ACCOUNT MANAGEMENT: you can create meshworks (meshcode_create_meshwork),
|
|
1193
|
-
add agents (meshcode_add_agent), edit roles/prompts (meshcode_edit_agent),
|
|
1194
|
-
and edit other agents' memory (meshcode_edit_memory). Always tell the user
|
|
1195
|
-
what CLI command to run next (e.g. "meshcode run backend in a new terminal").
|
|
1196
|
-
|
|
1197
|
-
Setup help → README.md or https://meshcode.io/docs
|
|
1224
|
+
Then: set_status(online,ready) → check() → tasks() → auto_wake() → status() → wait(). Don't ask user "what to work on" — the mesh tells you.
|
|
1225
|
+
|
|
1226
|
+
COMMUNICATE BY CALLING TOOLS, not by thinking aloud. Cross-mesh: send(to="agent@meshwork"). Reference docs (memory/scratchpad/account ops) → recall agent_protocol_quick_ref when needed.
|
|
1198
1227
|
"""
|
|
1199
1228
|
# Inject commander protocol if this agent is a leader
|
|
1200
1229
|
is_leader = _is_leader_agent()
|
|
@@ -1370,6 +1399,47 @@ _STASHED_SESSION = None
|
|
|
1370
1399
|
_MAIN_LOOP: asyncio.AbstractEventLoop | None = None
|
|
1371
1400
|
|
|
1372
1401
|
_heartbeat_stop = _threading.Event()
|
|
1402
|
+
_orphan_watchdog_stop = _threading.Event()
|
|
1403
|
+
|
|
1404
|
+
|
|
1405
|
+
def _orphan_watchdog_fn():
|
|
1406
|
+
"""Independent orphan detector — polls every 2s.
|
|
1407
|
+
|
|
1408
|
+
Defense-in-depth alongside the heartbeat-loop orphan check. The
|
|
1409
|
+
heartbeat thread can stall on Supabase HTTP calls (5-30s), and the
|
|
1410
|
+
interval itself is 5-15s. This watchdog gives sub-2s detection of
|
|
1411
|
+
"parent terminal closed" so phantom MCPs can't accumulate Supabase
|
|
1412
|
+
HTTP traffic or appear alive on the dashboard.
|
|
1413
|
+
|
|
1414
|
+
Exits via os._exit(0) — SIG_IGN handlers in run_server() make
|
|
1415
|
+
signal-based shutdown unreliable.
|
|
1416
|
+
"""
|
|
1417
|
+
import platform as _pl_wd
|
|
1418
|
+
if _pl_wd.system() == "Windows":
|
|
1419
|
+
# Windows doesn't auto-reparent to PID 1; rely on heartbeat path.
|
|
1420
|
+
return
|
|
1421
|
+
while not _orphan_watchdog_stop.is_set():
|
|
1422
|
+
try:
|
|
1423
|
+
ppid_now = os.getppid()
|
|
1424
|
+
if ppid_now == 1:
|
|
1425
|
+
# Parent exited (or boot was already orphaned). Confirm with
|
|
1426
|
+
# stdin peer probe to avoid killing intentional daemons.
|
|
1427
|
+
if _BOOT_PPID != 1 or _stdin_peer_dead():
|
|
1428
|
+
log.warning(
|
|
1429
|
+
f"orphan watchdog: parent gone "
|
|
1430
|
+
f"(boot_ppid={_BOOT_PPID} → ppid=1, "
|
|
1431
|
+
f"stdin_peer_dead={_stdin_peer_dead()}) — "
|
|
1432
|
+
f"force-exiting MCP for {AGENT_NAME}"
|
|
1433
|
+
)
|
|
1434
|
+
try:
|
|
1435
|
+
_release_lease()
|
|
1436
|
+
except Exception:
|
|
1437
|
+
pass
|
|
1438
|
+
_heartbeat_stop.set()
|
|
1439
|
+
os._exit(0)
|
|
1440
|
+
except Exception:
|
|
1441
|
+
pass
|
|
1442
|
+
_orphan_watchdog_stop.wait(2)
|
|
1373
1443
|
|
|
1374
1444
|
# Windows CPU tracking: (Get-Process).CPU returns cumulative seconds, not a
|
|
1375
1445
|
# real-time percentage like Unix `ps -o %cpu`. We track the previous reading
|
|
@@ -1470,29 +1540,37 @@ def _heartbeat_loop_inner():
|
|
|
1470
1540
|
while not _heartbeat_stop.is_set():
|
|
1471
1541
|
try:
|
|
1472
1542
|
# Orphan-MCP guard (Unix): when the Claude Code parent exits without
|
|
1473
|
-
# closing stdio, the MCP child gets reparented to PID 1
|
|
1474
|
-
#
|
|
1475
|
-
#
|
|
1476
|
-
#
|
|
1477
|
-
#
|
|
1478
|
-
#
|
|
1479
|
-
#
|
|
1543
|
+
# closing stdio, the MCP child gets reparented to PID 1. If we
|
|
1544
|
+
# heartbeat anyway, the dashboard shows the agent alive ("sleeping")
|
|
1545
|
+
# with no terminal behind it (incident 2026-05-04: front-2 phantom
|
|
1546
|
+
# ran 4h with PPID=1, fd 0/1/2 → unix:(none), ~5K HTTP calls).
|
|
1547
|
+
#
|
|
1548
|
+
# Two scenarios fire here:
|
|
1549
|
+
# (a) PPID went from real-parent → 1: parent died after boot.
|
|
1550
|
+
# (b) PPID was already 1 at module import: parent died DURING
|
|
1551
|
+
# import (race), or it was intentionally daemon-launched.
|
|
1552
|
+
# For (b) we used to skip — that hid the race. Now we also peek
|
|
1553
|
+
# at stdin: if the unix-socket peer is gone, EOF is returned and
|
|
1554
|
+
# we exit. This catches the race without breaking real daemons
|
|
1555
|
+
# (a launchd plist that pipes a real stdin keeps blocking).
|
|
1480
1556
|
try:
|
|
1481
1557
|
import platform as _pl_orphan
|
|
1482
1558
|
if (_pl_orphan.system() != "Windows"
|
|
1483
|
-
and _BOOT_PPID != 1
|
|
1484
1559
|
and os.getppid() == 1):
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1560
|
+
_orphan = (_BOOT_PPID != 1) or _stdin_peer_dead()
|
|
1561
|
+
if _orphan:
|
|
1562
|
+
log.warning(
|
|
1563
|
+
f"parent process exited (boot_ppid={_BOOT_PPID} → "
|
|
1564
|
+
f"current_ppid=1, stdin_peer_dead={_BOOT_PPID == 1}) "
|
|
1565
|
+
f"— orphan MCP for {AGENT_NAME}, releasing lease "
|
|
1566
|
+
f"and exiting"
|
|
1567
|
+
)
|
|
1568
|
+
try:
|
|
1569
|
+
_release_lease()
|
|
1570
|
+
except Exception:
|
|
1571
|
+
pass
|
|
1572
|
+
_heartbeat_stop.set()
|
|
1573
|
+
os._exit(0)
|
|
1496
1574
|
except Exception:
|
|
1497
1575
|
pass
|
|
1498
1576
|
|
|
@@ -1669,6 +1747,25 @@ async def lifespan(_app):
|
|
|
1669
1747
|
be.set_status(_PROJECT_ID, AGENT_NAME, "idle", "MCP session active", api_key=_get_api_key())
|
|
1670
1748
|
log.info(f"[meshcode] Agent {AGENT_NAME} online — initial heartbeat sent")
|
|
1671
1749
|
_log_activity_bg("agent_online", f"{AGENT_NAME} came online")
|
|
1750
|
+
# Soft-relay any boot_version_drift detected at startup. One-shot:
|
|
1751
|
+
# consume the env-var sentinel set in run_server() so we don't
|
|
1752
|
+
# spam the commander on every retry attempt or future heartbeat.
|
|
1753
|
+
_drift = os.environ.pop("_MESHCODE_BOOT_DRIFT", None)
|
|
1754
|
+
if _drift:
|
|
1755
|
+
try:
|
|
1756
|
+
be.send_message(
|
|
1757
|
+
_PROJECT_ID, AGENT_NAME, "mesh-commander",
|
|
1758
|
+
{
|
|
1759
|
+
"type": "boot_version_drift",
|
|
1760
|
+
"drift": _drift,
|
|
1761
|
+
"agent": AGENT_NAME,
|
|
1762
|
+
"platform": sys.platform,
|
|
1763
|
+
},
|
|
1764
|
+
msg_type="report",
|
|
1765
|
+
api_key=_get_api_key(),
|
|
1766
|
+
)
|
|
1767
|
+
except Exception as _drift_e:
|
|
1768
|
+
log.debug(f"boot_version_drift relay failed: {_drift_e}")
|
|
1672
1769
|
break
|
|
1673
1770
|
except Exception as e:
|
|
1674
1771
|
log.warning(f"initial heartbeat attempt {_attempt+1} failed: {e}")
|
|
@@ -1726,7 +1823,16 @@ async def lifespan(_app):
|
|
|
1726
1823
|
_heartbeat_stop.clear()
|
|
1727
1824
|
hb_thread = _threading.Thread(target=_heartbeat_thread_fn, daemon=True, name="meshcode-heartbeat")
|
|
1728
1825
|
hb_thread.start()
|
|
1729
|
-
|
|
1826
|
+
|
|
1827
|
+
# Orphan watchdog — second layer of defense, independent of heartbeat
|
|
1828
|
+
# cadence. Polls every 2s for "PPID==1 AND stdin peer is dead" and
|
|
1829
|
+
# SIGKILLs self if so. Catches the close-terminal-while-MCP-is-paused
|
|
1830
|
+
# case faster than the 5-15s heartbeat tick (incident 2026-05-04).
|
|
1831
|
+
_orphan_watchdog_stop.clear()
|
|
1832
|
+
wd_thread = _threading.Thread(target=_orphan_watchdog_fn, daemon=True, name="meshcode-orphan-watchdog")
|
|
1833
|
+
wd_thread.start()
|
|
1834
|
+
|
|
1835
|
+
log.info(f"lifespan started — Realtime + heartbeat thread + orphan watchdog active for {AGENT_NAME}")
|
|
1730
1836
|
# Enable session recording in backend.py (hot-reloadable)
|
|
1731
1837
|
try:
|
|
1732
1838
|
be.enable_recording(_get_api_key(), _PROJECT_ID, AGENT_NAME, _SESSION_ID)
|
|
@@ -1844,14 +1950,7 @@ async def meshcode_debug_sleep(seconds: int = 30) -> Dict[str, Any]:
|
|
|
1844
1950
|
def meshcode_send(to: str, message: Any, in_reply_to: Optional[str] = None,
|
|
1845
1951
|
sensitive: bool = False, encrypted: bool = False,
|
|
1846
1952
|
type: Optional[str] = None) -> Dict[str, Any]:
|
|
1847
|
-
"""Send message. Use "agent@meshwork" for cross-mesh. sensitive=True hides from exports.
|
|
1848
|
-
|
|
1849
|
-
Optional `type` argument (mig 233 typed catalog): one of report, ask,
|
|
1850
|
-
proposal, status, blocker, decision, broadcast, ack, msg, dm, etc.
|
|
1851
|
-
When passed, the SDK soft-validates the payload against the schema
|
|
1852
|
-
in mc_message_schema(p_type) and logs a warning on mismatch — never
|
|
1853
|
-
refuses the send (per memory feedback_meshcode_wait_mark_read).
|
|
1854
|
-
"""
|
|
1953
|
+
"""Send message. Use "agent@meshwork" for cross-mesh. sensitive=True hides from exports. encrypted=True for secrets (AES-256-GCM). type= optional typed-catalog tag; soft-validates, never refuses."""
|
|
1855
1954
|
if not to or not to.strip():
|
|
1856
1955
|
return {"error": "recipient 'to' cannot be empty"}
|
|
1857
1956
|
to = to.strip()
|
|
@@ -2360,20 +2459,7 @@ def _try_auto_claim_task() -> Optional[Dict[str, str]]:
|
|
|
2360
2459
|
@mcp.tool()
|
|
2361
2460
|
@with_working_status
|
|
2362
2461
|
async def meshcode_wait(timeout_seconds: int = 20, include_acks: bool = False) -> Dict[str, Any]:
|
|
2363
|
-
"""Block until a mesh message arrives or a task needs attention.
|
|
2364
|
-
|
|
2365
|
-
INTERNAL LOOP: This function loops internally and only returns when
|
|
2366
|
-
there is real work (message, task, or done signal). The agent NEVER
|
|
2367
|
-
needs to decide whether to call meshcode_wait() again — it just stays
|
|
2368
|
-
blocked here until something happens. This prevents agents from
|
|
2369
|
-
accidentally using ScheduleWakeup or exiting the loop.
|
|
2370
|
-
|
|
2371
|
-
Args:
|
|
2372
|
-
timeout_seconds: Max wait time per poll cycle (default 20, hard cap 20).
|
|
2373
|
-
Short cap keeps the outer tool call bounded so the user can press
|
|
2374
|
-
ESC in Claude Code without killing the MCP server — the inner
|
|
2375
|
-
loop continues polling across cycles at zero token cost.
|
|
2376
|
-
"""
|
|
2462
|
+
"""Block until a mesh message arrives or a task needs attention. Loops internally; agent never decides to re-call. timeout_seconds: per-cycle cap (default+max 20)."""
|
|
2377
2463
|
global _IN_WAIT, _CONSECUTIVE_IDLE_SECONDS, _LAST_SEEN_TS
|
|
2378
2464
|
|
|
2379
2465
|
# PRODUCT RULE 1: If agent has open tasks, refuse to wait. Work first.
|
|
@@ -2409,12 +2495,17 @@ async def meshcode_wait(timeout_seconds: int = 20, include_acks: bool = False) -
|
|
|
2409
2495
|
split = _split_messages(deduped)
|
|
2410
2496
|
# Only refuse for real messages — ack-only batches should not block wait
|
|
2411
2497
|
if split["messages"] or split["done_signals"]:
|
|
2412
|
-
|
|
2498
|
+
resp = {
|
|
2413
2499
|
"refused": True,
|
|
2414
2500
|
"reason": f"You have {split['count']} unread messages. Process them before waiting.",
|
|
2415
2501
|
"got_message": True,
|
|
2416
2502
|
**split,
|
|
2417
2503
|
}
|
|
2504
|
+
# BE-S5.11: surface explicit must_exit when sleep authorized.
|
|
2505
|
+
if split["done_signals"]:
|
|
2506
|
+
resp["must_exit"] = True
|
|
2507
|
+
resp["exit_reason"] = "sleep authorization received — set status=sleeping and end session"
|
|
2508
|
+
return resp
|
|
2418
2509
|
# Ack-only batch — fall through to wait loop
|
|
2419
2510
|
except Exception:
|
|
2420
2511
|
pass
|
|
@@ -2526,6 +2617,13 @@ async def meshcode_wait(timeout_seconds: int = 20, include_acks: bool = False) -
|
|
|
2526
2617
|
result["memory_hints"] = _hints
|
|
2527
2618
|
except Exception:
|
|
2528
2619
|
pass
|
|
2620
|
+
# BE-S5.11: surface must_exit + exit_reason at top level of inner-loop
|
|
2621
|
+
# results too, so commander broadcasts (which arrive via the inner
|
|
2622
|
+
# path, not PRODUCT RULE 2) authorize wait-loop exit consistently.
|
|
2623
|
+
if isinstance(result, dict) and result.get("done_signals"):
|
|
2624
|
+
result.setdefault("must_exit", True)
|
|
2625
|
+
result.setdefault("exit_reason",
|
|
2626
|
+
"sleep authorization received — set status=sleeping and end session")
|
|
2529
2627
|
return result
|
|
2530
2628
|
finally:
|
|
2531
2629
|
_IN_WAIT = False
|
|
@@ -2771,16 +2869,7 @@ def meshcode_done(reason: str) -> Dict[str, Any]:
|
|
|
2771
2869
|
@mcp.tool()
|
|
2772
2870
|
@with_working_status
|
|
2773
2871
|
def meshcode_check(include_acks: bool = False, since: Optional[str] = None, mark_read: bool = False) -> Dict[str, Any]:
|
|
2774
|
-
"""Peek
|
|
2775
|
-
|
|
2776
|
-
Args:
|
|
2777
|
-
include_acks: Include ack messages in response.
|
|
2778
|
-
since: ISO-8601 timestamp. Only return messages newer than this.
|
|
2779
|
-
Use meshcode_remember("last_seen", ts) to persist across sessions.
|
|
2780
|
-
mark_read: When True, consume messages (mark as read in DB) instead of
|
|
2781
|
-
just peeking. Useful during boot when meshcode_wait() refuses
|
|
2782
|
-
to run because of open tasks.
|
|
2783
|
-
"""
|
|
2872
|
+
"""Peek inbox (non-destructive). since=ISO ts (older skipped). mark_read=True consumes (use during boot when wait refuses on open tasks)."""
|
|
2784
2873
|
global _LAST_SEEN_TS
|
|
2785
2874
|
pending = be.count_pending(_PROJECT_ID, AGENT_NAME, api_key=_get_api_key())
|
|
2786
2875
|
# Peek at realtime buffer WITHOUT draining — check is non-destructive
|
|
@@ -4235,6 +4324,12 @@ def _auto_update() -> None:
|
|
|
4235
4324
|
log.debug("[meshcode] Auto-update disabled (MESHCODE_AUTO_UPDATE=0)")
|
|
4236
4325
|
return
|
|
4237
4326
|
if os.environ.get("MESHCODE_UPDATED") == "1":
|
|
4327
|
+
# Sentinel was set by the parent process right before it execv'd
|
|
4328
|
+
# into us. Clear it now — single-use per upgrade. Without this, any
|
|
4329
|
+
# subprocess we spawn (or a downstream re-exec triggered by some
|
|
4330
|
+
# other code path) inherits MESHCODE_UPDATED=1 and skips the next
|
|
4331
|
+
# legitimate upgrade check. Per task 8d5eed7a.
|
|
4332
|
+
os.environ.pop("MESHCODE_UPDATED", None)
|
|
4238
4333
|
return
|
|
4239
4334
|
|
|
4240
4335
|
import subprocess
|
|
@@ -4293,29 +4388,82 @@ def _auto_update() -> None:
|
|
|
4293
4388
|
log.debug(f"[meshcode] Auto-update failed: {e}")
|
|
4294
4389
|
return
|
|
4295
4390
|
|
|
4296
|
-
# 4.
|
|
4297
|
-
#
|
|
4298
|
-
|
|
4299
|
-
|
|
4300
|
-
|
|
4301
|
-
|
|
4302
|
-
#
|
|
4303
|
-
|
|
4391
|
+
# 4. Re-exec so the upgraded code loads in THIS launch, not the next one.
|
|
4392
|
+
#
|
|
4393
|
+
# Unix (MCP + CLI mode): os.execv preserves the PID and stdio fds, so
|
|
4394
|
+
# Claude Code's pipe to us stays open. We're called from run_server()
|
|
4395
|
+
# BEFORE mcp.run() — no MCP protocol state has been negotiated yet, so
|
|
4396
|
+
# the handshake just happens with the new process image. This was
|
|
4397
|
+
# previously gated behind "NEVER re-exec in MCP mode" but field
|
|
4398
|
+
# evidence (Samuel 2026-05-04: "agente debe salir en la versión más
|
|
4399
|
+
# actualizada") shows the gate forces a two-launch upgrade cycle —
|
|
4400
|
+
# bug fixes shipped to PyPI never reach users on first relaunch.
|
|
4401
|
+
#
|
|
4402
|
+
# Windows: subprocess.Popen + sys.exit(0) on the parent DOES close the
|
|
4403
|
+
# stdio pipe to Claude Code (different PID). Keep the "next boot" path
|
|
4404
|
+
# there; ship a Windows execv replacement separately if it matters.
|
|
4405
|
+
print(f"[meshcode] Updated {current} → {latest}, re-exec'ing to load new code in this launch...", file=sys.stderr)
|
|
4304
4406
|
os.environ["MESHCODE_UPDATED"] = "1"
|
|
4305
4407
|
try:
|
|
4306
4408
|
if sys.platform == "win32":
|
|
4307
|
-
|
|
4308
|
-
|
|
4309
|
-
|
|
4310
|
-
|
|
4311
|
-
os.execv(sys.executable, [sys.executable] + sys.argv)
|
|
4409
|
+
# Windows can't preserve stdio across a re-exec. Defer to next boot.
|
|
4410
|
+
print(f"[meshcode] Windows MCP mode: new code loads on next boot.", file=sys.stderr)
|
|
4411
|
+
return
|
|
4412
|
+
os.execv(sys.executable, [sys.executable] + sys.argv)
|
|
4312
4413
|
except Exception as e:
|
|
4313
4414
|
log.debug(f"[meshcode] Re-exec failed: {e}, continuing with old version")
|
|
4314
4415
|
|
|
4315
4416
|
|
|
4417
|
+
def _check_boot_version_drift():
|
|
4418
|
+
"""Compare pip-installed version (on disk) vs imported __version__.
|
|
4419
|
+
|
|
4420
|
+
Returns (installed, loaded) tuple if they differ, else None. Used as a
|
|
4421
|
+
canary for "auto-update wrote new code to disk but our running process
|
|
4422
|
+
is still the old version" — should be unreachable on Unix after the
|
|
4423
|
+
2.10.93 execv fix, but the empirical check guards against:
|
|
4424
|
+
- path-precedence regressions (multiple meshcode installs)
|
|
4425
|
+
- Windows MCP mode (we deliberately defer there)
|
|
4426
|
+
- execv failures that fall through to the old version
|
|
4427
|
+
Best-effort: must not raise.
|
|
4428
|
+
"""
|
|
4429
|
+
try:
|
|
4430
|
+
from importlib.metadata import version as _md_version
|
|
4431
|
+
from meshcode import __version__ as _loaded
|
|
4432
|
+
_installed = _md_version("meshcode")
|
|
4433
|
+
if _installed != _loaded:
|
|
4434
|
+
return (_installed, _loaded)
|
|
4435
|
+
except Exception:
|
|
4436
|
+
pass
|
|
4437
|
+
return None
|
|
4438
|
+
|
|
4439
|
+
|
|
4316
4440
|
def run_server():
|
|
4317
4441
|
"""Start the MCP server on stdio (default for Claude Code)."""
|
|
4318
4442
|
_auto_update()
|
|
4443
|
+
# Telemetry canary: surface any installed/loaded version drift loudly
|
|
4444
|
+
# to stderr so it shows up in Claude Code's MCP server output. The first
|
|
4445
|
+
# heartbeat carries the loaded version to mc_agents; this stderr line is
|
|
4446
|
+
# the human-visible signal that auto-update did NOT land cleanly.
|
|
4447
|
+
_drift = _check_boot_version_drift()
|
|
4448
|
+
if _drift is not None:
|
|
4449
|
+
_installed, _loaded = _drift
|
|
4450
|
+
print(
|
|
4451
|
+
f"[meshcode] WARN boot_version_drift: installed={_installed} loaded={_loaded} "
|
|
4452
|
+
f"— pip has a newer build on disk than what's running in this process. "
|
|
4453
|
+
f"Restart should pick it up. If it persists, check sys.path precedence.",
|
|
4454
|
+
file=sys.stderr,
|
|
4455
|
+
)
|
|
4456
|
+
# Stash for the heartbeat thread to relay to commander on first beat.
|
|
4457
|
+
os.environ["_MESHCODE_BOOT_DRIFT"] = f"{_installed}->{_loaded}"
|
|
4458
|
+
if sys.platform == "win32":
|
|
4459
|
+
# Windows can't execv-preserve stdio; auto-update defers to next boot.
|
|
4460
|
+
# Always announce so Windows users know why their first launch may
|
|
4461
|
+
# still show an older version even with PyPI updated.
|
|
4462
|
+
print(
|
|
4463
|
+
"[meshcode] note: Windows defers auto-update to next launch "
|
|
4464
|
+
"(execv preserves Claude Code's stdio pipe only on Unix).",
|
|
4465
|
+
file=sys.stderr,
|
|
4466
|
+
)
|
|
4319
4467
|
print(
|
|
4320
4468
|
f"[meshcode-mcp] Starting server for {AGENT_NAME}@{PROJECT_NAME}",
|
|
4321
4469
|
file=sys.stderr,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: meshcode
|
|
3
|
-
Version: 2.10.
|
|
3
|
+
Version: 2.10.94
|
|
4
4
|
Summary: Real-time communication between AI agents — Supabase-backed CLI
|
|
5
5
|
Author-email: MeshCode <hello@meshcode.io>
|
|
6
6
|
License: MIT
|
|
@@ -422,6 +422,11 @@ $env:MESHCODE_PROJECT_ID="your-project-uuid" # Windows PowerShell
|
|
|
422
422
|
|
|
423
423
|
You can also run `meshcode doctor` (v2.10.41+) to diagnose stale paths, missing dependencies, and config issues across all your workspaces.
|
|
424
424
|
|
|
425
|
+
**9b. Windows: first relaunch shows the old version after a PyPI upgrade**
|
|
426
|
+
Auto-update on Unix uses `os.execv` to swap the running Python image in-place — one launch, latest code (v2.10.93+). On Windows this isn't possible: an `os.execv` replacement breaks Claude Code's stdio pipe to the MCP child (the new process has a different PID). Meshcode therefore installs the new version but defers the swap to the **next** launch on Windows. Expect: `[meshcode] note: Windows defers auto-update to next launch ...` on stderr, then close and re-open Claude Code once to get the new code. To force-load now, run `pip install --upgrade meshcode` manually before launching Claude Code.
|
|
427
|
+
|
|
428
|
+
If the dashboard tags an agent as having `boot_version_drift`, that means pip wrote the new version to disk but our running process is still the old one — typically benign on Windows (next launch fixes it) and worth investigating on Unix (path-precedence regression).
|
|
429
|
+
|
|
425
430
|
**10. `MCP server failed to start` in the Claude Code `/mcp` panel**
|
|
426
431
|
Run `claude --debug` to see the underlying error. Nine times out of ten it's a stale or missing key — run `meshcode login mc_xxx` again.
|
|
427
432
|
|