swarph-cli 0.69.9__tar.gz → 0.70.0__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.69.9/src/swarph_cli.egg-info → swarph_cli-0.70.0}/PKG-INFO +1 -1
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/pyproject.toml +1 -1
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/__init__.py +1 -1
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/mesh.py +172 -30
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/monitor.py +16 -8
- {swarph_cli-0.69.9 → swarph_cli-0.70.0/src/swarph_cli.egg-info}/PKG-INFO +1 -1
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli.egg-info/SOURCES.txt +1 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_644_monitor_supervision.py +86 -11
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_739_wake_writer.py +48 -0
- swarph_cli-0.70.0/tests/test_961_cursor_grok_footer.py +331 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/LICENSE +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/README.md +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/setup.cfg +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/__main__.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/bench/__init__.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/bench/backends.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/bench/claude_cli.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/bench/pack.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/bench/prices.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/bench/providers.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/bench/quality.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/bench/refresh_prices.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/bench/rules.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/bench/runner.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/bench/validate.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/caller.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/capture/__init__.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/capture/harden.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/capture/lineage.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/capture/liveness.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/capture/manifest.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/capture/paths.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/capture/verify.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/cell.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/chain_token.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/channel.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/__init__.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/_content.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/_display.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/_gateway_client.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/add.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/bench.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/board.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/board_merge_check.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/brain.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/brain_ask.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/cell.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/cell_probe.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/channel.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/chat.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/codegraph.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/codegraph_hook.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/codex_hook_output.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/codex_waker.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/compress.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/daemon.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/dreaming.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/event.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/gateway.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/gh_route.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/group.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/guide.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/highlight.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/hook_interpreter.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/hook_output.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/hooks.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/import_session.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/init.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/install_hook.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/install_multiplexer.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/install_wake_hook.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/lane.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/mcp_server.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/memory.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/memory_sync.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/okf_links.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/onboard.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/protocol_handler.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/ratify.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/schedule.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/security.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/service.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/spawn.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/timeline.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/version.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/wake_hook_output.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/commands/watchdog.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/compress/__init__.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/compress/levers.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/compress/marker.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/compress/verify.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/console_safe.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/delivery_queue.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/dm_frame.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/__init__.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/candidates.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/clone.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/enrich.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/index_check.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/notify.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/organize.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/probes.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/report.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/run.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/slm.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/transcripts.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/dreaming/verify.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/gateway/__init__.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/gateway/feature_registry.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/gateway/lanes_control.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/gateway/schema.sql +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/gateway/server.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/gateway/services_control.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/gateway_default.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/gh_identity.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/guide/GUIDE.md +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/identity.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/main.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/multiplexer.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/orphan_daemons.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/pane_probe.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/parsers/__init__.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/parsers/claude.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/peer_executor.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/peer_executor_windows.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/peer_reconciliation.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/peer_reply_outbox.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/peer_service_host.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/peer_service_payload.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/peer_service_reply.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/peer_staging.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/query_filters.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/scripts/__init__.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/scripts/wake_watchdog.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/service/__init__.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/service/app.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/service/providers.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/session_bridge.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/source_text.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/stall_alert.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/systemd/codex-waker.default +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/timeline_paths.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli/tokens.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli.egg-info/entry_points.txt +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli.egg-info/requires.txt +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/src/swarph_cli.egg-info/top_level.txt +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_1006_session_board.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_124_register_merge.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_126_pending_from_complete.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_132_ensure_monitor_state_dir.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_138_memory_navigate.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_139_tool_probe.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_183_card_anchor.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_184_notify_honesty.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_201_win32_bash_refusal.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_243_onboard_per_peer_token.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_307_task4_ask_reply.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_319_refusal_exit_codes.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_323_mistral_backend.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_332_report_organize_findings.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_356_query_filters.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_397_gh_identity_router.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_397_router_preflight.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_402_one_identity_resolver.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_467b_onboard_operator_credential.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_472_register_caller_binding.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_523_guide.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_524_conftest_hermeticity.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_532_accept_flag.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_535_version_capability.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_546_default_gateway.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_547_verbs_from_registry.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_548_gateway_remainder.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_552_codegraph_gateway.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_555_relay_row_shape.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_556_relay_freshness.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_559_edges_truncated.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_564_bootstrap_first_peer.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_579_register_bootstrap_message.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_591_cli_step_graph.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_648_install_unit.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_654_reexec.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_656_owned_slm.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_684_dreaming_verb.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_689_pkg_version_order.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_715_grok_wake.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_716_timeline_one_path.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_723_grok_0251.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_724_ps1_ascii_or_bom.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_725_stdio_reconfigure.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_726_grok_limit_screen.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_729_dm_once.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_729_wake_watchdog.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_734_dreaming_enrich_degrade.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_735_dm_mcp_tools.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_740_edit_project.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_762_cards_edit_priority.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_764_enrich_collapse.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_801_confirm_flow_cli.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_802_stage_history.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_825_codegraph_hook_noise.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_825_install_posttooluse_only.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_825_remaining_cap_shred_bind.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_829_counterfactual_column.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_830_hook_binding_version.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_84_cli_labels.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_866_doctrine_guide.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_872_identity_refuses_not_warns.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_874_source_text.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_937_dreaming_notify.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_938_organize_line_limit.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_941_bench_backends.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_941_harness_342.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_945_same_line_pairing.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_955_prompt_audit.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_957_auth_http.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_959_venv_interpreter.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_960_channel.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_960_ensure_monitor_heartbeat.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_963_plugin_manifest.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_964_channel_cell.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_965_mesh_wait.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_975_channel_hop.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_add_security_gate.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_artifact_add.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_artifact_hash.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_artifact_lib.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_artifact_mcp.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_artifact_skill.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_artifact_tool.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_artifact_uri.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_bench_backends.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_bench_claude_cli.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_bench_command.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_bench_pack.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_bench_prices.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_bench_quality.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_bench_runner.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_bench_validate.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_board_card_thread.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_board_command.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_board_merge_check.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_board_obligation_close.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_brain_ask_command.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_brain_ask_env_diagnosis.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_brain_ask_identity_explicitness.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_brain_command.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_caller_meta_guard.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_capture_lineage.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_capture_liveness.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_capture_manifest.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_capture_paths.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_capture_security.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_cell_command_dispatch.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_cell_harden.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_cell_loader.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_cell_selfcheck.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_cell_verify.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_chain_token.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_channel_command.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_channel_poll_never_breaks_dms.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_channel_post.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_channel_wake_policy_filter.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_chat_command.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_claude_parser.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_claude_tmux_template.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_codegraph_caller_scope.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_codegraph_command.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_codegraph_hook.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_codegraph_mcp.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_codex_waker.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_compress_caller_convention.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_compress_command.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_compress_levers.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_compress_marker.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_compress_verify.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_console_encoding_e2e.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_content_composition.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_cursor_membrane.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_daemon_command.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_daemon_console_encoding.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_daemon_delivery.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_delivery_queue.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_display_sanitize.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_ensure_monitor_no_peer_default.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_event_emit.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_feature_to_uri.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_gateway_brain.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_gateway_client.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_gateway_command.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_gateway_message_idempotency.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_gh_route_doctor_mesh_name.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_group_command.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_guide_commands_parse.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_guide_selfcheck.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_heartbeat_check.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_highlight_command.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_hook_output.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_hooks_activity_marker.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_hooks_add.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_hooks_bundle.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_hooks_init.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_hooks_legacy_generation_removal.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_hooks_lifecycle.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_hooks_merge.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_hooks_verify.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_hooks_win32_bash_wrapper.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_hooks_win32_command_path.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_import_command.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_inbox_identity_provenance.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_init_command.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_init_provider_menu.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_install_codex_hooks.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_install_hook.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_install_multiplexer_command.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_install_postcompact_hook.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_install_wake_hook.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_lane_command.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_main.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_main_unknown_verb_guard.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_mcp_missing_hint.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_mcp_server.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_memory_command.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_memory_emit_snippet.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_memory_gateway.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_memory_sync.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_memory_sync_membrane.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_mesh_command.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_mesh_inbox_mark_read.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_mesh_inbox_sanitize.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_mesh_originate_thread_623.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_mesh_send_unknown_peer.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_mesh_sidecar.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_meta_edge_identity.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_monitor_channel_poll.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_monitor_cli.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_monitor_daemon_collision.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_monitor_self_name_verification.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_monitor_state_split.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_multiplexer.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_muse_membrane.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_muse_own_cli.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_obligation_close_is_surfaced.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_okf_links.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_onboard_command.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_opencode_membrane.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_opencode_plugin.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_orphan_daemons.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_packaged_artifacts_exist.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_packaged_skills.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_peer_executor.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_peer_executor_windows.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_peer_reply_drain_command.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_peer_reply_drain_supervisor.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_peer_reply_outbox.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_peer_reply_transport.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_peer_service_e2e.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_peer_service_host.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_peer_service_payload.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_peer_service_reply.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_postcompact_recall.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_process_liveness.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_protocol_handler.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_providers_isolation.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_ratify_command.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_readme_claims_no_gateway_default.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_repl_caller_convention.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_schedule_command.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_schedule_create_contract.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_security.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_service_app.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_service_command.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_service_providers.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_session_bridge_inject.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_session_bridge_probe.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_session_bridge_resolve.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_session_path_role_gate.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_shipped_units_match_the_cli.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_smoke_chat.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_smoke_one_shot.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_smoke_phase_5_5.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_spawn_base_hoist.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_spawn_command.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_spawn_identity_stamp.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_spawn_live_pin.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_spawn_mitosis_lineage.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_spawn_tmux_session.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_spawn_windows_relaunch.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_stall_alert.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_subprocess_decode_encoding.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_timeline_command.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_tmux_wake_runstate_defer.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_tmux_wake_session_reanchor.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_tmux_wake_stale.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_tmux_wake_submit_verify.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_token_file_one_parser.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_token_precedence_explicit_first.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_unified_token_resolver.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_version_command.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_version_consistency.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_vibe_membrane.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_watchdog.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_watchdog_dm_wake.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_watchdog_dm_wake_wiring.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_watchdog_emit_health.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_watchdog_install_flags.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_watchdog_model_rung.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/tests/test_watchdog_stale_peers.py +0 -0
- {swarph_cli-0.69.9 → swarph_cli-0.70.0}/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.
|
|
3
|
+
Version: 0.70.0
|
|
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.
|
|
7
|
+
version = "0.70.0"
|
|
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" }
|
|
@@ -2136,6 +2136,33 @@ _COMPOSER_PLACEHOLDERS = ("Add a follow-up", "Ask Codex to do anything")
|
|
|
2136
2136
|
#: running agent into an idle one forever.
|
|
2137
2137
|
_CURSOR_RUN_HINT = "ctrl+c to stop"
|
|
2138
2138
|
|
|
2139
|
+
#: A live cursor turn draws a braille spinner and then ``Thinking``,
|
|
2140
|
+
#: ``Running``, or ``Working`` on the row directly above the Tip/composer
|
|
2141
|
+
#: block. A quote of that row anywhere else, including a code echo, is not
|
|
2142
|
+
#: a turn. The match is anchored: stripped text has to start with the glyph.
|
|
2143
|
+
_CURSOR_SPINNER_ROW = re.compile(
|
|
2144
|
+
r"^[\u2800-\u28FF]+\s*(?:Thinking|Running|Working)\b"
|
|
2145
|
+
)
|
|
2146
|
+
|
|
2147
|
+
#: How many non-empty rows at the bottom can hold the cursor composer.
|
|
2148
|
+
#: Measured on cursor-lin 2026-09-30: the composer is the 4th non-empty row
|
|
2149
|
+
#: from the bottom (composer, task count, model footer, tilde). A quote of
|
|
2150
|
+
#: that row higher in the pane is scrollback, not the composer. Blank rows
|
|
2151
|
+
#: between those chrome lines do not count: a raw tail of N lines drops the
|
|
2152
|
+
#: composer off the window when the screen interleaves blanks.
|
|
2153
|
+
_CURSOR_COMPOSER_TAIL = 6
|
|
2154
|
+
|
|
2155
|
+
#: The 1.18.33 permission dialog draws ``△ Permission required`` above a
|
|
2156
|
+
#: stack of option rows. On the measured screen that row is the 10th
|
|
2157
|
+
#: non-empty row from the bottom, outside the composer tail.
|
|
2158
|
+
_OPENCODE_PERMISSION_TAIL = 12
|
|
2159
|
+
|
|
2160
|
+
|
|
2161
|
+
def _nonempty_tail(lines: list[str], n: int) -> list[str]:
|
|
2162
|
+
"""The last ``n`` rows that contain text. Blank rows are not rows."""
|
|
2163
|
+
filled = [ln for ln in lines if ln.strip()]
|
|
2164
|
+
return filled[-n:]
|
|
2165
|
+
|
|
2139
2166
|
|
|
2140
2167
|
def _only_wake_text(content: str) -> bool:
|
|
2141
2168
|
"""True when the composer holds wake text and nothing else.
|
|
@@ -2172,19 +2199,21 @@ def _opencode_permission_row(line: str) -> bool:
|
|
|
2172
2199
|
return s.startswith("┃") and "△" in s and "Permission required" in s
|
|
2173
2200
|
|
|
2174
2201
|
|
|
2202
|
+
def _is_opencode_composer_row(line: str) -> bool:
|
|
2203
|
+
"""One row of the opencode box: the mode row, the ``╹▀`` border, or the
|
|
2204
|
+
permission dialog. A quote of ``┃ Build ·`` above the tail is not this."""
|
|
2205
|
+
s = line.strip()
|
|
2206
|
+
return s.startswith("╹▀") or _opencode_mode_row(line) or _opencode_permission_row(line)
|
|
2207
|
+
|
|
2208
|
+
|
|
2175
2209
|
def _is_opencode_pane(lines: list[str]) -> bool:
|
|
2176
2210
|
"""The opencode composer box, measured on 1.18.33.
|
|
2177
2211
|
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
Cursor and claude do not draw any of these.
|
|
2212
|
+
Decided by ``_bottom_tui``: the mode row, the ``╹▀`` border, or the
|
|
2213
|
+
permission dialog has to be the bottom-most composer in the non-empty
|
|
2214
|
+
tail. A cursor pane that merely quotes ``┃ Build ·`` is not opencode.
|
|
2182
2215
|
"""
|
|
2183
|
-
|
|
2184
|
-
s = ln.strip()
|
|
2185
|
-
if s.startswith("╹▀") or _opencode_mode_row(ln) or _opencode_permission_row(ln):
|
|
2186
|
-
return True
|
|
2187
|
-
return False
|
|
2216
|
+
return bool(lines) and _bottom_tui(lines) == "opencode"
|
|
2188
2217
|
|
|
2189
2218
|
|
|
2190
2219
|
def _opencode_running(lines: list[str]) -> bool:
|
|
@@ -2281,29 +2310,115 @@ def _opencode_composer_state(lines: list[str]) -> Optional[str]:
|
|
|
2281
2310
|
return "busy"
|
|
2282
2311
|
|
|
2283
2312
|
|
|
2284
|
-
def
|
|
2285
|
-
"""
|
|
2313
|
+
def _is_grok_composer_row(lines: list[str], index: int) -> bool:
|
|
2314
|
+
"""A grok composer starts at a ``│ ❯`` row. Continuation rows that also
|
|
2315
|
+
start with ``│`` may sit between it and the ``╰`` footer, which is how
|
|
2316
|
+
a wrapped draft is drawn. A cursor diff hunk (``▎``) never counts."""
|
|
2317
|
+
row = lines[index].strip()
|
|
2318
|
+
if row.startswith("▎") or not (row.startswith("│") and "❯" in row):
|
|
2319
|
+
return False
|
|
2320
|
+
j = index + 1
|
|
2321
|
+
while j < len(lines):
|
|
2322
|
+
nxt = lines[j].strip()
|
|
2323
|
+
if nxt.startswith("▎"):
|
|
2324
|
+
return False
|
|
2325
|
+
if nxt.startswith("╰"):
|
|
2326
|
+
return True
|
|
2327
|
+
if nxt.startswith("│"):
|
|
2328
|
+
j += 1
|
|
2329
|
+
continue
|
|
2330
|
+
return False
|
|
2331
|
+
return False
|
|
2286
2332
|
|
|
2287
|
-
The footer names ``Grok 4.`` inside the box, or the splash prints
|
|
2288
|
-
``Grok Build``. History also draws a ``❯`` for the submitted prompt;
|
|
2289
|
-
that row has no box bar, so it is not the composer. A usage-limit
|
|
2290
|
-
modal drops both of those and is recognized by ``_grok_block_reason``.
|
|
2291
|
-
"""
|
|
2292
|
-
return any("Grok 4." in ln or ln.strip().startswith("Grok Build") for ln in lines)
|
|
2293
2333
|
|
|
2334
|
+
def _is_cursor_composer_row(line: str) -> bool:
|
|
2335
|
+
"""Any cursor composer row, including a human draft. The empty one
|
|
2336
|
+
reads ``→ Add a follow-up``. A ``▎`` hunk never counts."""
|
|
2337
|
+
row = line.strip()
|
|
2338
|
+
return row.startswith("→") and not row.startswith("▎")
|
|
2294
2339
|
|
|
2295
|
-
|
|
2296
|
-
|
|
2297
|
-
row
|
|
2298
|
-
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2340
|
+
|
|
2341
|
+
def _bottom_tui(lines: list[str]) -> "str | None":
|
|
2342
|
+
"""Who owns the bottom-most composer row in the non-empty tail.
|
|
2343
|
+
|
|
2344
|
+
``"cursor"``, ``"grok"``, or ``"opencode"``. A match above that tail
|
|
2345
|
+
is scrollback. The lower composer wins, so a quoted grok box or a
|
|
2346
|
+
quoted ``┃ Build ·`` row above the cursor composer does not take the pane.
|
|
2347
|
+
"""
|
|
2348
|
+
tail = _nonempty_tail(lines, _CURSOR_COMPOSER_TAIL)
|
|
2349
|
+
for i in range(len(tail) - 1, -1, -1):
|
|
2350
|
+
if _is_cursor_composer_row(tail[i]):
|
|
2351
|
+
return "cursor"
|
|
2352
|
+
if _is_grok_composer_row(tail, i):
|
|
2353
|
+
return "grok"
|
|
2354
|
+
if _is_opencode_composer_row(tail[i]):
|
|
2355
|
+
return "opencode"
|
|
2356
|
+
# A permission dialog is taller than the composer tail. Look further
|
|
2357
|
+
# for that one row. A mode row or a cursor/grok composer in the same
|
|
2358
|
+
# stretch is a quote, not a reason to take the pane.
|
|
2359
|
+
wider = _nonempty_tail(lines, _OPENCODE_PERMISSION_TAIL)
|
|
2360
|
+
for i in range(len(wider) - 1, -1, -1):
|
|
2361
|
+
if _is_cursor_composer_row(wider[i]) or _is_grok_composer_row(wider, i):
|
|
2362
|
+
return None
|
|
2363
|
+
if not _is_opencode_composer_row(wider[i]):
|
|
2364
|
+
continue
|
|
2365
|
+
if _opencode_permission_row(wider[i]):
|
|
2366
|
+
return "opencode"
|
|
2302
2367
|
return None
|
|
2303
|
-
|
|
2368
|
+
return None
|
|
2369
|
+
|
|
2370
|
+
|
|
2371
|
+
def _is_cursor_composer(lines: list[str]) -> bool:
|
|
2372
|
+
"""The cursor composer ROW, not a quote of its placeholder.
|
|
2373
|
+
|
|
2374
|
+
The row is the bottom-most composer inside the non-empty tail, the
|
|
2375
|
+
same row ``_composer_state`` reads. Its stripped text has to start
|
|
2376
|
+
with ``→ Add a follow-up``. A mention of that placeholder anywhere
|
|
2377
|
+
else in the pane is not the composer.
|
|
2378
|
+
"""
|
|
2379
|
+
return bool(lines) and _bottom_tui(lines) == "cursor"
|
|
2380
|
+
|
|
2381
|
+
|
|
2382
|
+
def _is_grok_pane(lines: list[str]) -> bool:
|
|
2383
|
+
"""The grok TUI, by the composer row at the bottom of the pane.
|
|
2384
|
+
|
|
2385
|
+
Cursor's footer can read ``Grok 4.7``. That string is not the test.
|
|
2386
|
+
A grok composer is a ``│ ❯`` row immediately followed by ``╰``, and
|
|
2387
|
+
only when that pair is the bottom-most composer in the non-empty tail.
|
|
2388
|
+
A ``│``/``❯`` pair or a ``╰`` / ``always-approve`` pair anywhere else,
|
|
2389
|
+
including a cursor diff hunk that starts with ``▎``, is not a grok pane.
|
|
2390
|
+
A history ``❯`` without the box bar is not the composer. A usage-limit
|
|
2391
|
+
modal drops the box and is recognized by ``_grok_block_reason``.
|
|
2392
|
+
"""
|
|
2393
|
+
return bool(lines) and _bottom_tui(lines) == "grok"
|
|
2394
|
+
|
|
2395
|
+
|
|
2396
|
+
def _grok_box_text(lines: list[str], index: int) -> str:
|
|
2397
|
+
"""The draft inside a grok box, including ``│`` continuation rows."""
|
|
2398
|
+
after = lines[index].split("❯", 1)[1]
|
|
2304
2399
|
if "│" in after:
|
|
2305
2400
|
after = after.split("│", 1)[0]
|
|
2306
|
-
|
|
2401
|
+
parts = [after.strip()]
|
|
2402
|
+
j = index + 1
|
|
2403
|
+
while j < len(lines) and lines[j].strip().startswith("│"):
|
|
2404
|
+
body = lines[j].strip()[1:]
|
|
2405
|
+
if "│" in body:
|
|
2406
|
+
body = body.split("│", 1)[0]
|
|
2407
|
+
parts.append(body.strip())
|
|
2408
|
+
j += 1
|
|
2409
|
+
return " ".join(p for p in parts if p)
|
|
2410
|
+
|
|
2411
|
+
|
|
2412
|
+
def _grok_input(lines: list[str]) -> Optional[str]:
|
|
2413
|
+
"""Text in the live grok box, '' when empty, None if that box is not
|
|
2414
|
+
the bottom composer. Continuation rows count as part of the draft."""
|
|
2415
|
+
tail = _nonempty_tail(lines, _CURSOR_COMPOSER_TAIL)
|
|
2416
|
+
for i in range(len(tail) - 1, -1, -1):
|
|
2417
|
+
if _is_cursor_composer_row(tail[i]) or _is_opencode_composer_row(tail[i]):
|
|
2418
|
+
return None
|
|
2419
|
+
if _is_grok_composer_row(tail, i):
|
|
2420
|
+
return _grok_box_text(tail, i)
|
|
2421
|
+
return None
|
|
2307
2422
|
|
|
2308
2423
|
|
|
2309
2424
|
# One grok screen, measured on the live 0.2.51 pane (52 rows). Lines above
|
|
@@ -2421,10 +2536,28 @@ def _agent_running(target: str) -> Optional[bool]:
|
|
|
2421
2536
|
return _opencode_running(lines)
|
|
2422
2537
|
if _is_grok_pane(lines):
|
|
2423
2538
|
return _grok_running(lines)
|
|
2424
|
-
|
|
2425
|
-
|
|
2539
|
+
tail = _nonempty_tail(lines, _CURSOR_COMPOSER_TAIL)
|
|
2540
|
+
composer = _composer_line(tail)
|
|
2541
|
+
if composer is None or not composer.startswith("→"):
|
|
2426
2542
|
return None
|
|
2427
|
-
|
|
2543
|
+
if _CURSOR_RUN_HINT in composer:
|
|
2544
|
+
return True
|
|
2545
|
+
# The spinner is the non-empty row directly above the Tip/composer
|
|
2546
|
+
# block: the Tip row when it is present, otherwise the composer.
|
|
2547
|
+
# No other row in the tail can make the pane read as running.
|
|
2548
|
+
idx = None
|
|
2549
|
+
for i in range(len(tail) - 1, -1, -1):
|
|
2550
|
+
if _is_cursor_composer_row(tail[i]):
|
|
2551
|
+
idx = i
|
|
2552
|
+
break
|
|
2553
|
+
if idx is None or idx == 0:
|
|
2554
|
+
return False
|
|
2555
|
+
anchor = idx - 1
|
|
2556
|
+
if tail[anchor].strip().startswith("Tip:"):
|
|
2557
|
+
if anchor == 0:
|
|
2558
|
+
return False
|
|
2559
|
+
anchor -= 1
|
|
2560
|
+
return bool(_CURSOR_SPINNER_ROW.match(tail[anchor].strip()))
|
|
2428
2561
|
|
|
2429
2562
|
|
|
2430
2563
|
def _composer_state(target: str) -> Optional[str]:
|
|
@@ -2452,6 +2585,12 @@ def _composer_state(target: str) -> Optional[str]:
|
|
|
2452
2585
|
if _is_grok_pane(lines):
|
|
2453
2586
|
return _grok_composer_state(lines)
|
|
2454
2587
|
composer = _composer_line(lines)
|
|
2588
|
+
if composer is not None and composer.startswith("→"):
|
|
2589
|
+
# A cursor marker above the bottom tail is a quoted row. The
|
|
2590
|
+
# composer itself is off-screen, so this pane is not readable.
|
|
2591
|
+
composer = _composer_line(_nonempty_tail(lines, _CURSOR_COMPOSER_TAIL))
|
|
2592
|
+
if composer is None or not composer.startswith("→"):
|
|
2593
|
+
return None
|
|
2455
2594
|
if composer is None:
|
|
2456
2595
|
return None
|
|
2457
2596
|
content = composer[1:].strip()
|
|
@@ -2976,8 +3115,11 @@ def _monitor_loop(state: MonitorState) -> int:
|
|
|
2976
3115
|
try:
|
|
2977
3116
|
_monitor_iteration(state)
|
|
2978
3117
|
except KeyboardInterrupt:
|
|
3118
|
+
# An interrupt during an iteration is not a clean return. Swallowing
|
|
3119
|
+
# it and returning 0 made _run_pinned delete monitor.pid, so the
|
|
3120
|
+
# watchdog then reported the cell unsupervised.
|
|
2979
3121
|
state.shutdown_requested = True
|
|
2980
|
-
|
|
3122
|
+
raise
|
|
2981
3123
|
except Exception as exc:
|
|
2982
3124
|
print(f"{state.log_prefix} iteration error: {type(exc).__name__}: {exc}",
|
|
2983
3125
|
file=sys.stderr)
|
|
@@ -345,13 +345,17 @@ def _service_leaf(text: "str | None") -> "str | None":
|
|
|
345
345
|
def _supervisor_for_pidfile(explicit: "str | None") -> "str | None":
|
|
346
346
|
"""What start writes into monitor.pid.
|
|
347
347
|
|
|
348
|
-
An explicit claim wins. Otherwise
|
|
349
|
-
|
|
350
|
-
|
|
348
|
+
An explicit claim wins over the cgroup. Otherwise record a unit only
|
|
349
|
+
when its name says it supervises a monitor (swarph-monitor*.service).
|
|
350
|
+
A foreign leaf such as claude-tmux@X.service is not a monitor supervisor.
|
|
351
|
+
The read is this process's own cgroup, taken now, while the pid is ours.
|
|
351
352
|
"""
|
|
352
353
|
if explicit:
|
|
353
354
|
return explicit
|
|
354
|
-
|
|
355
|
+
leaf = _service_leaf(_read_cgroup(os.getpid()))
|
|
356
|
+
if leaf and leaf.startswith("swarph-monitor"):
|
|
357
|
+
return leaf
|
|
358
|
+
return None
|
|
355
359
|
|
|
356
360
|
|
|
357
361
|
def _derive_systemd_unit(pid: "int | None") -> "str | None":
|
|
@@ -641,12 +645,16 @@ def _run_pinned(state, pidfile: Path, self_name: str, sinks: list, poll_s: int,
|
|
|
641
645
|
supervisor: "str | None" = None) -> int:
|
|
642
646
|
mesh.write_pidfile(pidfile, self_name=self_name, sinks=sinks, poll_s=poll_s,
|
|
643
647
|
supervisor=supervisor)
|
|
648
|
+
clean = False
|
|
644
649
|
try:
|
|
645
|
-
|
|
650
|
+
result = mesh._monitor_loop(state)
|
|
651
|
+
clean = True
|
|
652
|
+
return result
|
|
646
653
|
finally:
|
|
647
|
-
#
|
|
648
|
-
#
|
|
649
|
-
|
|
654
|
+
# A clean return can drop the file. SIGINT and any other unwind must
|
|
655
|
+
# not: the recorded supervisor is what the watchdog names after the
|
|
656
|
+
# process is gone. `monitor stop` still deletes the file itself.
|
|
657
|
+
if clean and mesh.pidfile_status(pidfile)[0] == "live_ours":
|
|
650
658
|
pidfile.unlink(missing_ok=True)
|
|
651
659
|
|
|
652
660
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: swarph-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.70.0
|
|
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
|
|
@@ -256,6 +256,7 @@ tests/test_957_auth_http.py
|
|
|
256
256
|
tests/test_959_venv_interpreter.py
|
|
257
257
|
tests/test_960_channel.py
|
|
258
258
|
tests/test_960_ensure_monitor_heartbeat.py
|
|
259
|
+
tests/test_961_cursor_grok_footer.py
|
|
259
260
|
tests/test_963_plugin_manifest.py
|
|
260
261
|
tests/test_964_channel_cell.py
|
|
261
262
|
tests/test_965_mesh_wait.py
|
|
@@ -76,23 +76,98 @@ def test_pidfile_records_supervisor_when_given(tmp_path):
|
|
|
76
76
|
assert rec["supervisor"] == "task:Swarph cursor-win Monitor"
|
|
77
77
|
|
|
78
78
|
|
|
79
|
-
def
|
|
80
|
-
|
|
79
|
+
def _start_once(monkeypatch, tmp_path, cgroup, extra=()):
|
|
80
|
+
_env(monkeypatch)
|
|
81
|
+
monkeypatch.setattr(monitor, "_verify_self_is_registered",
|
|
82
|
+
lambda *a, **k: (True, "registered"))
|
|
83
|
+
monkeypatch.setattr(mesh, "_monitor_iteration", lambda state: None)
|
|
84
|
+
monkeypatch.setattr(monitor, "_read_cgroup", lambda pid: cgroup)
|
|
85
|
+
assert _run(["start", "--once", *extra], tmp_path) == 0
|
|
86
|
+
return json.loads((tmp_path / "monitor.pid").read_text(encoding="utf-8"))
|
|
81
87
|
|
|
82
|
-
|
|
83
|
-
|
|
88
|
+
|
|
89
|
+
def test_a_swarph_monitor_cgroup_is_recorded_and_a_foreign_one_is_not(monkeypatch, tmp_path):
|
|
90
|
+
"""Only a swarph-monitor*.service leaf is a monitor supervisor.
|
|
91
|
+
|
|
92
|
+
claude-tmux@X.service and any other foreign unit are not recorded.
|
|
93
|
+
An explicit --supervisor still wins over the cgroup leaf; if it stops
|
|
94
|
+
winning, the recorded value is the leaf and this assertion fails.
|
|
84
95
|
"""
|
|
96
|
+
for unit in (
|
|
97
|
+
"swarph-monitor.service",
|
|
98
|
+
"swarph-monitor-gemini-researcher.service",
|
|
99
|
+
"swarph-monitor@fixture-cell.service",
|
|
100
|
+
):
|
|
101
|
+
rec = _start_once(monkeypatch, tmp_path, f"0::/system.slice/{unit}\n")
|
|
102
|
+
assert rec["supervisor"] == unit
|
|
103
|
+
(tmp_path / "monitor.pid").unlink()
|
|
104
|
+
rec = _start_once(
|
|
105
|
+
monkeypatch, tmp_path, "0::/user.slice/claude-tmux@123.service\n")
|
|
106
|
+
assert "supervisor" not in rec
|
|
107
|
+
(tmp_path / "monitor.pid").unlink()
|
|
108
|
+
rec = _start_once(
|
|
109
|
+
monkeypatch, tmp_path, "0::/system.slice/swarph-monitor.service\n",
|
|
110
|
+
extra=("--supervisor", "task:Named"))
|
|
111
|
+
assert rec["supervisor"] == "task:Named"
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def test_interrupt_inside_the_real_loop_keeps_the_supervisor(monkeypatch, tmp_path):
|
|
115
|
+
"""Fails at 8b4d11f on an assertion.
|
|
116
|
+
|
|
117
|
+
KeyboardInterrupt raised inside the real iteration used to be swallowed,
|
|
118
|
+
the loop returned 0, and _run_pinned deleted monitor.pid. The loop itself
|
|
119
|
+
is not patched.
|
|
120
|
+
"""
|
|
121
|
+
def raise_inside(_state):
|
|
122
|
+
raise KeyboardInterrupt
|
|
123
|
+
|
|
124
|
+
monkeypatch.setattr(mesh, "_monitor_iteration", raise_inside)
|
|
125
|
+
state = mesh.MonitorState(
|
|
126
|
+
self_name="fixture-cell", state_dir=tmp_path,
|
|
127
|
+
gateway="http://gateway.test", token="tok", sinks=[])
|
|
128
|
+
pidfile = tmp_path / "monitor.pid"
|
|
129
|
+
with pytest.raises(KeyboardInterrupt):
|
|
130
|
+
monitor._run_pinned(state, pidfile, "fixture-cell", [], 30,
|
|
131
|
+
supervisor="swarph-monitor.service")
|
|
132
|
+
assert pidfile.exists()
|
|
133
|
+
rec = json.loads(pidfile.read_text(encoding="utf-8"))
|
|
134
|
+
assert rec["supervisor"] == "swarph-monitor.service"
|
|
135
|
+
|
|
136
|
+
rec["pid"] = 4000000
|
|
137
|
+
pidfile.write_text(json.dumps(rec), encoding="utf-8")
|
|
85
138
|
_env(monkeypatch)
|
|
139
|
+
monkeypatch.setattr(mesh, "_monitor_iteration", lambda _state: None)
|
|
86
140
|
monkeypatch.setattr(monitor, "_verify_self_is_registered",
|
|
87
141
|
lambda *a, **k: (True, "registered"))
|
|
88
|
-
monkeypatch.setattr(mesh, "_monitor_iteration", lambda state: None)
|
|
89
|
-
monkeypatch.setattr(
|
|
90
|
-
monitor, "_read_cgroup",
|
|
91
|
-
lambda pid: "0::/user.slice/scratch-761.service\n")
|
|
92
142
|
assert _run(["start", "--once"], tmp_path) == 0
|
|
93
|
-
|
|
94
|
-
assert
|
|
95
|
-
|
|
143
|
+
reclaimed = json.loads(pidfile.read_text(encoding="utf-8"))
|
|
144
|
+
assert reclaimed["pid"] == os.getpid()
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def test_sigint_and_an_exception_leave_the_recorded_supervisor(monkeypatch, tmp_path):
|
|
148
|
+
"""Unwinding used to unlink monitor.pid, so the watchdog then said unsupervised."""
|
|
149
|
+
pidfile = tmp_path / "monitor.pid"
|
|
150
|
+
|
|
151
|
+
def raise_kbd(_state):
|
|
152
|
+
raise KeyboardInterrupt
|
|
153
|
+
|
|
154
|
+
monkeypatch.setattr(mesh, "_monitor_loop", raise_kbd)
|
|
155
|
+
with pytest.raises(KeyboardInterrupt):
|
|
156
|
+
monitor._run_pinned(object(), pidfile, "fixture-cell", [], 30,
|
|
157
|
+
supervisor="swarph-monitor.service")
|
|
158
|
+
assert json.loads(pidfile.read_text(encoding="utf-8"))["supervisor"] == (
|
|
159
|
+
"swarph-monitor.service")
|
|
160
|
+
|
|
161
|
+
def raise_exc(_state):
|
|
162
|
+
raise RuntimeError("boom")
|
|
163
|
+
|
|
164
|
+
monkeypatch.setattr(mesh, "_monitor_loop", raise_exc)
|
|
165
|
+
pidfile.unlink()
|
|
166
|
+
with pytest.raises(RuntimeError):
|
|
167
|
+
monitor._run_pinned(object(), pidfile, "fixture-cell", [], 30,
|
|
168
|
+
supervisor="swarph-monitor-gemini-researcher.service")
|
|
169
|
+
assert json.loads(pidfile.read_text(encoding="utf-8"))["supervisor"] == (
|
|
170
|
+
"swarph-monitor-gemini-researcher.service")
|
|
96
171
|
|
|
97
172
|
|
|
98
173
|
def test_start_does_not_record_the_session_manager(monkeypatch, tmp_path):
|
|
@@ -429,3 +429,51 @@ def test_main_sends_one_writer_down_dm_and_does_not_start(tmp_path):
|
|
|
429
429
|
assert "token-file" not in text
|
|
430
430
|
assert "monitor start" not in text
|
|
431
431
|
assert "gridiron" not in text
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
@pytest.mark.skipif(
|
|
435
|
+
sys.platform == "win32",
|
|
436
|
+
reason="Windows cannot execute the POSIX swarph stub as argv0",
|
|
437
|
+
)
|
|
438
|
+
def test_main_names_the_recorded_supervisor_not_the_ownership_fallback(tmp_path):
|
|
439
|
+
"""Fails if main() passes None instead of recorded_supervisor.
|
|
440
|
+
|
|
441
|
+
Ownership would then name the template unit. The pidfile names the
|
|
442
|
+
non-template unit, and that is the string the send must carry.
|
|
443
|
+
"""
|
|
444
|
+
from swarph_cli.commands.mesh import write_pidfile
|
|
445
|
+
|
|
446
|
+
root = tmp_path / "state"
|
|
447
|
+
_cell(root, "fixture-cell", fresh=True)
|
|
448
|
+
write_pidfile(
|
|
449
|
+
root / "fixture-cell" / "mesh-sidecar" / "monitor.pid",
|
|
450
|
+
self_name="fixture-cell", sinks=[], poll_s=30,
|
|
451
|
+
supervisor="swarph-monitor.service")
|
|
452
|
+
stub = tmp_path / "swarph"
|
|
453
|
+
log = tmp_path / "calls.log"
|
|
454
|
+
stub.write_text(textwrap.dedent("""\
|
|
455
|
+
#!/bin/sh
|
|
456
|
+
echo "$@" >> "$STUB_LOG"
|
|
457
|
+
case "$1 $2" in
|
|
458
|
+
"monitor status") exit 2 ;;
|
|
459
|
+
*) exit 0 ;;
|
|
460
|
+
esac
|
|
461
|
+
"""), encoding="utf-8")
|
|
462
|
+
stub.chmod(0o755)
|
|
463
|
+
env = {
|
|
464
|
+
**os.environ,
|
|
465
|
+
"PYTHONPATH": os.path.abspath("src"),
|
|
466
|
+
"SWARPH_STATE_ROOT": str(root),
|
|
467
|
+
"WAKE_WATCHDOG_STATE": str(tmp_path / "wake.json"),
|
|
468
|
+
"SWARPH_BIN": str(stub),
|
|
469
|
+
"STUB_LOG": str(log),
|
|
470
|
+
}
|
|
471
|
+
env.pop("SWARPH_SELF", None)
|
|
472
|
+
proc = subprocess.run(
|
|
473
|
+
[sys.executable, "-m", "swarph_cli.scripts.wake_watchdog",
|
|
474
|
+
"--as", "lab-ovh", "--escalate", "drop-on-meta-edge"],
|
|
475
|
+
env=env, capture_output=True, text=True)
|
|
476
|
+
assert proc.returncode == 0
|
|
477
|
+
text = log.read_text(encoding="utf-8")
|
|
478
|
+
assert "supervised by swarph-monitor.service" in text
|
|
479
|
+
assert "swarph-monitor@fixture-cell.service" not in text
|