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