swarph-cli 0.68.0__tar.gz → 0.68.2__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.
- {swarph_cli-0.68.0/src/swarph_cli.egg-info → swarph_cli-0.68.2}/PKG-INFO +1 -1
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/pyproject.toml +1 -1
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/__init__.py +1 -1
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/hooks.py +61 -17
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/mesh.py +77 -7
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/schedule.py +1 -1
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/watchdog.py +106 -31
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/gateway/server.py +1 -1
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/scripts/wake_watchdog.py +18 -10
- {swarph_cli-0.68.0 → swarph_cli-0.68.2/src/swarph_cli.egg-info}/PKG-INFO +1 -1
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli.egg-info/SOURCES.txt +2 -0
- swarph_cli-0.68.2/tests/test_184_notify_honesty.py +114 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_729_wake_watchdog.py +20 -0
- swarph_cli-0.68.2/tests/test_825_install_posttooluse_only.py +105 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_825_remaining_cap_shred_bind.py +4 -4
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_830_hook_binding_version.py +2 -2
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_codegraph_hook.py +1 -3
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_mesh_sidecar.py +12 -1
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_watchdog_dm_wake.py +3 -2
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_watchdog_dm_wake_cooldown.py +7 -6
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_watchdog_dm_wake_wiring.py +4 -2
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_watchdog_model_rung.py +4 -1
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/LICENSE +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/README.md +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/setup.cfg +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/__main__.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/bench/__init__.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/bench/backends.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/bench/pack.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/bench/prices.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/bench/providers.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/bench/quality.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/bench/refresh_prices.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/bench/rules.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/bench/runner.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/bench/validate.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/caller.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/capture/__init__.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/capture/harden.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/capture/lineage.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/capture/liveness.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/capture/manifest.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/capture/paths.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/capture/verify.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/cell.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/chain_token.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/channel.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/__init__.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/_content.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/_display.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/_gateway_client.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/add.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/bench.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/board.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/board_merge_check.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/brain.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/brain_ask.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/cell.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/cell_probe.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/channel.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/chat.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/codegraph.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/codegraph_hook.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/codex_hook_output.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/codex_waker.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/compress.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/daemon.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/dreaming.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/event.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/gateway.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/gh_route.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/group.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/guide.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/highlight.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/hook_interpreter.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/hook_output.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/import_session.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/init.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/install_hook.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/install_multiplexer.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/install_wake_hook.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/lane.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/mcp_server.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/memory.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/memory_sync.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/monitor.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/okf_links.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/onboard.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/protocol_handler.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/ratify.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/security.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/service.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/spawn.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/timeline.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/version.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/commands/wake_hook_output.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/compress/__init__.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/compress/levers.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/compress/marker.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/compress/verify.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/console_safe.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/delivery_queue.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/dm_frame.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/dreaming/__init__.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/dreaming/candidates.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/dreaming/clone.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/dreaming/enrich.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/dreaming/index_check.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/dreaming/notify.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/dreaming/organize.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/dreaming/probes.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/dreaming/report.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/dreaming/run.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/dreaming/slm.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/dreaming/transcripts.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/dreaming/verify.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/gateway/__init__.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/gateway/feature_registry.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/gateway/lanes_control.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/gateway/schema.sql +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/gateway/services_control.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/gateway_default.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/gh_identity.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/guide/GUIDE.md +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/main.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/multiplexer.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/orphan_daemons.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/pane_probe.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/parsers/__init__.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/parsers/claude.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/peer_executor.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/peer_executor_windows.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/peer_reconciliation.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/peer_reply_outbox.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/peer_service_host.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/peer_service_payload.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/peer_service_reply.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/peer_staging.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/query_filters.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/scripts/__init__.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/service/__init__.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/service/app.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/service/providers.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/session_bridge.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/source_text.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/stall_alert.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/systemd/codex-waker.default +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/timeline_paths.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli/tokens.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli.egg-info/entry_points.txt +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli.egg-info/requires.txt +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/src/swarph_cli.egg-info/top_level.txt +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_124_register_merge.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_126_pending_from_complete.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_132_ensure_monitor_state_dir.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_138_memory_navigate.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_139_tool_probe.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_183_card_anchor.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_201_win32_bash_refusal.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_243_onboard_per_peer_token.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_307_task4_ask_reply.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_319_refusal_exit_codes.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_323_mistral_backend.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_332_report_organize_findings.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_356_query_filters.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_397_gh_identity_router.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_397_router_preflight.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_467b_onboard_operator_credential.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_523_guide.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_524_conftest_hermeticity.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_532_accept_flag.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_535_version_capability.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_546_default_gateway.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_547_verbs_from_registry.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_548_gateway_remainder.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_552_codegraph_gateway.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_555_relay_row_shape.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_556_relay_freshness.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_559_edges_truncated.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_564_bootstrap_first_peer.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_579_register_bootstrap_message.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_591_cli_step_graph.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_644_monitor_supervision.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_648_install_unit.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_654_reexec.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_656_owned_slm.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_684_dreaming_verb.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_689_pkg_version_order.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_716_timeline_one_path.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_724_ps1_ascii_or_bom.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_725_stdio_reconfigure.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_729_dm_once.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_734_dreaming_enrich_degrade.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_735_dm_mcp_tools.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_740_edit_project.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_762_cards_edit_priority.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_764_enrich_collapse.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_801_confirm_flow_cli.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_802_stage_history.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_825_codegraph_hook_noise.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_829_counterfactual_column.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_84_cli_labels.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_866_doctrine_guide.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_872_identity_refuses_not_warns.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_874_source_text.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_937_dreaming_notify.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_938_organize_line_limit.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_941_bench_backends.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_941_harness_342.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_945_same_line_pairing.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_955_prompt_audit.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_957_auth_http.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_959_venv_interpreter.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_960_channel.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_963_plugin_manifest.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_964_channel_cell.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_965_mesh_wait.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_975_channel_hop.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_add_security_gate.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_artifact_add.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_artifact_hash.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_artifact_lib.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_artifact_mcp.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_artifact_skill.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_artifact_tool.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_artifact_uri.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_bench_backends.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_bench_command.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_bench_pack.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_bench_prices.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_bench_quality.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_bench_runner.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_bench_validate.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_board_card_thread.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_board_command.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_board_merge_check.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_board_obligation_close.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_brain_ask_command.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_brain_ask_env_diagnosis.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_brain_ask_identity_explicitness.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_brain_command.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_caller_meta_guard.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_capture_lineage.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_capture_liveness.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_capture_manifest.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_capture_paths.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_capture_security.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_cell_command_dispatch.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_cell_harden.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_cell_loader.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_cell_selfcheck.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_cell_verify.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_chain_token.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_channel_command.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_channel_poll_never_breaks_dms.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_channel_post.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_channel_wake_policy_filter.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_chat_command.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_claude_parser.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_claude_tmux_template.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_codegraph_caller_scope.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_codegraph_command.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_codegraph_mcp.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_codex_waker.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_compress_caller_convention.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_compress_command.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_compress_levers.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_compress_marker.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_compress_verify.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_console_encoding_e2e.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_content_composition.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_cursor_membrane.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_daemon_command.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_daemon_console_encoding.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_daemon_delivery.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_delivery_queue.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_display_sanitize.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_ensure_monitor_no_peer_default.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_event_emit.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_feature_to_uri.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_gateway_brain.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_gateway_client.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_gateway_command.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_gateway_message_idempotency.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_gh_route_doctor_mesh_name.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_group_command.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_guide_commands_parse.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_guide_selfcheck.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_heartbeat_check.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_highlight_command.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_hook_output.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_hooks_activity_marker.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_hooks_add.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_hooks_bundle.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_hooks_init.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_hooks_legacy_generation_removal.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_hooks_lifecycle.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_hooks_merge.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_hooks_verify.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_hooks_win32_bash_wrapper.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_hooks_win32_command_path.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_import_command.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_inbox_identity_provenance.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_init_command.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_init_provider_menu.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_install_codex_hooks.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_install_hook.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_install_multiplexer_command.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_install_postcompact_hook.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_install_wake_hook.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_lane_command.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_main.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_main_unknown_verb_guard.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_mcp_missing_hint.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_mcp_server.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_memory_command.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_memory_emit_snippet.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_memory_gateway.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_memory_sync.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_memory_sync_membrane.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_mesh_command.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_mesh_inbox_mark_read.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_mesh_inbox_sanitize.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_mesh_originate_thread_623.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_mesh_send_unknown_peer.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_meta_edge_identity.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_monitor_channel_poll.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_monitor_cli.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_monitor_daemon_collision.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_monitor_self_name_verification.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_monitor_state_split.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_multiplexer.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_muse_membrane.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_muse_own_cli.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_obligation_close_is_surfaced.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_okf_links.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_onboard_command.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_opencode_membrane.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_opencode_plugin.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_orphan_daemons.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_packaged_artifacts_exist.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_packaged_skills.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_peer_executor.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_peer_executor_windows.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_peer_reply_drain_command.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_peer_reply_drain_supervisor.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_peer_reply_outbox.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_peer_reply_transport.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_peer_service_e2e.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_peer_service_host.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_peer_service_payload.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_peer_service_reply.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_postcompact_recall.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_process_liveness.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_protocol_handler.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_providers_isolation.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_ratify_command.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_readme_claims_no_gateway_default.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_repl_caller_convention.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_schedule_command.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_schedule_create_contract.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_security.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_service_app.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_service_command.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_service_providers.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_session_bridge_inject.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_session_bridge_probe.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_session_bridge_resolve.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_session_path_role_gate.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_shipped_units_match_the_cli.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_smoke_chat.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_smoke_one_shot.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_smoke_phase_5_5.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_spawn_base_hoist.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_spawn_command.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_spawn_identity_stamp.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_spawn_live_pin.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_spawn_mitosis_lineage.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_spawn_tmux_session.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_spawn_windows_relaunch.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_stall_alert.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_subprocess_decode_encoding.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_timeline_command.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_tmux_wake_runstate_defer.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_tmux_wake_session_reanchor.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_tmux_wake_stale.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_tmux_wake_submit_verify.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_token_file_one_parser.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_token_precedence_explicit_first.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_unified_token_resolver.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_version_command.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_version_consistency.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_vibe_membrane.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_watchdog.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_watchdog_emit_health.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_watchdog_install_flags.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_watchdog_stale_peers.py +0 -0
- {swarph_cli-0.68.0 → swarph_cli-0.68.2}/tests/test_windows_waker_installer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: swarph-cli
|
|
3
|
-
Version: 0.68.
|
|
3
|
+
Version: 0.68.2
|
|
4
4
|
Summary: The `swarph` binary — a multi-LLM CLI and mesh-gateway client + bundled server (`swarph gateway`): one-shot prompts across providers, interactive `chat`, multi-provider `spawn` (claude/codex/antigravity via a ProviderMembrane with subprocess billing-scrub), `mesh` send/inbox/register, `brain-ask` over the swarph-brain (gbrain) semantic memory, git-backed `assisted_memory`, per-invocation `gh` identity routing, a silent DM-wake hook, session `import`, and a stranded-session `watchdog`.
|
|
5
5
|
Author: Pierre Samson, Claude Opus
|
|
6
6
|
License: MIT
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "swarph-cli"
|
|
7
|
-
version = "0.68.
|
|
7
|
+
version = "0.68.2"
|
|
8
8
|
description = "The `swarph` binary — a multi-LLM CLI and mesh-gateway client + bundled server (`swarph gateway`): one-shot prompts across providers, interactive `chat`, multi-provider `spawn` (claude/codex/antigravity via a ProviderMembrane with subprocess billing-scrub), `mesh` send/inbox/register, `brain-ask` over the swarph-brain (gbrain) semantic memory, git-backed `assisted_memory`, per-invocation `gh` identity routing, a silent DM-wake hook, session `import`, and a stranded-session `watchdog`."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -190,17 +190,17 @@ exit 0
|
|
|
190
190
|
# The bundled codegraph hook (cards #194 + #825). Thin wrapper around
|
|
191
191
|
# `swarph codegraph-hook` so heuristics stay unit-testable.
|
|
192
192
|
#
|
|
193
|
-
# #825: UserPromptSubmit
|
|
194
|
-
#
|
|
195
|
-
# (
|
|
196
|
-
#
|
|
193
|
+
# #825, retired 2026-09-26: UserPromptSubmit / Stop / StopFailure are not
|
|
194
|
+
# installed. The audit showed the prompt side did not steer tool choice
|
|
195
|
+
# (3 codegraph calls in 1,206 displays). PostToolUse/Bash is the binding
|
|
196
|
+
# that stays. install_hook drops the retired events for this command so a
|
|
197
|
+
# later `swarph hooks add` cannot put them back.
|
|
197
198
|
_CODEGRAPH_ON_GREP_SH = r"""#!/bin/sh
|
|
198
199
|
# codegraph-on-grep.sh — swarph bundled Claude Code hook (#194 + #825).
|
|
199
200
|
# swarph-cli-bundle-version: __SWARPH_CLI_VERSION__
|
|
200
201
|
#
|
|
201
|
-
#
|
|
202
|
-
#
|
|
203
|
-
# Stop/StopFailure: record whether the session grepped, called codegraph, or neither.
|
|
202
|
+
# PostToolUse/Bash: annotate code greps. UserPromptSubmit, Stop, and
|
|
203
|
+
# StopFailure are not bound (card #825, retired 2026-09-26).
|
|
204
204
|
# #830: version stamp is substituted at install time — hooks list compares it to
|
|
205
205
|
# the running swarph --version so a binding/code skew is reportable.
|
|
206
206
|
exec swarph codegraph-hook "$@"
|
|
@@ -210,10 +210,8 @@ BUILTIN_HOOKS: dict = {
|
|
|
210
210
|
"codegraph-on-grep": HookBundle(
|
|
211
211
|
name="codegraph-on-grep",
|
|
212
212
|
description=(
|
|
213
|
-
"On
|
|
214
|
-
"
|
|
215
|
-
"symbol-level answers. Prompt path steers tool choice; Bash path "
|
|
216
|
-
"annotates. Audits counterfactual outcomes. Never blocks. Reports "
|
|
213
|
+
"On PostToolUse/Bash (code grep), queries the gateway's structural "
|
|
214
|
+
"codegraph and injects symbol-level answers. Never blocks. Reports "
|
|
217
215
|
"an unavailable index LOUDLY rather than as 'no matches'."
|
|
218
216
|
),
|
|
219
217
|
publisher="swarph-builtin",
|
|
@@ -221,13 +219,10 @@ BUILTIN_HOOKS: dict = {
|
|
|
221
219
|
script_name="codegraph-on-grep.sh",
|
|
222
220
|
script_body=_CODEGRAPH_ON_GREP_SH,
|
|
223
221
|
bindings=(
|
|
224
|
-
HookBinding("UserPromptSubmit", ""),
|
|
225
222
|
HookBinding("PostToolUse", "Bash"),
|
|
226
|
-
HookBinding("Stop", ""),
|
|
227
|
-
HookBinding("StopFailure", ""),
|
|
228
223
|
),
|
|
229
224
|
handler_events=(
|
|
230
|
-
"
|
|
225
|
+
"PostToolUse",
|
|
231
226
|
),
|
|
232
227
|
),
|
|
233
228
|
"cell-resilience": HookBundle(
|
|
@@ -502,6 +497,49 @@ def _unmerge_hook(settings: dict, event: str, matcher: str, command: str) -> dic
|
|
|
502
497
|
return settings
|
|
503
498
|
|
|
504
499
|
|
|
500
|
+
def _drop_command_outside_bindings(settings: dict, commands, bundle: HookBundle) -> dict:
|
|
501
|
+
"""Remove this bundle's command strings from events it does not declare.
|
|
502
|
+
|
|
503
|
+
``_merge_hook`` only adds. A binding deleted from the bundle would
|
|
504
|
+
otherwise survive the next install, which is how the retired codegraph
|
|
505
|
+
prompt-side events came back (card #825). ``commands`` is the install
|
|
506
|
+
ladder (canonical plus older Windows forms). Other commands on those
|
|
507
|
+
events are left alone.
|
|
508
|
+
"""
|
|
509
|
+
keep = {(b.event, b.matcher) for b in bundle.bindings}
|
|
510
|
+
hooks = settings.get("hooks")
|
|
511
|
+
if not isinstance(hooks, dict):
|
|
512
|
+
return settings
|
|
513
|
+
drop = {v for c in commands for v in _command_variants_for(c)}
|
|
514
|
+
for event, entries in list(hooks.items()):
|
|
515
|
+
if not isinstance(entries, list):
|
|
516
|
+
continue
|
|
517
|
+
removed = False
|
|
518
|
+
for entry in list(entries):
|
|
519
|
+
# A string (or anything else) in an unrelated event is not a
|
|
520
|
+
# group. Skip it. _unmerge_hook would call .get on it (#825).
|
|
521
|
+
if not isinstance(entry, dict):
|
|
522
|
+
continue
|
|
523
|
+
acts = entry.get("hooks")
|
|
524
|
+
if not isinstance(acts, list) or (event, entry.get("matcher", "")) in keep:
|
|
525
|
+
continue
|
|
526
|
+
kept = [
|
|
527
|
+
a for a in acts
|
|
528
|
+
if not (isinstance(a, dict) and a.get("command") in drop)
|
|
529
|
+
]
|
|
530
|
+
# Never rewrite a group that did not hold this command. An empty
|
|
531
|
+
# hooks list and a matcher-only group stay as they were.
|
|
532
|
+
if len(kept) == len(acts):
|
|
533
|
+
continue
|
|
534
|
+
entry["hooks"] = kept
|
|
535
|
+
if not kept:
|
|
536
|
+
entries.remove(entry)
|
|
537
|
+
removed = True
|
|
538
|
+
if removed and not entries:
|
|
539
|
+
del hooks[event]
|
|
540
|
+
return settings
|
|
541
|
+
|
|
542
|
+
|
|
505
543
|
# --------------------------------------------------------------------------- #
|
|
506
544
|
# Local-bundle resolver (T3)
|
|
507
545
|
# --------------------------------------------------------------------------- #
|
|
@@ -699,6 +737,11 @@ def install_hook(
|
|
|
699
737
|
settings = _load_settings(settings_path)
|
|
700
738
|
for b in bundle.bindings:
|
|
701
739
|
settings = _merge_hook(settings, b.event, b.matcher, command)
|
|
740
|
+
# Drop this command from every event the bundle no longer declares.
|
|
741
|
+
# Otherwise a retired binding (codegraph UserPromptSubmit/Stop, #825)
|
|
742
|
+
# stays forever, and the next add puts it back if it was removed by hand.
|
|
743
|
+
settings = _drop_command_outside_bindings(
|
|
744
|
+
settings, _installed_command_variants(bundle, hooks_home), bundle)
|
|
702
745
|
|
|
703
746
|
# ---- write the script (version-stamped), then atomic-save settings ----
|
|
704
747
|
hooks_home_p.mkdir(parents=True, exist_ok=True)
|
|
@@ -1058,8 +1101,9 @@ def list_hooks(
|
|
|
1058
1101
|
|
|
1059
1102
|
name [installed|partial|available] trust=builtin — description
|
|
1060
1103
|
|
|
1061
|
-
``partial`` (#825): at least one binding present but not the full set
|
|
1062
|
-
|
|
1104
|
+
``partial`` (#825): at least one binding present but not the full set.
|
|
1105
|
+
codegraph-on-grep has a single PostToolUse/Bash binding, so that shape
|
|
1106
|
+
is installed, not partial.
|
|
1063
1107
|
When installed and the script carries a ``swarph-cli-bundle-version`` stamp
|
|
1064
1108
|
(#830), a second line reports match / mismatch vs the running package.
|
|
1065
1109
|
"""
|
|
@@ -1253,6 +1253,38 @@ def _write_cursor_atomic(path: Path, cursor: dict) -> None:
|
|
|
1253
1253
|
os.replace(tmp, path)
|
|
1254
1254
|
|
|
1255
1255
|
|
|
1256
|
+
def _persist_wake_stamp(state: "MonitorState") -> None:
|
|
1257
|
+
"""Rewrite cursor.json once a wake stamp exists.
|
|
1258
|
+
|
|
1259
|
+
The observation save runs before any sink, so it records last_wake_at
|
|
1260
|
+
while the name is still unknown. Without this second write the file
|
|
1261
|
+
keeps 0.0 after a keystroke (#405).
|
|
1262
|
+
"""
|
|
1263
|
+
stamps: list[float] = []
|
|
1264
|
+
for led in state.ledgers.values():
|
|
1265
|
+
raw = led.get("last_wake_at")
|
|
1266
|
+
if raw is None:
|
|
1267
|
+
continue
|
|
1268
|
+
try:
|
|
1269
|
+
stamp = float(raw)
|
|
1270
|
+
except (TypeError, ValueError):
|
|
1271
|
+
continue
|
|
1272
|
+
if stamp > 0:
|
|
1273
|
+
stamps.append(stamp)
|
|
1274
|
+
if not stamps:
|
|
1275
|
+
return
|
|
1276
|
+
newest = max(stamps)
|
|
1277
|
+
previous = state.observed.get("last_wake_at")
|
|
1278
|
+
try:
|
|
1279
|
+
previous_f = float(previous) if previous is not None else 0.0
|
|
1280
|
+
except (TypeError, ValueError):
|
|
1281
|
+
previous_f = 0.0
|
|
1282
|
+
if previous_f == newest:
|
|
1283
|
+
return
|
|
1284
|
+
state.observed["last_wake_at"] = newest
|
|
1285
|
+
_write_cursor_atomic(state.cursor_path, dict(state.cursor))
|
|
1286
|
+
|
|
1287
|
+
|
|
1256
1288
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
1257
1289
|
# card #122 — pluggable delivery
|
|
1258
1290
|
#
|
|
@@ -1390,6 +1422,18 @@ class NoneSink(Sink):
|
|
|
1390
1422
|
super().__init__("none")
|
|
1391
1423
|
|
|
1392
1424
|
|
|
1425
|
+
def _stamp_wake(led: dict) -> None:
|
|
1426
|
+
"""The keystroke, not the delivery report.
|
|
1427
|
+
|
|
1428
|
+
`last_delivery_at` also moves when a standing wake returns True with no
|
|
1429
|
+
keystroke. `last_wake_at` moves only here, beside `last_wake_injected_at`.
|
|
1430
|
+
The name is absent until this runs; 0.0 is not a time.
|
|
1431
|
+
"""
|
|
1432
|
+
now = time.time()
|
|
1433
|
+
led["last_wake_at"] = now
|
|
1434
|
+
led["last_wake_injected_at"] = now
|
|
1435
|
+
|
|
1436
|
+
|
|
1393
1437
|
class TmuxSink(Sink):
|
|
1394
1438
|
"""Poke a tmux pane — the behaviour `mesh sidecar` has always had.
|
|
1395
1439
|
|
|
@@ -1475,7 +1519,7 @@ class TmuxSink(Sink):
|
|
|
1475
1519
|
return None
|
|
1476
1520
|
ok = _tmux_wake(self.target)
|
|
1477
1521
|
if ok:
|
|
1478
|
-
led
|
|
1522
|
+
_stamp_wake(led)
|
|
1479
1523
|
return True
|
|
1480
1524
|
if ok is None:
|
|
1481
1525
|
return None # human adopted mid-settle: defer
|
|
@@ -1504,7 +1548,7 @@ class TmuxSink(Sink):
|
|
|
1504
1548
|
return None
|
|
1505
1549
|
ok = _tmux_wake(self.target)
|
|
1506
1550
|
if ok:
|
|
1507
|
-
led
|
|
1551
|
+
_stamp_wake(led)
|
|
1508
1552
|
return True
|
|
1509
1553
|
if ok is None:
|
|
1510
1554
|
return None # human adopted mid-settle: defer
|
|
@@ -1549,7 +1593,8 @@ class TmuxSink(Sink):
|
|
|
1549
1593
|
# The wake text was OBSERVED already sitting in this pane, so
|
|
1550
1594
|
# the injection anchor stays valid for this session — nudging
|
|
1551
1595
|
# it does not re-anchor (the text predates us).
|
|
1552
|
-
|
|
1596
|
+
if "last_wake_injected_at" not in led:
|
|
1597
|
+
_stamp_wake(led)
|
|
1553
1598
|
return True
|
|
1554
1599
|
return False
|
|
1555
1600
|
if composer == "busy":
|
|
@@ -1576,7 +1621,7 @@ class TmuxSink(Sink):
|
|
|
1576
1621
|
return None
|
|
1577
1622
|
if ok:
|
|
1578
1623
|
led["wake_outstanding"] = True
|
|
1579
|
-
led
|
|
1624
|
+
_stamp_wake(led)
|
|
1580
1625
|
return True
|
|
1581
1626
|
# False splits by what the pane actually holds: text observably STUCK
|
|
1582
1627
|
# in the composer -> mark outstanding so the next poll nudges instead
|
|
@@ -1898,7 +1943,17 @@ class MonitorState:
|
|
|
1898
1943
|
continue
|
|
1899
1944
|
led = _new_ledger()
|
|
1900
1945
|
led["last_delivered_id"] = seed
|
|
1901
|
-
|
|
1946
|
+
raw = self.observed.get("last_wake_at")
|
|
1947
|
+
try:
|
|
1948
|
+
stamp = float(raw) if raw is not None else 0.0
|
|
1949
|
+
except (TypeError, ValueError):
|
|
1950
|
+
stamp = 0.0
|
|
1951
|
+
led["last_delivery_at"] = stamp
|
|
1952
|
+
# A positive file value is the only wake time the old cursor had.
|
|
1953
|
+
# 0.0 was the unread default, not a measurement — leave the key
|
|
1954
|
+
# absent so a later read is unknown, not "never".
|
|
1955
|
+
if stamp > 0:
|
|
1956
|
+
led["last_wake_at"] = stamp
|
|
1902
1957
|
self.ledgers[sink.name] = led
|
|
1903
1958
|
|
|
1904
1959
|
def ledger(self, name: str) -> dict:
|
|
@@ -2522,6 +2577,10 @@ def _monitor_iteration(state: MonitorState, *, poll_channels: bool = True) -> No
|
|
|
2522
2577
|
# A delivery deferred by the idle guard (or failed against a dead sink) must
|
|
2523
2578
|
# still land even though no NEW mail arrived, so this runs on every poll.
|
|
2524
2579
|
_monitor_deliver(state)
|
|
2580
|
+
# The observation cursor is written BEFORE delivery, while last_wake_at is
|
|
2581
|
+
# still unknown. A wake that landed this poll has to be written again or
|
|
2582
|
+
# cursor.json keeps the 0.0 default after a real keystroke (#405).
|
|
2583
|
+
_persist_wake_stamp(state)
|
|
2525
2584
|
|
|
2526
2585
|
# MATERIALIZE new ledgers even if nothing moved them. Found by driving the
|
|
2527
2586
|
# real CLI: a pure `--deliver pull` monitor never writes ledgers.json until
|
|
@@ -2783,7 +2842,14 @@ class _LegacyCursorView(MutableMapping):
|
|
|
2783
2842
|
if key == "last_msg_id":
|
|
2784
2843
|
return self._observed.get("last_msg_id", 0)
|
|
2785
2844
|
if key == "last_wake_at":
|
|
2786
|
-
|
|
2845
|
+
raw = self._led().get("last_wake_at")
|
|
2846
|
+
if raw is None:
|
|
2847
|
+
return None
|
|
2848
|
+
try:
|
|
2849
|
+
stamp = float(raw)
|
|
2850
|
+
except (TypeError, ValueError):
|
|
2851
|
+
return None
|
|
2852
|
+
return stamp if stamp > 0 else None
|
|
2787
2853
|
if key == "pending_wake":
|
|
2788
2854
|
return int(self._led()["last_delivered_id"]) < self._observed_id()
|
|
2789
2855
|
raise KeyError(key)
|
|
@@ -2802,7 +2868,11 @@ class _LegacyCursorView(MutableMapping):
|
|
|
2802
2868
|
self._led()["last_delivered_id"] = int(value)
|
|
2803
2869
|
return
|
|
2804
2870
|
if key == "last_wake_at":
|
|
2805
|
-
|
|
2871
|
+
stamp = float(value)
|
|
2872
|
+
self._led()["last_wake_at"] = stamp
|
|
2873
|
+
# The idle guard reads last_delivery_at. Legacy callers suppress
|
|
2874
|
+
# it by assigning this key; keep that clock in step.
|
|
2875
|
+
self._led()["last_delivery_at"] = stamp
|
|
2806
2876
|
return
|
|
2807
2877
|
if key == "pending_wake":
|
|
2808
2878
|
self._led()["last_delivered_id"] = 0 if value else self._observed_id()
|
|
@@ -256,7 +256,7 @@ def run_schedule(argv: list) -> int:
|
|
|
256
256
|
if args.command == "delete":
|
|
257
257
|
return _run_delete(args)
|
|
258
258
|
if args.command == "fire-now":
|
|
259
|
-
return _run_action(args, "fire-now", "fire-now", "
|
|
259
|
+
return _run_action(args, "fire-now", "fire-now", "marked")
|
|
260
260
|
parser.error(f"unknown command: {args.command}")
|
|
261
261
|
except RuntimeError as exc:
|
|
262
262
|
print(f"swarph schedule: {exc}", file=sys.stderr)
|
|
@@ -792,6 +792,72 @@ def _resolve_send_target(name: str, process_name: str = "claude") -> str:
|
|
|
792
792
|
return name
|
|
793
793
|
|
|
794
794
|
|
|
795
|
+
# How long to wait after Enter before reading the composer. tmux rc==0 means
|
|
796
|
+
# the keys were accepted, not that the pane submitted them (#184).
|
|
797
|
+
_SUBMIT_SETTLE_S = 0.6
|
|
798
|
+
|
|
799
|
+
|
|
800
|
+
# Claude's composer is the last ❯ / > line. Everything under the ─── rule is
|
|
801
|
+
# the status footer ("⏵⏵ auto mode on", "✔ Update installed"), which is the
|
|
802
|
+
# last non-empty line of a live pane and is not the composer (#184, #553).
|
|
803
|
+
_COMPOSER_MARKERS = ("❯", ">", "›")
|
|
804
|
+
|
|
805
|
+
|
|
806
|
+
def _composed_text(stdout: str) -> Optional[str]:
|
|
807
|
+
"""Text in the composer, with wrapped continuation lines joined.
|
|
808
|
+
|
|
809
|
+
None when no composer marker is present. The footer below the rule is
|
|
810
|
+
not part of the composer.
|
|
811
|
+
"""
|
|
812
|
+
lines = (stdout or "").splitlines()
|
|
813
|
+
idx = None
|
|
814
|
+
for i, ln in enumerate(lines):
|
|
815
|
+
if ln.strip().startswith(_COMPOSER_MARKERS):
|
|
816
|
+
idx = i
|
|
817
|
+
if idx is None:
|
|
818
|
+
return None
|
|
819
|
+
first = lines[idx].strip()
|
|
820
|
+
for marker in _COMPOSER_MARKERS:
|
|
821
|
+
if first.startswith(marker):
|
|
822
|
+
first = first[len(marker):].strip()
|
|
823
|
+
break
|
|
824
|
+
chunks = [first] if first else []
|
|
825
|
+
for ln in lines[idx + 1:]:
|
|
826
|
+
s = ln.strip()
|
|
827
|
+
if not s:
|
|
828
|
+
continue
|
|
829
|
+
if "───" in s or s.startswith("─"):
|
|
830
|
+
break
|
|
831
|
+
chunks.append(s)
|
|
832
|
+
return " ".join(chunks)
|
|
833
|
+
|
|
834
|
+
|
|
835
|
+
def _composer_still_holds(target: str, text: str) -> Optional[bool]:
|
|
836
|
+
"""Whether the composer, not the status footer, still contains `text`.
|
|
837
|
+
|
|
838
|
+
True = the payload is still composed (accepted, not submitted).
|
|
839
|
+
False = the composer was read and does not hold it.
|
|
840
|
+
None = the pane could not be read, or no composer line was found.
|
|
841
|
+
Callers fail closed on None.
|
|
842
|
+
"""
|
|
843
|
+
try:
|
|
844
|
+
result = subprocess.run(
|
|
845
|
+
["tmux", "capture-pane", "-p", "-t", target],
|
|
846
|
+
capture_output=True, timeout=5, text=True,
|
|
847
|
+
encoding="utf-8", errors="replace",
|
|
848
|
+
)
|
|
849
|
+
except (subprocess.TimeoutExpired, FileNotFoundError, OSError):
|
|
850
|
+
return None
|
|
851
|
+
if result.returncode != 0:
|
|
852
|
+
return None
|
|
853
|
+
composed = _composed_text(result.stdout or "")
|
|
854
|
+
if composed is None:
|
|
855
|
+
return None
|
|
856
|
+
# A wrap inside a token ('la' / 'b-ovh') joins with a space the payload
|
|
857
|
+
# does not have. Fold all whitespace on both sides (#184, #555).
|
|
858
|
+
return re.sub(r"\s+", "", text) in re.sub(r"\s+", "", composed)
|
|
859
|
+
|
|
860
|
+
|
|
795
861
|
def _tmux_send_keys(
|
|
796
862
|
name: str, text: str, clear_input: bool = False, process_name: str = "claude"
|
|
797
863
|
) -> bool:
|
|
@@ -805,6 +871,10 @@ def _tmux_send_keys(
|
|
|
805
871
|
slash-command inject (drop seat-A C2): a prose wake merging into typed
|
|
806
872
|
text is noise, but `/model ...` merging into typed text submits a
|
|
807
873
|
corrupted command.
|
|
874
|
+
|
|
875
|
+
Returns True only when a follow-up capture shows the payload has left
|
|
876
|
+
the composer line. tmux accepting the keys is not delivery (#184). An
|
|
877
|
+
unreadable pane is False: the wake stays retryable.
|
|
808
878
|
"""
|
|
809
879
|
target = _resolve_send_target(name, process_name)
|
|
810
880
|
keys = ["C-u", text, "Enter"] if clear_input else [text, "Enter"]
|
|
@@ -813,7 +883,10 @@ def _tmux_send_keys(
|
|
|
813
883
|
["tmux", "send-keys", "-t", target, *keys],
|
|
814
884
|
capture_output=True, timeout=5,
|
|
815
885
|
)
|
|
816
|
-
|
|
886
|
+
if result.returncode != 0:
|
|
887
|
+
return False
|
|
888
|
+
time.sleep(_SUBMIT_SETTLE_S)
|
|
889
|
+
return _composer_still_holds(target, text) is False
|
|
817
890
|
except (subprocess.TimeoutExpired, FileNotFoundError, OSError):
|
|
818
891
|
return False
|
|
819
892
|
|
|
@@ -824,12 +897,12 @@ def _dm_wake(
|
|
|
824
897
|
target_peer: str,
|
|
825
898
|
token: str,
|
|
826
899
|
content: str,
|
|
827
|
-
) -> bool:
|
|
828
|
-
"""A1-DM:
|
|
900
|
+
) -> str | bool:
|
|
901
|
+
"""A1-DM: POST a wake DM. Never raises.
|
|
829
902
|
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
903
|
+
Returns ``"accepted"`` when the gateway stores the row (HTTP 2xx) and
|
|
904
|
+
``False`` otherwise. ``"accepted"`` is not delivery: this process cannot
|
|
905
|
+
see the recipient ledger, so it must not return True (#184).
|
|
833
906
|
"""
|
|
834
907
|
try:
|
|
835
908
|
body = {
|
|
@@ -843,7 +916,9 @@ def _dm_wake(
|
|
|
843
916
|
body,
|
|
844
917
|
token,
|
|
845
918
|
)
|
|
846
|
-
|
|
919
|
+
if 200 <= status < 300:
|
|
920
|
+
return "accepted"
|
|
921
|
+
return False
|
|
847
922
|
except Exception:
|
|
848
923
|
return False
|
|
849
924
|
|
|
@@ -1113,7 +1188,8 @@ def _notify_peer_event(
|
|
|
1113
1188
|
f"(autonomous ladder action; watchdog log on host has the diag)"
|
|
1114
1189
|
)
|
|
1115
1190
|
diag["notify_peer"] = peer
|
|
1116
|
-
|
|
1191
|
+
# 2xx stored the row. It did not deliver it (#184).
|
|
1192
|
+
diag["notify_accepted"] = _dm_wake(args.gateway, role, peer, token, content) == "accepted"
|
|
1117
1193
|
|
|
1118
1194
|
|
|
1119
1195
|
def _escalate_a2(
|
|
@@ -1259,17 +1335,16 @@ def _dm_wake_scan(
|
|
|
1259
1335
|
local session check uses.
|
|
1260
1336
|
|
|
1261
1337
|
T4 — per-peer no-spam cooldown. A peer that stays stale across many ticks
|
|
1262
|
-
is
|
|
1338
|
+
is POSTed ONCE per ``--dm-wake-cooldown-sec`` window, not every tick. The
|
|
1263
1339
|
cooldown state ``{peer: last_wake_epoch}`` is loaded once at scan start;
|
|
1264
|
-
inside the loop a peer is SKIPPED while ``now - last_wake < cooldown_sec
|
|
1265
|
-
|
|
1266
|
-
|
|
1340
|
+
inside the loop a peer is SKIPPED while ``now - last_wake < cooldown_sec``.
|
|
1341
|
+
Only a gateway-accepted POST stamps the cooldown; a rejected POST is
|
|
1342
|
+
retried next tick. The state is saved once after the loop.
|
|
1267
1343
|
|
|
1268
1344
|
``now_epoch`` defaults to ``_now()`` (injectable for deterministic tests).
|
|
1269
1345
|
|
|
1270
|
-
Returns
|
|
1271
|
-
|
|
1272
|
-
raises (an alert path must not crash the watchdog).
|
|
1346
|
+
Returns 0. A 2xx is the gateway storing the row, not the recipient's
|
|
1347
|
+
ledger, so this scan does not report a fired wake (#184). Never raises.
|
|
1273
1348
|
"""
|
|
1274
1349
|
if not args.dm_wake:
|
|
1275
1350
|
return 0
|
|
@@ -1300,20 +1375,21 @@ def _dm_wake_scan(
|
|
|
1300
1375
|
# T4 — load the per-peer cooldown map once at scan start.
|
|
1301
1376
|
state = _load_dm_wake_state(state_path)
|
|
1302
1377
|
|
|
1303
|
-
|
|
1378
|
+
accepted = 0
|
|
1304
1379
|
skipped: list[str] = []
|
|
1305
1380
|
for target in stale:
|
|
1306
1381
|
last_wake = state.get(target, 0)
|
|
1307
1382
|
if now_epoch - last_wake < cooldown_sec:
|
|
1308
|
-
# Within cooldown — already
|
|
1309
|
-
# cooldown-skip must NOT count toward the exit-3 wake tally.
|
|
1383
|
+
# Within cooldown — already POSTed this peer recently. Skip.
|
|
1310
1384
|
skipped.append(target)
|
|
1311
1385
|
continue
|
|
1312
|
-
if _dm_wake(gateway, self_peer, target, token or "", _DM_WAKE_PROMPT):
|
|
1313
|
-
#
|
|
1314
|
-
#
|
|
1386
|
+
if _dm_wake(gateway, self_peer, target, token or "", _DM_WAKE_PROMPT) == "accepted":
|
|
1387
|
+
# Gateway stored the row. Stamp the cooldown so the next tick
|
|
1388
|
+
# does not re-POST, but do NOT count it as a fired wake: a 2xx
|
|
1389
|
+
# is not the recipient's ledger (#184). A rejected POST leaves
|
|
1390
|
+
# the cooldown unset so the next tick retries.
|
|
1315
1391
|
state[target] = now_epoch
|
|
1316
|
-
|
|
1392
|
+
accepted += 1
|
|
1317
1393
|
|
|
1318
1394
|
# Persist the (possibly-updated) cooldown map once after the loop.
|
|
1319
1395
|
_save_dm_wake_state(state_path, state)
|
|
@@ -1325,12 +1401,13 @@ def _dm_wake_scan(
|
|
|
1325
1401
|
"self_peer": self_peer,
|
|
1326
1402
|
"stale_peers": stale,
|
|
1327
1403
|
"skipped_cooldown": skipped,
|
|
1328
|
-
"
|
|
1404
|
+
"wakes_accepted": accepted,
|
|
1405
|
+
"wakes_fired": 0,
|
|
1329
1406
|
"cooldown_sec": cooldown_sec,
|
|
1330
1407
|
},
|
|
1331
1408
|
args.verbose,
|
|
1332
1409
|
)
|
|
1333
|
-
return
|
|
1410
|
+
return 0
|
|
1334
1411
|
|
|
1335
1412
|
|
|
1336
1413
|
def run_check(args: argparse.Namespace) -> int:
|
|
@@ -1338,13 +1415,11 @@ def run_check(args: argparse.Namespace) -> int:
|
|
|
1338
1415
|
exit 0/1/2/3/4 — byte-for-byte unchanged), then layers the ``--dm-wake``
|
|
1339
1416
|
mesh-monitor scan on top.
|
|
1340
1417
|
|
|
1341
|
-
Exit precedence (T3): the local own-session action wins.
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
fired do we surface exit 3 (A1-DM). When ``--dm-wake`` is off the scan is
|
|
1347
|
-
a pure no-op, so the 0/1/2 behavior is preserved exactly.
|
|
1418
|
+
Exit precedence (T3): the local own-session action wins. The dm-wake
|
|
1419
|
+
scan POSTs and returns 0: a gateway accept is not a delivered wake, so
|
|
1420
|
+
it does not surface exit 3 (#184). Exit 3 remains the local detection
|
|
1421
|
+
error (unreadable cursor). When ``--dm-wake`` is off the scan is a
|
|
1422
|
+
pure no-op.
|
|
1348
1423
|
"""
|
|
1349
1424
|
log_path = _resolve_log_path(args.log)
|
|
1350
1425
|
global _HEALTH_EMITTER
|
|
@@ -3117,7 +3117,7 @@ async def scheduled_event_fire_now(name: str,
|
|
|
3117
3117
|
c.execute(
|
|
3118
3118
|
"UPDATE scheduled_events SET last_fired_at = ?, last_status = 'fired', "
|
|
3119
3119
|
"fire_count = fire_count + 1 WHERE name = ?", (now, name))
|
|
3120
|
-
return {"name": name, "fired": True, "fired_at": now,
|
|
3120
|
+
return {"name": name, "fired": False, "marked": True, "fired_at": now,
|
|
3121
3121
|
"target_cell": row["target_cell"], "task": row["task"],
|
|
3122
3122
|
"context_ref": json.loads(row["context_ref"])}
|
|
3123
3123
|
|
|
@@ -16,11 +16,28 @@ from typing import Iterable
|
|
|
16
16
|
GAP_SECONDS = 60
|
|
17
17
|
|
|
18
18
|
|
|
19
|
+
def _channel_alive(inbox: str, now: float | None = None) -> bool:
|
|
20
|
+
"""A channel server writes channel_heartbeat.json beside the inbox every 60 s.
|
|
21
|
+
|
|
22
|
+
`swarph channel-serve` takes no inbox argument, so no command line names the
|
|
23
|
+
path; a fresh heartbeat is the only evidence the channel is reading.
|
|
24
|
+
"""
|
|
25
|
+
hb = Path(inbox).parent / "channel_heartbeat.json"
|
|
26
|
+
try:
|
|
27
|
+
ts = float(json.loads(hb.read_text(encoding="utf-8")).get("ts") or 0)
|
|
28
|
+
except (OSError, ValueError, TypeError, AttributeError):
|
|
29
|
+
return False
|
|
30
|
+
return (time.time() if now is None else now) - ts < 180
|
|
31
|
+
|
|
32
|
+
|
|
19
33
|
def _watching(inbox: str, cmdlines: Iterable[str]) -> bool:
|
|
20
|
-
"""A reader is tail or dm_notify_filter with the inbox path as its own argument
|
|
34
|
+
"""A reader is tail or dm_notify_filter with the inbox path as its own argument,
|
|
35
|
+
or a channel server whose heartbeat is fresh.
|
|
21
36
|
|
|
22
37
|
A process that merely mentions the path (a prompt, a log line) is not a reader.
|
|
23
38
|
"""
|
|
39
|
+
if _channel_alive(inbox):
|
|
40
|
+
return True
|
|
24
41
|
for cmd in cmdlines:
|
|
25
42
|
parts = cmd.split()
|
|
26
43
|
if inbox not in parts:
|
|
@@ -29,15 +46,6 @@ def _watching(inbox: str, cmdlines: Iterable[str]) -> bool:
|
|
|
29
46
|
return True
|
|
30
47
|
if any(p.endswith("dm_notify_filter") or p.endswith("dm_notify_filter.py") for p in parts):
|
|
31
48
|
return True
|
|
32
|
-
if any(p.endswith("swarph_cli.channel") or p.endswith("channel.py") for p in parts):
|
|
33
|
-
hb = Path(inbox).parent / "channel_heartbeat.json"
|
|
34
|
-
try:
|
|
35
|
-
data = json.loads(hb.read_text(encoding="utf-8"))
|
|
36
|
-
ts = float(data.get("ts") or 0)
|
|
37
|
-
except (OSError, ValueError, TypeError):
|
|
38
|
-
continue
|
|
39
|
-
if time.time() - ts < 180:
|
|
40
|
-
return True
|
|
41
49
|
return False
|
|
42
50
|
|
|
43
51
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: swarph-cli
|
|
3
|
-
Version: 0.68.
|
|
3
|
+
Version: 0.68.2
|
|
4
4
|
Summary: The `swarph` binary — a multi-LLM CLI and mesh-gateway client + bundled server (`swarph gateway`): one-shot prompts across providers, interactive `chat`, multi-provider `spawn` (claude/codex/antigravity via a ProviderMembrane with subprocess billing-scrub), `mesh` send/inbox/register, `brain-ask` over the swarph-brain (gbrain) semantic memory, git-backed `assisted_memory`, per-invocation `gh` identity routing, a silent DM-wake hook, session `import`, and a stranded-session `watchdog`.
|
|
5
5
|
Author: Pierre Samson, Claude Opus
|
|
6
6
|
License: MIT
|
|
@@ -182,6 +182,7 @@ tests/test_132_ensure_monitor_state_dir.py
|
|
|
182
182
|
tests/test_138_memory_navigate.py
|
|
183
183
|
tests/test_139_tool_probe.py
|
|
184
184
|
tests/test_183_card_anchor.py
|
|
185
|
+
tests/test_184_notify_honesty.py
|
|
185
186
|
tests/test_201_win32_bash_refusal.py
|
|
186
187
|
tests/test_243_onboard_per_peer_token.py
|
|
187
188
|
tests/test_307_task4_ask_reply.py
|
|
@@ -227,6 +228,7 @@ tests/test_764_enrich_collapse.py
|
|
|
227
228
|
tests/test_801_confirm_flow_cli.py
|
|
228
229
|
tests/test_802_stage_history.py
|
|
229
230
|
tests/test_825_codegraph_hook_noise.py
|
|
231
|
+
tests/test_825_install_posttooluse_only.py
|
|
230
232
|
tests/test_825_remaining_cap_shred_bind.py
|
|
231
233
|
tests/test_829_counterfactual_column.py
|
|
232
234
|
tests/test_830_hook_binding_version.py
|