meshcode 2.11.48__tar.gz → 2.11.54__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {meshcode-2.11.48 → meshcode-2.11.54}/PKG-INFO +1 -1
- {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/__init__.py +1 -1
- {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/meshcode_mcp/server.py +464 -2
- meshcode-2.11.54/meshcode/quickstart.py +231 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/meshcode.egg-info/PKG-INFO +1 -1
- meshcode-2.11.54/meshcode.egg-info/SOURCES.txt +83 -0
- meshcode-2.11.54/meshcode.egg-info/top_level.txt +1 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/pyproject.toml +1 -1
- meshcode-2.11.54/tests/test_autonomous_closegap_1.py +164 -0
- meshcode-2.11.54/tests/test_autonomous_closegap_2.py +210 -0
- meshcode-2.11.54/tests/test_autonomous_closegap_3.py +163 -0
- meshcode-2.11.54/tests/test_autonomous_prompt_inject.py +126 -0
- meshcode-2.11.48/meshcode/ascii_art.py +0 -638
- meshcode-2.11.48/meshcode/cli.py +0 -42
- meshcode-2.11.48/meshcode/compat.py +0 -174
- meshcode-2.11.48/meshcode/error_hints.py +0 -74
- meshcode-2.11.48/meshcode/exceptions.py +0 -52
- meshcode-2.11.48/meshcode/invites.py +0 -406
- meshcode-2.11.48/meshcode/launcher.py +0 -353
- meshcode-2.11.48/meshcode/launcher_install.py +0 -414
- meshcode-2.11.48/meshcode/meshcode_mcp/__init__.py +0 -22
- meshcode-2.11.48/meshcode/meshcode_mcp/__main__.py +0 -62
- meshcode-2.11.48/meshcode/meshcode_mcp/test_backend.py +0 -86
- meshcode-2.11.48/meshcode/meshcode_mcp/test_realtime.py +0 -95
- meshcode-2.11.48/meshcode/meshcode_mcp/test_server_wrapper.py +0 -117
- meshcode-2.11.48/meshcode/preferences.py +0 -260
- meshcode-2.11.48/meshcode/protocol_v2.py +0 -129
- meshcode-2.11.48/meshcode/quickstart.py +0 -148
- meshcode-2.11.48/meshcode/secrets.py +0 -365
- meshcode-2.11.48/meshcode/supervisor.py +0 -186
- meshcode-2.11.48/meshcode/upload.py +0 -125
- meshcode-2.11.48/meshcode-backend-wt/comms_v4.py +0 -1941
- meshcode-2.11.48/meshcode-backend-wt/meshcode/__init__.py +0 -82
- meshcode-2.11.48/meshcode-backend-wt/meshcode/comms_v4.py +0 -3563
- meshcode-2.11.48/meshcode-backend-wt/meshcode/meshcode_mcp/backend.py +0 -1261
- meshcode-2.11.48/meshcode-backend-wt/meshcode/meshcode_mcp/realtime.py +0 -460
- meshcode-2.11.48/meshcode-backend-wt/meshcode/meshcode_mcp/server.py +0 -4117
- meshcode-2.11.48/meshcode-backend-wt/meshcode/quickstart.py +0 -148
- meshcode-2.11.48/meshcode-backend-wt/meshcode/run_agent.py +0 -958
- meshcode-2.11.48/meshcode-backend-wt/meshcode/self_update.py +0 -345
- meshcode-2.11.48/meshcode-backend-wt/meshcode/setup_clients.py +0 -926
- meshcode-2.11.48/meshcode-backend-wt/scripts/sentinel.py +0 -257
- meshcode-2.11.48/meshcode-backend-wt/tests/test_rpc_migrations.py +0 -387
- meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/__init__.py +0 -82
- meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/ascii_art.py +0 -638
- meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/cli.py +0 -42
- meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/comms_v4.py +0 -3563
- meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/compat.py +0 -174
- meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/error_hints.py +0 -74
- meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/exceptions.py +0 -52
- meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/invites.py +0 -406
- meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/launcher.py +0 -353
- meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/launcher_install.py +0 -414
- meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/__init__.py +0 -22
- meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/__main__.py +0 -62
- meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/backend.py +0 -1261
- meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/realtime.py +0 -460
- meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/server.py +0 -4117
- meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_backend.py +0 -86
- meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_realtime.py +0 -95
- meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/meshcode_mcp/test_server_wrapper.py +0 -117
- meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/preferences.py +0 -260
- meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/protocol_v2.py +0 -129
- meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/quickstart.py +0 -148
- meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/run_agent.py +0 -958
- meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/secrets.py +0 -365
- meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/self_update.py +0 -345
- meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/setup_clients.py +0 -926
- meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/supervisor.py +0 -186
- meshcode-2.11.48/meshcode-noun-wt/build/lib/meshcode/upload.py +0 -125
- meshcode-2.11.48/meshcode-noun-wt/comms_v4.py +0 -1941
- meshcode-2.11.48/meshcode-noun-wt/meshcode/__init__.py +0 -82
- meshcode-2.11.48/meshcode-noun-wt/meshcode/ascii_art.py +0 -638
- meshcode-2.11.48/meshcode-noun-wt/meshcode/cli.py +0 -42
- meshcode-2.11.48/meshcode-noun-wt/meshcode/comms_v4.py +0 -3563
- meshcode-2.11.48/meshcode-noun-wt/meshcode/compat.py +0 -174
- meshcode-2.11.48/meshcode-noun-wt/meshcode/error_hints.py +0 -74
- meshcode-2.11.48/meshcode-noun-wt/meshcode/exceptions.py +0 -52
- meshcode-2.11.48/meshcode-noun-wt/meshcode/invites.py +0 -406
- meshcode-2.11.48/meshcode-noun-wt/meshcode/launcher.py +0 -353
- meshcode-2.11.48/meshcode-noun-wt/meshcode/launcher_install.py +0 -414
- meshcode-2.11.48/meshcode-noun-wt/meshcode/meshcode_mcp/__init__.py +0 -22
- meshcode-2.11.48/meshcode-noun-wt/meshcode/meshcode_mcp/__main__.py +0 -62
- meshcode-2.11.48/meshcode-noun-wt/meshcode/meshcode_mcp/backend.py +0 -1261
- meshcode-2.11.48/meshcode-noun-wt/meshcode/meshcode_mcp/realtime.py +0 -460
- meshcode-2.11.48/meshcode-noun-wt/meshcode/meshcode_mcp/server.py +0 -4117
- meshcode-2.11.48/meshcode-noun-wt/meshcode/meshcode_mcp/test_backend.py +0 -86
- meshcode-2.11.48/meshcode-noun-wt/meshcode/meshcode_mcp/test_realtime.py +0 -95
- meshcode-2.11.48/meshcode-noun-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -117
- meshcode-2.11.48/meshcode-noun-wt/meshcode/preferences.py +0 -260
- meshcode-2.11.48/meshcode-noun-wt/meshcode/protocol_v2.py +0 -129
- meshcode-2.11.48/meshcode-noun-wt/meshcode/quickstart.py +0 -148
- meshcode-2.11.48/meshcode-noun-wt/meshcode/run_agent.py +0 -958
- meshcode-2.11.48/meshcode-noun-wt/meshcode/secrets.py +0 -365
- meshcode-2.11.48/meshcode-noun-wt/meshcode/self_update.py +0 -345
- meshcode-2.11.48/meshcode-noun-wt/meshcode/setup_clients.py +0 -926
- meshcode-2.11.48/meshcode-noun-wt/meshcode/supervisor.py +0 -186
- meshcode-2.11.48/meshcode-noun-wt/meshcode/upload.py +0 -125
- meshcode-2.11.48/meshcode-noun-wt/scripts/sentinel.py +0 -257
- meshcode-2.11.48/meshcode-noun-wt/tests/test_core.py +0 -216
- meshcode-2.11.48/meshcode-noun-wt/tests/test_cross_agent_messaging.py +0 -366
- meshcode-2.11.48/meshcode-noun-wt/tests/test_esc_deaf_state.py +0 -361
- meshcode-2.11.48/meshcode-noun-wt/tests/test_exceptions.py +0 -107
- meshcode-2.11.48/meshcode-noun-wt/tests/test_mark_read_batch.py +0 -200
- meshcode-2.11.48/meshcode-noun-wt/tests/test_migration_integrity.py +0 -176
- meshcode-2.11.48/meshcode-noun-wt/tests/test_realtime_event_freshness.py +0 -236
- meshcode-2.11.48/meshcode-noun-wt/tests/test_rls_cross_tenant.py +0 -255
- meshcode-2.11.48/meshcode-noun-wt/tests/test_rpc_migrations.py +0 -387
- meshcode-2.11.48/meshcode-noun-wt/tests/test_security_regressions.py +0 -171
- meshcode-2.11.48/meshcode-noun-wt/tests/test_sentinel.py +0 -148
- meshcode-2.11.48/meshcode-noun-wt/tests/test_status_enum_coverage.py +0 -231
- meshcode-2.11.48/meshcode-tasks-wt/comms_v4.py +0 -1941
- meshcode-2.11.48/meshcode-tasks-wt/meshcode/__init__.py +0 -82
- meshcode-2.11.48/meshcode-tasks-wt/meshcode/ascii_art.py +0 -638
- meshcode-2.11.48/meshcode-tasks-wt/meshcode/cli.py +0 -42
- meshcode-2.11.48/meshcode-tasks-wt/meshcode/comms_v4.py +0 -3563
- meshcode-2.11.48/meshcode-tasks-wt/meshcode/compat.py +0 -174
- meshcode-2.11.48/meshcode-tasks-wt/meshcode/error_hints.py +0 -74
- meshcode-2.11.48/meshcode-tasks-wt/meshcode/exceptions.py +0 -52
- meshcode-2.11.48/meshcode-tasks-wt/meshcode/invites.py +0 -406
- meshcode-2.11.48/meshcode-tasks-wt/meshcode/launcher.py +0 -353
- meshcode-2.11.48/meshcode-tasks-wt/meshcode/launcher_install.py +0 -414
- meshcode-2.11.48/meshcode-tasks-wt/meshcode/meshcode_mcp/__init__.py +0 -22
- meshcode-2.11.48/meshcode-tasks-wt/meshcode/meshcode_mcp/__main__.py +0 -62
- meshcode-2.11.48/meshcode-tasks-wt/meshcode/meshcode_mcp/backend.py +0 -1261
- meshcode-2.11.48/meshcode-tasks-wt/meshcode/meshcode_mcp/realtime.py +0 -460
- meshcode-2.11.48/meshcode-tasks-wt/meshcode/meshcode_mcp/server.py +0 -4117
- meshcode-2.11.48/meshcode-tasks-wt/meshcode/meshcode_mcp/test_backend.py +0 -86
- meshcode-2.11.48/meshcode-tasks-wt/meshcode/meshcode_mcp/test_realtime.py +0 -95
- meshcode-2.11.48/meshcode-tasks-wt/meshcode/meshcode_mcp/test_server_wrapper.py +0 -117
- meshcode-2.11.48/meshcode-tasks-wt/meshcode/preferences.py +0 -260
- meshcode-2.11.48/meshcode-tasks-wt/meshcode/protocol_v2.py +0 -129
- meshcode-2.11.48/meshcode-tasks-wt/meshcode/quickstart.py +0 -148
- meshcode-2.11.48/meshcode-tasks-wt/meshcode/run_agent.py +0 -958
- meshcode-2.11.48/meshcode-tasks-wt/meshcode/secrets.py +0 -365
- meshcode-2.11.48/meshcode-tasks-wt/meshcode/self_update.py +0 -345
- meshcode-2.11.48/meshcode-tasks-wt/meshcode/setup_clients.py +0 -926
- meshcode-2.11.48/meshcode-tasks-wt/meshcode/supervisor.py +0 -186
- meshcode-2.11.48/meshcode-tasks-wt/meshcode/upload.py +0 -125
- meshcode-2.11.48/meshcode-tasks-wt/scripts/sentinel.py +0 -257
- meshcode-2.11.48/meshcode-tasks-wt/tests/test_core.py +0 -216
- meshcode-2.11.48/meshcode-tasks-wt/tests/test_cross_agent_messaging.py +0 -366
- meshcode-2.11.48/meshcode-tasks-wt/tests/test_esc_deaf_state.py +0 -361
- meshcode-2.11.48/meshcode-tasks-wt/tests/test_exceptions.py +0 -107
- meshcode-2.11.48/meshcode-tasks-wt/tests/test_mark_read_batch.py +0 -200
- meshcode-2.11.48/meshcode-tasks-wt/tests/test_migration_integrity.py +0 -176
- meshcode-2.11.48/meshcode-tasks-wt/tests/test_realtime_event_freshness.py +0 -236
- meshcode-2.11.48/meshcode-tasks-wt/tests/test_rls_cross_tenant.py +0 -255
- meshcode-2.11.48/meshcode-tasks-wt/tests/test_rpc_migrations.py +0 -387
- meshcode-2.11.48/meshcode-tasks-wt/tests/test_security_regressions.py +0 -171
- meshcode-2.11.48/meshcode-tasks-wt/tests/test_sentinel.py +0 -148
- meshcode-2.11.48/meshcode-tasks-wt/tests/test_status_enum_coverage.py +0 -231
- meshcode-2.11.48/meshcode.egg-info/PKG-INFO 2 +0 -452
- meshcode-2.11.48/meshcode.egg-info/SOURCES 2.txt +0 -229
- meshcode-2.11.48/meshcode.egg-info/SOURCES.txt +0 -235
- meshcode-2.11.48/meshcode.egg-info/dependency_links 2.txt +0 -1
- meshcode-2.11.48/meshcode.egg-info/entry_points 2.txt +0 -3
- meshcode-2.11.48/meshcode.egg-info/requires 2.txt +0 -13
- meshcode-2.11.48/meshcode.egg-info/top_level 2.txt +0 -4
- meshcode-2.11.48/meshcode.egg-info/top_level.txt +0 -4
- meshcode-2.11.48/tests/test_core.py +0 -216
- meshcode-2.11.48/tests/test_cross_agent_messaging.py +0 -366
- meshcode-2.11.48/tests/test_esc_deaf_state.py +0 -361
- meshcode-2.11.48/tests/test_exceptions.py +0 -107
- meshcode-2.11.48/tests/test_mark_read_batch.py +0 -200
- meshcode-2.11.48/tests/test_migration_integrity.py +0 -176
- meshcode-2.11.48/tests/test_realtime_event_freshness.py +0 -236
- meshcode-2.11.48/tests/test_rls_cross_tenant.py +0 -255
- meshcode-2.11.48/tests/test_security_regressions.py +0 -171
- meshcode-2.11.48/tests/test_sentinel.py +0 -148
- meshcode-2.11.48/tests/test_status_enum_coverage.py +0 -231
- {meshcode-2.11.48 → meshcode-2.11.54}/README.md +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/__main__.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/_stop_hook_template.py +0 -0
- {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/ascii_art.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/atomic_push.py +0 -0
- {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/cli.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/comms_v4.py +0 -0
- {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/compat.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/daemon.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/date_parse.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/doctor.py +0 -0
- {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/error_hints.py +0 -0
- {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/exceptions.py +0 -0
- {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/invites.py +0 -0
- {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/launcher.py +0 -0
- {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/launcher_install.py +0 -0
- {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/meshcode_mcp/__init__.py +0 -0
- {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/meshcode_mcp/__main__.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/meshcode_mcp/backend.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/meshcode_mcp/realtime.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/meshcode_mcp/sleep_signals.py +0 -0
- {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/meshcode_mcp/test_backend.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/meshcode_mcp/test_boot_timing.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/meshcode_mcp/test_install_guard.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/meshcode_mcp/test_prefs_claude_version.py +0 -0
- {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/meshcode_mcp/test_realtime.py +0 -0
- {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/meshcode_mcp/test_server_wrapper.py +0 -0
- {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/preferences.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/protocol_handler.py +0 -0
- {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/protocol_v2.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/rpc_allowlist.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/run_agent.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/scripts/check_secrets.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/scripts/race_rate_harness.py +0 -0
- {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/secrets.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/self_update.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/meshcode/setup_clients.py +0 -0
- {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/supervisor.py +0 -0
- {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/meshcode/upload.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/meshcode.egg-info/dependency_links.txt +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/meshcode.egg-info/entry_points.txt +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/meshcode.egg-info/requires.txt +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/setup.cfg +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_auto_update_hardening.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_boot_bug_regression.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_color_truecolor.py +0 -0
- {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/tests/test_core.py +0 -0
- {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/tests/test_cross_agent_messaging.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_date_parse.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_doctor.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_epistemic_v1_python_sdk.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_epistemic_v1_stop_conditions.py +0 -0
- {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/tests/test_esc_deaf_state.py +0 -0
- {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/tests/test_exceptions.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_init_device_code.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_install_guard.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_lease_sigterm_release.py +0 -0
- {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/tests/test_mark_read_batch.py +0 -0
- {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/tests/test_migration_integrity.py +0 -0
- {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/tests/test_realtime_event_freshness.py +0 -0
- {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/tests/test_rls_cross_tenant.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_rpc_grants.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_rpc_migrations.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_run_agent_dry_run.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_run_agent_no_server_import.py +0 -0
- {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/tests/test_security_regressions.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_self_update_user_site.py +0 -0
- {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/tests/test_sentinel.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_setup_path.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_sleep_signals.py +0 -0
- {meshcode-2.11.48/meshcode-backend-wt → meshcode-2.11.54}/tests/test_status_enum_coverage.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_stay_on_loop_hook.py +0 -0
- {meshcode-2.11.48 → meshcode-2.11.54}/tests/test_wait_open_tasks_contradiction.py +0 -0
|
@@ -16,7 +16,7 @@ import hashlib as _hashlib
|
|
|
16
16
|
import traceback as _traceback
|
|
17
17
|
from collections import deque
|
|
18
18
|
from contextlib import asynccontextmanager
|
|
19
|
-
from typing import Any, Dict, List, Optional, Union
|
|
19
|
+
from typing import Any, Dict, List, Optional, Set, Union
|
|
20
20
|
|
|
21
21
|
from meshcode import __version__ as _SDK_VERSION
|
|
22
22
|
|
|
@@ -1707,6 +1707,11 @@ Then: set_status(online,ready) → check() → tasks() → auto_wake() → statu
|
|
|
1707
1707
|
COMMUNICATE BY CALLING TOOLS, not by thinking aloud. Cross-mesh: send(to="agent@meshwork"). Reference docs (memory/scratchpad/account ops) → recall agent_protocol_quick_ref when needed.
|
|
1708
1708
|
|
|
1709
1709
|
DECISIONS: when you face a non-trivial pick (architectural choice, risky migration, scope decision), call meshcode_decide(decision, alternatives, evidence_refs) BEFORE acting — returns a trace_id. Call meshcode_outcome(trace_id, outcome, outcome_eval) when the result becomes observable. Use meshcode_explain(memory_key=…) or (decision_substring=…) to surface prior traces + open contradictions. Memory hints with open_contradictions take a -0.2 ranking penalty — treat as a flag, not a veto.
|
|
1710
|
+
|
|
1711
|
+
ESCALATION RULES:
|
|
1712
|
+
- MUST escalate to human: destructive ops (drop/delete/rm -rf), irreversible changes (force-push main, mig without rollback), product-direction decisions (what feature next), legal/compliance, security (auth/secrets/PII), spend > $50 USD.
|
|
1713
|
+
- MUST NOT escalate: code style, implementation approach, naming, test strategy, migration ordering, refactor scope, library choice, which agent does what.
|
|
1714
|
+
- GREY ZONE: scope changes, deadline trade-offs, cross-mesh assignments → try meshcode_call(to="mesh-commander", function="advise") first; escalate to human only if commander offline or responds "escalate".
|
|
1710
1715
|
"""
|
|
1711
1716
|
# Inject commander protocol if this agent is a leader
|
|
1712
1717
|
is_leader = _is_leader_agent()
|
|
@@ -1726,6 +1731,22 @@ COMMANDER PROTOCOL (you are the team lead):
|
|
|
1726
1731
|
- PROACTIVE MAINTENANCE: call meshcode_auto_wake() on session start and after
|
|
1727
1732
|
completing sprints. It scans meshwork health (stale agents, task backlog,
|
|
1728
1733
|
empty memories). Turn suggestions into real tasks — quality over quantity.
|
|
1734
|
+
|
|
1735
|
+
ADVISE HANDLER (mesh-first decision protocol):
|
|
1736
|
+
When an agent calls meshcode_call(to=you, function="advise", args={question, context}), you are the first line of decision support. Respond with one of:
|
|
1737
|
+
- {"action":"decide","answer":"<your recommendation>","confidence":"high|medium"} — you have enough context to advise. The caller executes your recommendation.
|
|
1738
|
+
- {"action":"escalate","reason":"<why>"} — the question requires human judgment (falls under MUST-escalate criteria: destructive, irreversible, product-direction, legal, security, spend >$50). The caller messages the human.
|
|
1739
|
+
- {"action":"defer","reason":"<why>","suggest":"<alternative agent or resource>"} — you lack context but know who does.
|
|
1740
|
+
Use meshcode_recall_search and meshcode_tasks to inform your answer before responding. Bias toward "decide" — only escalate when the criteria genuinely apply.
|
|
1741
|
+
"""
|
|
1742
|
+
else:
|
|
1743
|
+
base += """
|
|
1744
|
+
AUTONOMOUS MODE — these rules activate when you_are_autonomous=true in your most recent boot/wait/check response:
|
|
1745
|
+
- Cadence: decide → execute → report. No empty acks, no "waiting for approval" for things you can resolve.
|
|
1746
|
+
- Pre-decision context: if a decision depends on prior context, run meshcode_recall_search(keywords) FIRST before choosing.
|
|
1747
|
+
- Mesh-first: before messaging a human (sammybenu/ian/fis), try meshcode_call(to="mesh-commander", function="advise", args={question: "...", context: "..."}) first. Only escalate to human if commander is offline or responds "escalate".
|
|
1748
|
+
- Bias toward action: when you have enough information to decide, decide. Ship and report the outcome — don't ask permission.
|
|
1749
|
+
- Self-awareness: after completing a task, call meshcode_recall_search for related memories and update or consolidate stale ones.
|
|
1729
1750
|
"""
|
|
1730
1751
|
return base
|
|
1731
1752
|
|
|
@@ -2787,6 +2808,18 @@ def meshcode_send(to: Union[str, List[str]], message: Any, in_reply_to: Optional
|
|
|
2787
2808
|
if _payload_len > 8000:
|
|
2788
2809
|
return {"error": f"message too large ({_payload_len} chars). Use meshcode_task_create for long content. Messages must be structured JSON <8000 chars."}
|
|
2789
2810
|
|
|
2811
|
+
# CLOSEGAP-3 (task 69dc5a62): ship-detection auto-checkpoint. If the
|
|
2812
|
+
# outgoing payload is a ship-report (explicit type or canonical
|
|
2813
|
+
# commits+task+version shape) AND references a task currently
|
|
2814
|
+
# claimed/in_progress by this agent, complete it. Runs BEFORE the
|
|
2815
|
+
# actual send so the dashboard reflects the new state at roughly
|
|
2816
|
+
# the same wall-clock as the report drops in the inbox.
|
|
2817
|
+
# Baseline (NOT autonomous-gated). Best-effort; audit via mc_activity.
|
|
2818
|
+
try:
|
|
2819
|
+
_try_auto_complete_on_ship(payload)
|
|
2820
|
+
except Exception:
|
|
2821
|
+
pass
|
|
2822
|
+
|
|
2790
2823
|
# Typed-catalog soft validation (BE-S4.2.sdk / mig 233): if caller
|
|
2791
2824
|
# passes type=, fetch the schema and check required fields. NEVER
|
|
2792
2825
|
# refuses the send — best-effort warn-only. Skips when no type given,
|
|
@@ -3441,6 +3474,363 @@ def _try_auto_claim_task() -> Optional[Dict[str, str]]:
|
|
|
3441
3474
|
return None
|
|
3442
3475
|
|
|
3443
3476
|
|
|
3477
|
+
# AUTONOMOUS-CLOSEGAP-1 (task beadc9e6, Samuel directive 2026-05-26T05:15Z):
|
|
3478
|
+
# heuristic auto-claim for OPEN tasks where assignee is 'auto' or unset,
|
|
3479
|
+
# matched against the local agent's role keywords + registered skills.
|
|
3480
|
+
# Polite: load-gated, opt-in cross-mesh, logged to mc_activity.
|
|
3481
|
+
_REGISTERED_SKILLS: List[str] = [] # populated by meshcode_register_skills
|
|
3482
|
+
|
|
3483
|
+
_HEURISTIC_TOKEN_SPLIT = "[\\s,;/|\\-_().\\[\\]{}]+"
|
|
3484
|
+
_HEURISTIC_STOPWORDS = {
|
|
3485
|
+
"the", "and", "for", "with", "from", "this", "that", "have", "has",
|
|
3486
|
+
"are", "was", "were", "but", "any", "all", "you", "your", "our",
|
|
3487
|
+
"agent", "agents", "task", "tasks", "via", "using", "use", "use",
|
|
3488
|
+
"via", "into", "onto", "out", "off", "per", "general", "purpose",
|
|
3489
|
+
"ai", "assistant", "mcp", "connected", "mc",
|
|
3490
|
+
}
|
|
3491
|
+
|
|
3492
|
+
|
|
3493
|
+
def _agent_keyword_set() -> Set[str]:
|
|
3494
|
+
"""Lowercased keyword set built from agent role + name + registered skills.
|
|
3495
|
+
Drops short/stopword tokens. Used by the auto-claim heuristic."""
|
|
3496
|
+
import re as _re
|
|
3497
|
+
tokens: Set[str] = set()
|
|
3498
|
+
sources = [AGENT_ROLE or "", AGENT_NAME or ""]
|
|
3499
|
+
sources.extend(_REGISTERED_SKILLS or [])
|
|
3500
|
+
for src in sources:
|
|
3501
|
+
for tok in _re.split(_HEURISTIC_TOKEN_SPLIT, str(src).lower()):
|
|
3502
|
+
if len(tok) >= 3 and tok not in _HEURISTIC_STOPWORDS:
|
|
3503
|
+
tokens.add(tok)
|
|
3504
|
+
return tokens
|
|
3505
|
+
|
|
3506
|
+
|
|
3507
|
+
def _heuristic_match_score(task: Dict[str, Any], agent_tokens: Set[str]) -> int:
|
|
3508
|
+
"""Count token-overlap hits across task title + first 200 chars of description.
|
|
3509
|
+
Each registered-skill hit counts double (skill > role keyword)."""
|
|
3510
|
+
if not agent_tokens:
|
|
3511
|
+
return 0
|
|
3512
|
+
blob = (str(task.get("title") or "") + " " + str(task.get("description") or "")[:200]).lower()
|
|
3513
|
+
role_hits = sum(1 for tok in agent_tokens if tok in blob)
|
|
3514
|
+
skill_bonus = sum(1 for s in (_REGISTERED_SKILLS or []) if s and s.lower() in blob)
|
|
3515
|
+
return role_hits + skill_bonus
|
|
3516
|
+
|
|
3517
|
+
|
|
3518
|
+
def _try_auto_claim_open_by_heuristic(max_load: int = 2) -> Optional[Dict[str, Any]]:
|
|
3519
|
+
"""AUTONOMOUS-CLOSEGAP-1: claim one open unowned/auto task whose
|
|
3520
|
+
title + description-head overlaps with this agent's role/skills.
|
|
3521
|
+
|
|
3522
|
+
Polite contract:
|
|
3523
|
+
- skip if agent already holds ``max_load`` or more active claims
|
|
3524
|
+
(configurable via MESHCODE_AUTO_CLAIM_MAX_LOAD)
|
|
3525
|
+
- skip cross-mesh tasks unless MESHCODE_AUTO_CLAIM_CROSS_MESH=1
|
|
3526
|
+
- audit reason logged to mc_activity ("auto_claim_heuristic" event)
|
|
3527
|
+
|
|
3528
|
+
Returns the claimed task summary or None.
|
|
3529
|
+
"""
|
|
3530
|
+
try:
|
|
3531
|
+
api_key = _get_api_key()
|
|
3532
|
+
if not api_key:
|
|
3533
|
+
return None
|
|
3534
|
+
# Single status_filter=None call covers BOTH the load-gate count
|
|
3535
|
+
# (claimed/in_progress rows owned by me) and the candidate filter
|
|
3536
|
+
# (open rows with assignee='auto'/unset). Backend caches.
|
|
3537
|
+
result = be.task_list(api_key, _PROJECT_ID, AGENT_NAME, status_filter=None)
|
|
3538
|
+
if not isinstance(result, dict) or not result.get("ok"):
|
|
3539
|
+
return None
|
|
3540
|
+
tasks = result.get("tasks", []) or []
|
|
3541
|
+
# Load gate — count claims I already hold that aren't done/canceled.
|
|
3542
|
+
_ACTIVE = {"claimed", "in_progress", "in_review"}
|
|
3543
|
+
my_active = sum(
|
|
3544
|
+
1 for t in tasks
|
|
3545
|
+
if t.get("claimed_by") == AGENT_NAME and t.get("status") in _ACTIVE
|
|
3546
|
+
)
|
|
3547
|
+
if my_active >= max_load:
|
|
3548
|
+
log.debug(f"[meshcode] heuristic auto-claim skipped: load={my_active} >= max={max_load}")
|
|
3549
|
+
return None
|
|
3550
|
+
# Polite cross-mesh gate
|
|
3551
|
+
_cross_mesh = os.environ.get("MESHCODE_AUTO_CLAIM_CROSS_MESH", "").strip().lower() in ("1", "true", "yes", "on")
|
|
3552
|
+
candidates: List[Dict[str, Any]] = []
|
|
3553
|
+
for t in tasks:
|
|
3554
|
+
if t.get("status") != "open":
|
|
3555
|
+
continue
|
|
3556
|
+
if t.get("claimed_by"):
|
|
3557
|
+
continue
|
|
3558
|
+
assignee = (t.get("assignee") or "").strip().lower()
|
|
3559
|
+
# 'auto' OR empty (unset) — '*' wildcard is handled by _try_auto_claim_task.
|
|
3560
|
+
if assignee not in ("", "auto"):
|
|
3561
|
+
continue
|
|
3562
|
+
if not _cross_mesh:
|
|
3563
|
+
src_proj = t.get("source_project_id")
|
|
3564
|
+
if src_proj and _PROJECT_ID and src_proj != _PROJECT_ID:
|
|
3565
|
+
continue
|
|
3566
|
+
candidates.append(t)
|
|
3567
|
+
if not candidates:
|
|
3568
|
+
return None
|
|
3569
|
+
agent_tokens = _agent_keyword_set()
|
|
3570
|
+
scored: List[tuple] = []
|
|
3571
|
+
for t in candidates:
|
|
3572
|
+
score = _heuristic_match_score(t, agent_tokens)
|
|
3573
|
+
if score > 0:
|
|
3574
|
+
scored.append((score, t))
|
|
3575
|
+
if not scored:
|
|
3576
|
+
return None
|
|
3577
|
+
_PRIO = {"urgent": 0, "high": 1, "normal": 2, "low": 3}
|
|
3578
|
+
scored.sort(key=lambda st: (-st[0], _PRIO.get(st[1].get("priority", "normal"), 2), st[1].get("created_at", "")))
|
|
3579
|
+
score, target = scored[0]
|
|
3580
|
+
claim_result = be.sb_rpc("mc_task_claim", {
|
|
3581
|
+
"p_api_key": api_key,
|
|
3582
|
+
"p_task_id": target["id"],
|
|
3583
|
+
})
|
|
3584
|
+
if not (isinstance(claim_result, dict) and claim_result.get("ok")):
|
|
3585
|
+
log.debug(f"[meshcode] heuristic claim lost race on {target['id'][:8]}: {claim_result}")
|
|
3586
|
+
return None
|
|
3587
|
+
log.info(f"[meshcode] heuristic auto-claim score={score}: {target['title'][:60]}")
|
|
3588
|
+
try:
|
|
3589
|
+
_log_activity_bg(
|
|
3590
|
+
"auto_claim_heuristic",
|
|
3591
|
+
f"{AGENT_NAME} claimed '{str(target.get('title'))[:80]}' (score={score}, assignee={(target.get('assignee') or 'unset')})",
|
|
3592
|
+
)
|
|
3593
|
+
except Exception:
|
|
3594
|
+
pass
|
|
3595
|
+
return {
|
|
3596
|
+
"id": target["id"][:8],
|
|
3597
|
+
"title": str(target.get("title") or "")[:80],
|
|
3598
|
+
"priority": target.get("priority", "normal"),
|
|
3599
|
+
"match_score": score,
|
|
3600
|
+
"reason": "role+skill keyword overlap",
|
|
3601
|
+
}
|
|
3602
|
+
except Exception as e:
|
|
3603
|
+
log.debug(f"[meshcode] heuristic auto-claim failed: {e}")
|
|
3604
|
+
return None
|
|
3605
|
+
|
|
3606
|
+
|
|
3607
|
+
# CLOSEGAP-2 (task 01bfe6c4): when an agent calls
|
|
3608
|
+
# `meshcode_set_status(working, "<task-hint>")`, surface the matching
|
|
3609
|
+
# claimed task UUID so we can auto-flip it to in_progress via the
|
|
3610
|
+
# existing mc_task_start RPC (mig 349). Eliminates the manual second
|
|
3611
|
+
# call. Baseline (NOT autonomous-gated per Samuel reframe ce506e0d).
|
|
3612
|
+
def _match_claimed_task_for_set_status(task_hint: str) -> Optional[str]:
|
|
3613
|
+
"""Given the free-form `task` arg of `meshcode_set_status(working, ...)`,
|
|
3614
|
+
return the UUID of a single claimed-by-me task that matches, or None.
|
|
3615
|
+
|
|
3616
|
+
Matching rules (deterministic — no guess on ambiguity):
|
|
3617
|
+
1. If hint contains "task <hex>" (the prefix that
|
|
3618
|
+
`meshcode_task_claim` writes), extract and match by id-prefix on
|
|
3619
|
+
my claimed list. Exactly 1 match → return its UUID.
|
|
3620
|
+
2. Else, fuzzy by title substring (either direction). Exactly 1
|
|
3621
|
+
match → return its UUID.
|
|
3622
|
+
3. 0 matches OR >1 matches → return None (don't guess).
|
|
3623
|
+
"""
|
|
3624
|
+
if not task_hint or not task_hint.strip():
|
|
3625
|
+
return None
|
|
3626
|
+
try:
|
|
3627
|
+
import re as _re
|
|
3628
|
+
api_key = _get_api_key()
|
|
3629
|
+
if not api_key:
|
|
3630
|
+
return None
|
|
3631
|
+
listing = be.task_list(api_key, _PROJECT_ID, AGENT_NAME, status_filter=None)
|
|
3632
|
+
if not isinstance(listing, dict) or not listing.get("ok"):
|
|
3633
|
+
return None
|
|
3634
|
+
mine_claimed = [
|
|
3635
|
+
t for t in (listing.get("tasks") or [])
|
|
3636
|
+
if t.get("claimed_by") == AGENT_NAME and t.get("status") == "claimed"
|
|
3637
|
+
]
|
|
3638
|
+
if not mine_claimed:
|
|
3639
|
+
return None
|
|
3640
|
+
# Rule 1: explicit "task <hex>" hint (e.g. "task 01bfe6c4: ...")
|
|
3641
|
+
m = _re.search(r"task\s+([0-9a-f]{4,8}(?:-[0-9a-f]+)?)", task_hint, _re.IGNORECASE)
|
|
3642
|
+
if m:
|
|
3643
|
+
prefix = m.group(1).lower()
|
|
3644
|
+
id_matches = [t["id"] for t in mine_claimed if str(t.get("id") or "").lower().startswith(prefix)]
|
|
3645
|
+
if len(id_matches) == 1:
|
|
3646
|
+
return id_matches[0]
|
|
3647
|
+
# Multiple/zero by prefix → fall through to title matching;
|
|
3648
|
+
# don't return None yet, the title may still disambiguate.
|
|
3649
|
+
# Rule 2: title substring (either direction). 5-char floor avoids
|
|
3650
|
+
# accidentally matching on tiny shared substrings ("the", "task").
|
|
3651
|
+
hint_low = task_hint.lower()
|
|
3652
|
+
title_matches: List[str] = []
|
|
3653
|
+
for t in mine_claimed:
|
|
3654
|
+
title_low = str(t.get("title") or "").lower()
|
|
3655
|
+
if len(title_low) < 5:
|
|
3656
|
+
continue
|
|
3657
|
+
if title_low in hint_low or (len(hint_low) >= 5 and hint_low in title_low):
|
|
3658
|
+
title_matches.append(t["id"])
|
|
3659
|
+
if len(title_matches) == 1:
|
|
3660
|
+
return title_matches[0]
|
|
3661
|
+
return None
|
|
3662
|
+
except Exception:
|
|
3663
|
+
return None
|
|
3664
|
+
|
|
3665
|
+
|
|
3666
|
+
# CLOSEGAP-3 (task 69dc5a62, Samuel reframe ce506e0d): when an agent
|
|
3667
|
+
# sends a ship-report message that references a task it currently
|
|
3668
|
+
# holds in `claimed` or `in_progress`, auto-complete that task with a
|
|
3669
|
+
# coherent summary built from the payload. Same applies on
|
|
3670
|
+
# set_status(status='done', ...) — completes the single in_progress
|
|
3671
|
+
# claim with a status-driven summary. Baseline (NOT autonomous-gated).
|
|
3672
|
+
def _ship_marker_in_payload(payload: Dict[str, Any]) -> bool:
|
|
3673
|
+
"""Detect a ship-report payload. Tight rules to avoid false positives:
|
|
3674
|
+
- explicit type marker: payload.type in {ship, shipped, ship_report}
|
|
3675
|
+
- OR canonical ship-JSON shape: has 'task' AND ('commits' OR 'version')
|
|
3676
|
+
"""
|
|
3677
|
+
if not isinstance(payload, dict):
|
|
3678
|
+
return False
|
|
3679
|
+
ptype = str(payload.get("type") or "").strip().lower()
|
|
3680
|
+
if ptype in ("ship", "shipped", "ship_report"):
|
|
3681
|
+
return True
|
|
3682
|
+
has_task = bool(payload.get("task"))
|
|
3683
|
+
has_commits = bool(payload.get("commits"))
|
|
3684
|
+
has_version = bool(payload.get("version"))
|
|
3685
|
+
return has_task and (has_commits or has_version)
|
|
3686
|
+
|
|
3687
|
+
|
|
3688
|
+
def _extract_task_ref_from_payload(payload: Dict[str, Any]) -> Optional[str]:
|
|
3689
|
+
"""Extract an 8+ hex-char task id prefix from a ship-report payload.
|
|
3690
|
+
Looks in explicit fields first (task_id, task, id) then falls back
|
|
3691
|
+
to the textual blob. Returns lowercased prefix or None.
|
|
3692
|
+
"""
|
|
3693
|
+
import re as _re
|
|
3694
|
+
if not isinstance(payload, dict):
|
|
3695
|
+
return None
|
|
3696
|
+
for key in ("task_id", "task", "id"):
|
|
3697
|
+
val = payload.get(key)
|
|
3698
|
+
if isinstance(val, str):
|
|
3699
|
+
m = _re.search(r"([0-9a-f]{8}(?:-[0-9a-f]+)*)", val, _re.IGNORECASE)
|
|
3700
|
+
if m:
|
|
3701
|
+
return m.group(1).lower()
|
|
3702
|
+
# Last resort: scan a textual blob
|
|
3703
|
+
blob_parts = []
|
|
3704
|
+
for k in ("text", "commits", "summary"):
|
|
3705
|
+
v = payload.get(k)
|
|
3706
|
+
if isinstance(v, (str, dict, list)):
|
|
3707
|
+
blob_parts.append(str(v))
|
|
3708
|
+
blob = " ".join(blob_parts)
|
|
3709
|
+
m = _re.search(r"\b([0-9a-f]{8})\b", blob, _re.IGNORECASE)
|
|
3710
|
+
if m:
|
|
3711
|
+
return m.group(1).lower()
|
|
3712
|
+
return None
|
|
3713
|
+
|
|
3714
|
+
|
|
3715
|
+
def _build_ship_summary(payload: Dict[str, Any], duration_note: str = "") -> str:
|
|
3716
|
+
"""Stringify a ship-report payload into a task-complete summary."""
|
|
3717
|
+
parts = [f"AUTO-CHECKPOINT (CLOSEGAP-3): ship detected via meshcode_send{duration_note}."]
|
|
3718
|
+
for key in ("version", "behavior"):
|
|
3719
|
+
val = payload.get(key)
|
|
3720
|
+
if val:
|
|
3721
|
+
parts.append(f"{key}={val}")
|
|
3722
|
+
commits = payload.get("commits")
|
|
3723
|
+
if isinstance(commits, dict) and commits:
|
|
3724
|
+
parts.append("commits=" + " ".join(f"{k}:{v}" for k, v in commits.items()))
|
|
3725
|
+
elif commits:
|
|
3726
|
+
parts.append(f"commits={commits}")
|
|
3727
|
+
files = payload.get("files")
|
|
3728
|
+
if files:
|
|
3729
|
+
parts.append(f"files={files}")
|
|
3730
|
+
summary = " | ".join(str(p) for p in parts)
|
|
3731
|
+
return summary[:1500]
|
|
3732
|
+
|
|
3733
|
+
|
|
3734
|
+
def _try_auto_complete_on_ship(payload: Dict[str, Any]) -> Optional[Dict[str, Any]]:
|
|
3735
|
+
"""CLOSEGAP-3: if `payload` is a ship-report referencing a task id
|
|
3736
|
+
that this agent currently holds in claimed/in_progress, call
|
|
3737
|
+
mc_task_complete with a built summary. Best-effort.
|
|
3738
|
+
"""
|
|
3739
|
+
try:
|
|
3740
|
+
if not _ship_marker_in_payload(payload):
|
|
3741
|
+
return None
|
|
3742
|
+
ref = _extract_task_ref_from_payload(payload)
|
|
3743
|
+
if not ref:
|
|
3744
|
+
return None
|
|
3745
|
+
api_key = _get_api_key()
|
|
3746
|
+
if not api_key:
|
|
3747
|
+
return None
|
|
3748
|
+
listing = be.task_list(api_key, _PROJECT_ID, AGENT_NAME, status_filter=None)
|
|
3749
|
+
if not isinstance(listing, dict) or not listing.get("ok"):
|
|
3750
|
+
return None
|
|
3751
|
+
matches = [
|
|
3752
|
+
t for t in (listing.get("tasks") or [])
|
|
3753
|
+
if t.get("claimed_by") == AGENT_NAME
|
|
3754
|
+
and t.get("status") in ("claimed", "in_progress")
|
|
3755
|
+
and str(t.get("id") or "").lower().startswith(ref)
|
|
3756
|
+
]
|
|
3757
|
+
if len(matches) != 1:
|
|
3758
|
+
return None
|
|
3759
|
+
target = matches[0]
|
|
3760
|
+
# Duration from last_progress_at (mig 349 stamps this on in_progress flip)
|
|
3761
|
+
duration_note = ""
|
|
3762
|
+
started_at = target.get("last_progress_at") or target.get("claimed_at")
|
|
3763
|
+
if started_at and target.get("status") == "in_progress":
|
|
3764
|
+
try:
|
|
3765
|
+
from datetime import datetime, timezone
|
|
3766
|
+
_start_dt = datetime.fromisoformat(str(started_at).replace("Z", "+00:00"))
|
|
3767
|
+
_delta = datetime.now(timezone.utc) - _start_dt
|
|
3768
|
+
_mins = int(_delta.total_seconds() // 60)
|
|
3769
|
+
if _mins > 0:
|
|
3770
|
+
duration_note = f" (duration ~{_mins}m)"
|
|
3771
|
+
except Exception:
|
|
3772
|
+
pass
|
|
3773
|
+
summary = _build_ship_summary(payload, duration_note=duration_note)
|
|
3774
|
+
result = be.task_complete(api_key, _PROJECT_ID, target["id"], AGENT_NAME, summary=summary)
|
|
3775
|
+
if not (isinstance(result, dict) and result.get("ok")):
|
|
3776
|
+
return None
|
|
3777
|
+
try:
|
|
3778
|
+
_log_activity_bg(
|
|
3779
|
+
"auto_checkpoint",
|
|
3780
|
+
f"{AGENT_NAME} auto-completed task {target['id'][:8]} on ship report",
|
|
3781
|
+
)
|
|
3782
|
+
except Exception:
|
|
3783
|
+
pass
|
|
3784
|
+
return {
|
|
3785
|
+
"task_id": target["id"][:8],
|
|
3786
|
+
"title": str(target.get("title") or "")[:80],
|
|
3787
|
+
"duration_note": duration_note,
|
|
3788
|
+
}
|
|
3789
|
+
except Exception as e:
|
|
3790
|
+
log.debug(f"[meshcode] auto-checkpoint on ship failed: {e}")
|
|
3791
|
+
return None
|
|
3792
|
+
|
|
3793
|
+
|
|
3794
|
+
def _try_auto_complete_on_done_status(task_hint: str) -> Optional[Dict[str, Any]]:
|
|
3795
|
+
"""CLOSEGAP-3 sibling: when set_status='done', complete the single
|
|
3796
|
+
in_progress task if there's exactly one. No-op for 0 or >1 active.
|
|
3797
|
+
Summary built from the task_hint + a fallback note.
|
|
3798
|
+
"""
|
|
3799
|
+
try:
|
|
3800
|
+
api_key = _get_api_key()
|
|
3801
|
+
if not api_key:
|
|
3802
|
+
return None
|
|
3803
|
+
listing = be.task_list(api_key, _PROJECT_ID, AGENT_NAME, status_filter=None)
|
|
3804
|
+
if not isinstance(listing, dict) or not listing.get("ok"):
|
|
3805
|
+
return None
|
|
3806
|
+
active = [
|
|
3807
|
+
t for t in (listing.get("tasks") or [])
|
|
3808
|
+
if t.get("claimed_by") == AGENT_NAME and t.get("status") == "in_progress"
|
|
3809
|
+
]
|
|
3810
|
+
if len(active) != 1:
|
|
3811
|
+
return None
|
|
3812
|
+
target = active[0]
|
|
3813
|
+
summary = (
|
|
3814
|
+
f"AUTO-CHECKPOINT (CLOSEGAP-3): agent flipped set_status='done'. "
|
|
3815
|
+
f"Task hint: {(task_hint or '(none)')[:200]}. "
|
|
3816
|
+
f"No ship-report content captured — set_status payload-only."
|
|
3817
|
+
)
|
|
3818
|
+
result = be.task_complete(api_key, _PROJECT_ID, target["id"], AGENT_NAME, summary=summary)
|
|
3819
|
+
if not (isinstance(result, dict) and result.get("ok")):
|
|
3820
|
+
return None
|
|
3821
|
+
try:
|
|
3822
|
+
_log_activity_bg(
|
|
3823
|
+
"auto_checkpoint",
|
|
3824
|
+
f"{AGENT_NAME} auto-completed task {target['id'][:8]} on set_status=done",
|
|
3825
|
+
)
|
|
3826
|
+
except Exception:
|
|
3827
|
+
pass
|
|
3828
|
+
return {"task_id": target["id"][:8], "title": str(target.get("title") or "")[:80]}
|
|
3829
|
+
except Exception as e:
|
|
3830
|
+
log.debug(f"[meshcode] auto-checkpoint on done-status failed: {e}")
|
|
3831
|
+
return None
|
|
3832
|
+
|
|
3833
|
+
|
|
3444
3834
|
# AGENT-INTEL-1-PY (task 38b1a0e2): mesh_online_snapshot in wait return.
|
|
3445
3835
|
# Calls mc_roster_live (mig 348) and projects a compact list of live+idle
|
|
3446
3836
|
# agents for the receiving agent's situational awareness. Cached 10s so
|
|
@@ -3675,7 +4065,16 @@ async def meshcode_wait(timeout_seconds: int = 20, include_acks: bool = False) -
|
|
|
3675
4065
|
# the mesh feel proactive.
|
|
3676
4066
|
_auto_claim_threshold = 10 if _autonomous else 30
|
|
3677
4067
|
if _CONSECUTIVE_IDLE_SECONDS >= _auto_claim_threshold and not _is_leader_agent():
|
|
3678
|
-
|
|
4068
|
+
# AUTONOMOUS-CLOSEGAP-1 (task beadc9e6): heuristic
|
|
4069
|
+
# match on assignee='auto'/unset first (better fit),
|
|
4070
|
+
# wildcard '*' fallback second. Load gate configurable.
|
|
4071
|
+
try:
|
|
4072
|
+
_max_load = int(os.environ.get("MESHCODE_AUTO_CLAIM_MAX_LOAD", "2"))
|
|
4073
|
+
except Exception:
|
|
4074
|
+
_max_load = 2
|
|
4075
|
+
claimed = _try_auto_claim_open_by_heuristic(max_load=_max_load)
|
|
4076
|
+
if not claimed:
|
|
4077
|
+
claimed = _try_auto_claim_task()
|
|
3679
4078
|
if claimed:
|
|
3680
4079
|
result["auto_claimed"] = claimed
|
|
3681
4080
|
result["pending_tasks"] = [claimed]
|
|
@@ -4250,6 +4649,40 @@ def meshcode_set_status(status: str, task: str = "") -> Dict[str, Any]:
|
|
|
4250
4649
|
resp["auto_claimed_on_reconnect"] = _claimed_on_reconnect
|
|
4251
4650
|
except Exception:
|
|
4252
4651
|
pass
|
|
4652
|
+
# CLOSEGAP-2 (task 01bfe6c4, Samuel reframe ce506e0d): auto-flip
|
|
4653
|
+
# the matching claimed task to in_progress via mc_task_start when
|
|
4654
|
+
# an agent calls set_status(working, "<task-hint>"). Eliminates the
|
|
4655
|
+
# manual second call and gives the FE an authoritative status
|
|
4656
|
+
# source. Baseline (NOT autonomous-gated). Best-effort.
|
|
4657
|
+
if status == "working" and task and isinstance(resp, dict) and resp.get("ok"):
|
|
4658
|
+
try:
|
|
4659
|
+
_matched_id = _match_claimed_task_for_set_status(task)
|
|
4660
|
+
if _matched_id:
|
|
4661
|
+
_start_resp = be.sb_rpc("mc_task_start", {
|
|
4662
|
+
"p_api_key": _get_api_key(),
|
|
4663
|
+
"p_project_id": _PROJECT_ID,
|
|
4664
|
+
"p_task_id": _matched_id,
|
|
4665
|
+
"p_starting_agent": AGENT_NAME,
|
|
4666
|
+
})
|
|
4667
|
+
if isinstance(_start_resp, dict) and _start_resp.get("ok"):
|
|
4668
|
+
resp["auto_started_task"] = {
|
|
4669
|
+
"id": _matched_id[:8],
|
|
4670
|
+
"title": _start_resp.get("title"),
|
|
4671
|
+
"demoted_count": _start_resp.get("demoted_count", 0),
|
|
4672
|
+
}
|
|
4673
|
+
except Exception:
|
|
4674
|
+
pass # best-effort; never block set_status
|
|
4675
|
+
# CLOSEGAP-3 (task 69dc5a62, Samuel reframe ce506e0d): when the
|
|
4676
|
+
# agent flips set_status='done' with exactly one active in_progress
|
|
4677
|
+
# claim, auto-complete that claim with a status-driven summary.
|
|
4678
|
+
# Baseline (NOT autonomous-gated). Best-effort.
|
|
4679
|
+
if status == "done" and isinstance(resp, dict) and resp.get("ok"):
|
|
4680
|
+
try:
|
|
4681
|
+
_cp = _try_auto_complete_on_done_status(task)
|
|
4682
|
+
if _cp:
|
|
4683
|
+
resp["auto_checkpointed_task"] = _cp
|
|
4684
|
+
except Exception:
|
|
4685
|
+
pass
|
|
4253
4686
|
return resp
|
|
4254
4687
|
|
|
4255
4688
|
|
|
@@ -5538,6 +5971,32 @@ def meshcode_set_meshwork_autonomous_default(enabled: bool) -> Dict[str, Any]:
|
|
|
5538
5971
|
})
|
|
5539
5972
|
|
|
5540
5973
|
|
|
5974
|
+
@mcp.tool()
|
|
5975
|
+
@with_working_status
|
|
5976
|
+
def meshcode_set_autonomous(enabled: bool) -> Dict[str, Any]:
|
|
5977
|
+
"""Toggle autonomous mode for THIS agent. Reflected on the dashboard in real-time.
|
|
5978
|
+
|
|
5979
|
+
When enabled, the agent operates in decide-execute-report cadence with
|
|
5980
|
+
mesh-first escalation (see AUTONOMOUS MODE in system instructions).
|
|
5981
|
+
When disabled, the agent reverts to standard ask-mode behavior.
|
|
5982
|
+
|
|
5983
|
+
Args:
|
|
5984
|
+
enabled: True = autonomous mode ON; False = OFF.
|
|
5985
|
+
"""
|
|
5986
|
+
global _AUTONOMOUS_MODE_CACHE, _AUTONOMOUS_MODE_CACHE_TS
|
|
5987
|
+
result = be.sb_rpc("mc_agent_set_autonomous", {
|
|
5988
|
+
"p_api_key": _get_api_key(),
|
|
5989
|
+
"p_project_id": _PROJECT_ID,
|
|
5990
|
+
"p_agent_name": AGENT_NAME,
|
|
5991
|
+
"p_enabled": bool(enabled),
|
|
5992
|
+
})
|
|
5993
|
+
if isinstance(result, dict) and not result.get("error"):
|
|
5994
|
+
_AUTONOMOUS_MODE_CACHE = bool(enabled)
|
|
5995
|
+
_AUTONOMOUS_MODE_CACHE_TS = _time.monotonic()
|
|
5996
|
+
return {"ok": True, "autonomous_mode": bool(enabled), "agent": AGENT_NAME}
|
|
5997
|
+
return result or {"error": "failed to set autonomous mode"}
|
|
5998
|
+
|
|
5999
|
+
|
|
5541
6000
|
@mcp.tool()
|
|
5542
6001
|
@with_working_status
|
|
5543
6002
|
def meshcode_edit_agent(name: str, role: Optional[str] = None, launch_prompt: Optional[str] = None) -> Dict[str, Any]:
|
|
@@ -5806,9 +6265,12 @@ def meshcode_release_fixture(row_id: str) -> Dict[str, Any]:
|
|
|
5806
6265
|
def meshcode_register_skills(skills: List[str]) -> Dict[str, Any]:
|
|
5807
6266
|
"""Register the skill catalog available in this MCP session.
|
|
5808
6267
|
MESH-IMPROVE-8 (mig 344). Call at boot or when runtime skills change.
|
|
6268
|
+
Also caches locally for the AUTONOMOUS-CLOSEGAP-1 heuristic.
|
|
5809
6269
|
"""
|
|
6270
|
+
global _REGISTERED_SKILLS
|
|
5810
6271
|
if skills is None:
|
|
5811
6272
|
skills = []
|
|
6273
|
+
_REGISTERED_SKILLS = [str(s) for s in skills if s]
|
|
5812
6274
|
return be.sb_rpc("mc_register_skills", {
|
|
5813
6275
|
"p_api_key": _get_api_key(),
|
|
5814
6276
|
"p_project_id": _PROJECT_ID,
|