swarph-cli 0.69.4__tar.gz → 0.69.6__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {swarph_cli-0.69.4/src/swarph_cli.egg-info → swarph_cli-0.69.6}/PKG-INFO +1 -1
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/pyproject.toml +1 -1
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/__init__.py +1 -1
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/daemon.py +4 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/mcp_server.py +4 -3
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/mesh.py +168 -20
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/scripts/ensure_monitor.sh +61 -2
- swarph_cli-0.69.6/src/swarph_cli/stall_alert.py +144 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6/src/swarph_cli.egg-info}/PKG-INFO +1 -1
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli.egg-info/SOURCES.txt +3 -0
- swarph_cli-0.69.6/tests/test_715_grok_wake.py +88 -0
- swarph_cli-0.69.6/tests/test_723_grok_0251.py +104 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_735_dm_mcp_tools.py +2 -2
- swarph_cli-0.69.6/tests/test_960_ensure_monitor_heartbeat.py +166 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_mesh_inbox_mark_read.py +60 -7
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_monitor_state_split.py +5 -0
- swarph_cli-0.69.6/tests/test_stall_alert.py +140 -0
- swarph_cli-0.69.4/src/swarph_cli/stall_alert.py +0 -52
- swarph_cli-0.69.4/tests/test_stall_alert.py +0 -52
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/LICENSE +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/README.md +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/setup.cfg +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/__main__.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/bench/__init__.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/bench/backends.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/bench/claude_cli.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/bench/pack.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/bench/prices.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/bench/providers.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/bench/quality.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/bench/refresh_prices.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/bench/rules.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/bench/runner.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/bench/validate.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/caller.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/capture/__init__.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/capture/harden.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/capture/lineage.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/capture/liveness.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/capture/manifest.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/capture/paths.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/capture/verify.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/cell.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/chain_token.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/channel.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/__init__.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/_content.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/_display.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/_gateway_client.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/add.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/bench.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/board.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/board_merge_check.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/brain.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/brain_ask.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/cell.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/cell_probe.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/channel.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/chat.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/codegraph.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/codegraph_hook.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/codex_hook_output.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/codex_waker.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/compress.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/dreaming.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/event.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/gateway.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/gh_route.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/group.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/guide.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/highlight.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/hook_interpreter.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/hook_output.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/hooks.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/import_session.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/init.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/install_hook.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/install_multiplexer.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/install_wake_hook.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/lane.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/memory.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/memory_sync.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/monitor.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/okf_links.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/onboard.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/protocol_handler.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/ratify.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/schedule.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/security.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/service.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/spawn.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/timeline.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/version.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/wake_hook_output.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/commands/watchdog.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/compress/__init__.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/compress/levers.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/compress/marker.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/compress/verify.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/console_safe.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/delivery_queue.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/dm_frame.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/dreaming/__init__.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/dreaming/candidates.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/dreaming/clone.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/dreaming/enrich.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/dreaming/index_check.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/dreaming/notify.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/dreaming/organize.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/dreaming/probes.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/dreaming/report.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/dreaming/run.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/dreaming/slm.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/dreaming/transcripts.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/dreaming/verify.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/gateway/__init__.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/gateway/feature_registry.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/gateway/lanes_control.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/gateway/schema.sql +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/gateway/server.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/gateway/services_control.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/gateway_default.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/gh_identity.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/guide/GUIDE.md +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/identity.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/main.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/multiplexer.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/orphan_daemons.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/pane_probe.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/parsers/__init__.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/parsers/claude.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/peer_executor.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/peer_executor_windows.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/peer_reconciliation.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/peer_reply_outbox.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/peer_service_host.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/peer_service_payload.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/peer_service_reply.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/peer_staging.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/query_filters.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/scripts/__init__.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/scripts/wake_watchdog.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/service/__init__.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/service/app.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/service/providers.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/session_bridge.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/source_text.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/systemd/codex-waker.default +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/timeline_paths.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli/tokens.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli.egg-info/entry_points.txt +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli.egg-info/requires.txt +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/src/swarph_cli.egg-info/top_level.txt +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_124_register_merge.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_126_pending_from_complete.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_132_ensure_monitor_state_dir.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_138_memory_navigate.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_139_tool_probe.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_183_card_anchor.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_184_notify_honesty.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_201_win32_bash_refusal.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_243_onboard_per_peer_token.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_307_task4_ask_reply.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_319_refusal_exit_codes.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_323_mistral_backend.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_332_report_organize_findings.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_356_query_filters.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_397_gh_identity_router.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_397_router_preflight.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_402_one_identity_resolver.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_467b_onboard_operator_credential.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_472_register_caller_binding.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_523_guide.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_524_conftest_hermeticity.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_532_accept_flag.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_535_version_capability.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_546_default_gateway.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_547_verbs_from_registry.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_548_gateway_remainder.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_552_codegraph_gateway.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_555_relay_row_shape.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_556_relay_freshness.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_559_edges_truncated.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_564_bootstrap_first_peer.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_579_register_bootstrap_message.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_591_cli_step_graph.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_644_monitor_supervision.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_648_install_unit.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_654_reexec.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_656_owned_slm.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_684_dreaming_verb.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_689_pkg_version_order.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_716_timeline_one_path.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_724_ps1_ascii_or_bom.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_725_stdio_reconfigure.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_729_dm_once.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_729_wake_watchdog.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_734_dreaming_enrich_degrade.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_740_edit_project.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_762_cards_edit_priority.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_764_enrich_collapse.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_801_confirm_flow_cli.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_802_stage_history.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_825_codegraph_hook_noise.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_825_install_posttooluse_only.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_825_remaining_cap_shred_bind.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_829_counterfactual_column.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_830_hook_binding_version.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_84_cli_labels.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_866_doctrine_guide.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_872_identity_refuses_not_warns.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_874_source_text.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_937_dreaming_notify.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_938_organize_line_limit.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_941_bench_backends.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_941_harness_342.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_945_same_line_pairing.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_955_prompt_audit.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_957_auth_http.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_959_venv_interpreter.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_960_channel.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_963_plugin_manifest.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_964_channel_cell.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_965_mesh_wait.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_975_channel_hop.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_add_security_gate.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_artifact_add.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_artifact_hash.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_artifact_lib.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_artifact_mcp.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_artifact_skill.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_artifact_tool.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_artifact_uri.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_bench_backends.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_bench_claude_cli.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_bench_command.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_bench_pack.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_bench_prices.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_bench_quality.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_bench_runner.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_bench_validate.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_board_card_thread.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_board_command.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_board_merge_check.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_board_obligation_close.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_brain_ask_command.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_brain_ask_env_diagnosis.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_brain_ask_identity_explicitness.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_brain_command.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_caller_meta_guard.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_capture_lineage.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_capture_liveness.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_capture_manifest.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_capture_paths.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_capture_security.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_cell_command_dispatch.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_cell_harden.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_cell_loader.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_cell_selfcheck.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_cell_verify.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_chain_token.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_channel_command.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_channel_poll_never_breaks_dms.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_channel_post.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_channel_wake_policy_filter.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_chat_command.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_claude_parser.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_claude_tmux_template.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_codegraph_caller_scope.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_codegraph_command.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_codegraph_hook.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_codegraph_mcp.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_codex_waker.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_compress_caller_convention.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_compress_command.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_compress_levers.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_compress_marker.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_compress_verify.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_console_encoding_e2e.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_content_composition.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_cursor_membrane.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_daemon_command.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_daemon_console_encoding.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_daemon_delivery.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_delivery_queue.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_display_sanitize.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_ensure_monitor_no_peer_default.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_event_emit.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_feature_to_uri.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_gateway_brain.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_gateway_client.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_gateway_command.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_gateway_message_idempotency.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_gh_route_doctor_mesh_name.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_group_command.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_guide_commands_parse.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_guide_selfcheck.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_heartbeat_check.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_highlight_command.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_hook_output.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_hooks_activity_marker.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_hooks_add.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_hooks_bundle.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_hooks_init.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_hooks_legacy_generation_removal.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_hooks_lifecycle.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_hooks_merge.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_hooks_verify.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_hooks_win32_bash_wrapper.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_hooks_win32_command_path.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_import_command.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_inbox_identity_provenance.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_init_command.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_init_provider_menu.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_install_codex_hooks.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_install_hook.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_install_multiplexer_command.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_install_postcompact_hook.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_install_wake_hook.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_lane_command.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_main.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_main_unknown_verb_guard.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_mcp_missing_hint.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_mcp_server.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_memory_command.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_memory_emit_snippet.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_memory_gateway.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_memory_sync.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_memory_sync_membrane.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_mesh_command.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_mesh_inbox_sanitize.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_mesh_originate_thread_623.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_mesh_send_unknown_peer.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_mesh_sidecar.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_meta_edge_identity.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_monitor_channel_poll.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_monitor_cli.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_monitor_daemon_collision.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_monitor_self_name_verification.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_multiplexer.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_muse_membrane.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_muse_own_cli.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_obligation_close_is_surfaced.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_okf_links.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_onboard_command.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_opencode_membrane.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_opencode_plugin.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_orphan_daemons.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_packaged_artifacts_exist.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_packaged_skills.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_peer_executor.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_peer_executor_windows.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_peer_reply_drain_command.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_peer_reply_drain_supervisor.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_peer_reply_outbox.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_peer_reply_transport.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_peer_service_e2e.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_peer_service_host.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_peer_service_payload.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_peer_service_reply.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_postcompact_recall.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_process_liveness.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_protocol_handler.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_providers_isolation.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_ratify_command.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_readme_claims_no_gateway_default.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_repl_caller_convention.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_schedule_command.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_schedule_create_contract.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_security.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_service_app.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_service_command.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_service_providers.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_session_bridge_inject.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_session_bridge_probe.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_session_bridge_resolve.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_session_path_role_gate.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_shipped_units_match_the_cli.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_smoke_chat.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_smoke_one_shot.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_smoke_phase_5_5.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_spawn_base_hoist.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_spawn_command.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_spawn_identity_stamp.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_spawn_live_pin.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_spawn_mitosis_lineage.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_spawn_tmux_session.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_spawn_windows_relaunch.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_subprocess_decode_encoding.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_timeline_command.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_tmux_wake_runstate_defer.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_tmux_wake_session_reanchor.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_tmux_wake_stale.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_tmux_wake_submit_verify.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_token_file_one_parser.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_token_precedence_explicit_first.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_unified_token_resolver.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_version_command.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_version_consistency.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_vibe_membrane.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_watchdog.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_watchdog_dm_wake.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_watchdog_dm_wake_wiring.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_watchdog_emit_health.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_watchdog_install_flags.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_watchdog_model_rung.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/tests/test_watchdog_stale_peers.py +0 -0
- {swarph_cli-0.69.4 → swarph_cli-0.69.6}/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.69.
|
|
3
|
+
Version: 0.69.6
|
|
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.69.
|
|
7
|
+
version = "0.69.6"
|
|
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" }
|
|
@@ -381,8 +381,12 @@ def _legacy_attempt_delivery_removed(state: DaemonState) -> None:
|
|
|
381
381
|
entries = state.queue.pending()
|
|
382
382
|
block = _render_delivery_block(entries)
|
|
383
383
|
if session_bridge.inject(pane, block):
|
|
384
|
+
stalled = state.queue.deferred_ticks
|
|
384
385
|
state.queue.remove({e["id"] for e in entries})
|
|
385
386
|
state.queue.reset_deferred()
|
|
387
|
+
if stalled:
|
|
388
|
+
stall_alert.clear_stall_alert(
|
|
389
|
+
state.gateway, state.token, state.self_name)
|
|
386
390
|
# inject failure → leave queued, retry next tick (no counter bump —
|
|
387
391
|
# the cell was idle; a send failure is transient, not a stall).
|
|
388
392
|
except Exception as exc: # noqa: BLE001 — bridge must never crash the loop
|
|
@@ -344,14 +344,15 @@ def _dm_reply(message_id: int, text: str, kind: str = "answer",
|
|
|
344
344
|
st, d = _mesh._post_json(f"{gw}/messages", body, tok)
|
|
345
345
|
if not (200 <= st < 300):
|
|
346
346
|
return {"error": f"gateway {st}: {d.get('detail', d) if isinstance(d, dict) else d}"}
|
|
347
|
-
|
|
347
|
+
failed = _mesh._mark_read(gw, tok, [{"id": int(message_id)}])
|
|
348
|
+
acked = int(message_id) not in failed
|
|
348
349
|
return {
|
|
349
350
|
"id": d.get("id"),
|
|
350
351
|
"to_node": to,
|
|
351
352
|
"kind": kind,
|
|
352
353
|
"thread_id": d.get("thread_id"),
|
|
353
|
-
"acked": int(message_id) if
|
|
354
|
-
"ack_status":
|
|
354
|
+
"acked": int(message_id) if acked else None,
|
|
355
|
+
"ack_status": 200 if acked else 0,
|
|
355
356
|
"closed_obligations": d.get("closed_obligations") or [],
|
|
356
357
|
}
|
|
357
358
|
except Exception as exc:
|
|
@@ -944,38 +944,58 @@ def _run_reply(args: argparse.Namespace) -> int:
|
|
|
944
944
|
return 0
|
|
945
945
|
|
|
946
946
|
|
|
947
|
-
|
|
947
|
+
_BULK_READ_MAX = 500
|
|
948
|
+
|
|
949
|
+
|
|
950
|
+
def _mark_read(gateway: str, token: str, messages: list) -> list:
|
|
948
951
|
"""Mark every unread DM we just surfaced as read. Best-effort.
|
|
949
952
|
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
because the CLI never called the gateway's POST /messages/{id}/read.
|
|
953
|
+
One POST /messages/read per 500 ids. The gateway's single worker spends a
|
|
954
|
+
synchronous sqlite write on every request, so a per-id loop is how a drain
|
|
955
|
+
turns into HTTP 503. A 404 means that route is not on this gateway yet, and
|
|
956
|
+
only then does this fall back to one POST per id.
|
|
955
957
|
|
|
956
958
|
A mark-read failure must NEVER fail the listing — the caller has already seen
|
|
957
959
|
the messages, and losing the read receipt is strictly less bad than losing them.
|
|
958
960
|
"""
|
|
959
961
|
base = gateway.rstrip("/")
|
|
960
|
-
|
|
962
|
+
ids = []
|
|
961
963
|
for dm in messages:
|
|
962
|
-
if dm
|
|
963
|
-
|
|
964
|
-
|
|
964
|
+
if isinstance(dm, dict):
|
|
965
|
+
if dm.get("read_at"):
|
|
966
|
+
continue
|
|
967
|
+
msg_id = dm.get("id")
|
|
968
|
+
else:
|
|
969
|
+
msg_id = dm
|
|
965
970
|
if msg_id is None:
|
|
966
971
|
continue
|
|
972
|
+
ids.append(msg_id)
|
|
973
|
+
failed = []
|
|
974
|
+
for start in range(0, len(ids), _BULK_READ_MAX):
|
|
975
|
+
chunk = ids[start:start + _BULK_READ_MAX]
|
|
967
976
|
try:
|
|
968
|
-
status, _ = _post_json(f"{base}/messages/
|
|
969
|
-
if status < 200 or status >= 300:
|
|
970
|
-
failed.append(msg_id)
|
|
977
|
+
status, _ = _post_json(f"{base}/messages/read", {"ids": chunk}, token)
|
|
971
978
|
except Exception: # network, DNS, timeout — never break the listing
|
|
972
|
-
failed.
|
|
979
|
+
failed.extend(chunk)
|
|
980
|
+
continue
|
|
981
|
+
if status == 404:
|
|
982
|
+
for msg_id in chunk:
|
|
983
|
+
try:
|
|
984
|
+
one, _ = _post_json(f"{base}/messages/{msg_id}/read", {}, token)
|
|
985
|
+
if one < 200 or one >= 300:
|
|
986
|
+
failed.append(msg_id)
|
|
987
|
+
except Exception:
|
|
988
|
+
failed.append(msg_id)
|
|
989
|
+
continue
|
|
990
|
+
if status < 200 or status >= 300:
|
|
991
|
+
failed.extend(chunk)
|
|
973
992
|
if failed:
|
|
974
993
|
print(
|
|
975
994
|
f"swarph mesh inbox: mark-read failed for id(s) {failed} "
|
|
976
995
|
"(messages shown above; they will be re-listed as unread)",
|
|
977
996
|
file=sys.stderr,
|
|
978
997
|
)
|
|
998
|
+
return failed
|
|
979
999
|
|
|
980
1000
|
|
|
981
1001
|
def _run_inbox(args: argparse.Namespace) -> int:
|
|
@@ -1461,6 +1481,18 @@ class TmuxSink(Sink):
|
|
|
1461
1481
|
|
|
1462
1482
|
def deliver(self, state: "MonitorState", dms: list, up_to_id: int) -> Optional[bool]:
|
|
1463
1483
|
led = state.ledger(self.name)
|
|
1484
|
+
# #723: a rate-limit or usage-limit screen is not deliverable. An
|
|
1485
|
+
# empty composer beside "Retry failed: rate limit" used to inject
|
|
1486
|
+
# again, and the 1.0.44 upgrade modal (no Grok footer) used to
|
|
1487
|
+
# return False and log DELIVERY FAILED. Defer with a named reason
|
|
1488
|
+
# and send nothing.
|
|
1489
|
+
pane = _capture_pane_lines(self.target)
|
|
1490
|
+
if pane:
|
|
1491
|
+
reason = _grok_block_reason(pane)
|
|
1492
|
+
if reason:
|
|
1493
|
+
print(f"[monitor] {self.name}: {reason} — deferring, zero keys",
|
|
1494
|
+
flush=True)
|
|
1495
|
+
return None
|
|
1464
1496
|
# #620: a wake's job is done the moment a TURN is observed running —
|
|
1465
1497
|
# the cell is awake, whoever woke it (our injected wake fired, or
|
|
1466
1498
|
# the human typed). Both older clearing paths miss the zombie
|
|
@@ -1477,7 +1509,7 @@ class TmuxSink(Sink):
|
|
|
1477
1509
|
# Opencode keeps the finished ▣ header on screen, so a deferral
|
|
1478
1510
|
# during the turn must not clear the flag: the next idle poll
|
|
1479
1511
|
# still owes a wake. Cursor's #620 clear stays.
|
|
1480
|
-
if _opencode_in_progress(self.target):
|
|
1512
|
+
if _opencode_in_progress(self.target) or _grok_in_progress(self.target):
|
|
1481
1513
|
return None
|
|
1482
1514
|
led["wake_outstanding"] = False
|
|
1483
1515
|
if led.get("wake_outstanding"):
|
|
@@ -1504,7 +1536,8 @@ class TmuxSink(Sink):
|
|
|
1504
1536
|
# A finished opencode turn leaves a ▣ header with a
|
|
1505
1537
|
# trailing duration and a clear box. The standing-wake
|
|
1506
1538
|
# claim would swallow every later DM. Inject again.
|
|
1507
|
-
if _opencode_turn_finished_target(self.target)
|
|
1539
|
+
if (_opencode_turn_finished_target(self.target)
|
|
1540
|
+
or _grok_turn_finished_target(self.target)):
|
|
1508
1541
|
ok = _tmux_wake(self.target)
|
|
1509
1542
|
if ok:
|
|
1510
1543
|
_stamp_wake(led)
|
|
@@ -2000,6 +2033,9 @@ _WAKE_PROMPT = "check mesh"
|
|
|
2000
2033
|
# not shown. Longer than _WAKE_PROMPT, so wake-text detection strips this
|
|
2001
2034
|
# one first.
|
|
2002
2035
|
_OPENCODE_WAKE_PROMPT = "check mesh: swarph_dm_unread"
|
|
2036
|
+
# Grok's TUI has the same shape (#715, grok 1.0.41): no push channel, so
|
|
2037
|
+
# the wake is one line naming the pull tool and no DM body.
|
|
2038
|
+
_GROK_WAKE_PROMPT = "check mesh: swarph_dm_unread"
|
|
2003
2039
|
# A finished turn keeps its ▣ header and adds a duration.
|
|
2004
2040
|
# Short turns read `· 3.3s` (drop-on-meta-edge, 1.18.33, card #961 post
|
|
2005
2041
|
# 54557). A longer idle turn reads `· 3m 4s` (opencode pane, 2026-09-29
|
|
@@ -2110,7 +2146,7 @@ def _only_wake_text(content: str) -> bool:
|
|
|
2110
2146
|
human's line.
|
|
2111
2147
|
"""
|
|
2112
2148
|
rest = content
|
|
2113
|
-
for prompt in (_OPENCODE_WAKE_PROMPT, _WAKE_PROMPT):
|
|
2149
|
+
for prompt in (_GROK_WAKE_PROMPT, _OPENCODE_WAKE_PROMPT, _WAKE_PROMPT):
|
|
2114
2150
|
rest = rest.replace(prompt, "")
|
|
2115
2151
|
return rest.strip() == ""
|
|
2116
2152
|
|
|
@@ -2245,6 +2281,110 @@ def _opencode_composer_state(lines: list[str]) -> Optional[str]:
|
|
|
2245
2281
|
return "busy"
|
|
2246
2282
|
|
|
2247
2283
|
|
|
2284
|
+
def _is_grok_pane(lines: list[str]) -> bool:
|
|
2285
|
+
"""Grok Build composer, measured on sacrificial 1.0.41 and 0.2.51 TUIs.
|
|
2286
|
+
|
|
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
|
+
|
|
2294
|
+
|
|
2295
|
+
def _grok_input(lines: list[str]) -> Optional[str]:
|
|
2296
|
+
"""Text in the live ``│ ❯`` box, '' when empty, None if no box."""
|
|
2297
|
+
row = None
|
|
2298
|
+
for ln in lines:
|
|
2299
|
+
if "❯" in ln and "│" in ln:
|
|
2300
|
+
row = ln
|
|
2301
|
+
if row is None:
|
|
2302
|
+
return None
|
|
2303
|
+
after = row.split("❯", 1)[1]
|
|
2304
|
+
if "│" in after:
|
|
2305
|
+
after = after.split("│", 1)[0]
|
|
2306
|
+
return after.strip()
|
|
2307
|
+
|
|
2308
|
+
|
|
2309
|
+
def _grok_block_reason(lines: list[str]) -> Optional[str]:
|
|
2310
|
+
"""A rate or usage limit is not an idle composer and not a dead pane.
|
|
2311
|
+
|
|
2312
|
+
0.2.51 draws ``Retry failed: You've hit the rate limit for your plan``
|
|
2313
|
+
under an empty ``│ ❯`` box, with no spinner. 1.0.44 draws a ``┃`` modal
|
|
2314
|
+
``You hit your free usage limit`` and drops the Grok footer entirely.
|
|
2315
|
+
Either screen used to look clear (inject again) or unreadable
|
|
2316
|
+
(DELIVERY FAILED). Return a stable reason token, or None.
|
|
2317
|
+
"""
|
|
2318
|
+
blob = "\n".join(lines).lower()
|
|
2319
|
+
if "you hit your free usage limit" in blob:
|
|
2320
|
+
return "grok-usage-limit"
|
|
2321
|
+
if "rate limit" in blob:
|
|
2322
|
+
return "grok-rate-limit"
|
|
2323
|
+
if "usage limit" in blob:
|
|
2324
|
+
return "grok-usage-limit"
|
|
2325
|
+
if "retry failed" in blob:
|
|
2326
|
+
return "grok-retry-failed"
|
|
2327
|
+
return None
|
|
2328
|
+
|
|
2329
|
+
|
|
2330
|
+
def _grok_running(lines: list[str]) -> bool:
|
|
2331
|
+
"""True only while the turn is in progress.
|
|
2332
|
+
|
|
2333
|
+
1.0.41/1.0.44: ``Waiting for response``. 0.2.51: ``Waiting…`` at the
|
|
2334
|
+
start of the turn, then a tool row whose footer is ``Ctrl+c:cancel``
|
|
2335
|
+
for as long as the turn lasts. ``Worked for`` / ``Thought for`` stay
|
|
2336
|
+
on screen after the turn and must not count as running.
|
|
2337
|
+
"""
|
|
2338
|
+
for ln in lines:
|
|
2339
|
+
if "Waiting for response" in ln or "Waiting…" in ln or "Waiting..." in ln:
|
|
2340
|
+
return True
|
|
2341
|
+
if "Ctrl+c:cancel" in ln:
|
|
2342
|
+
return True
|
|
2343
|
+
return False
|
|
2344
|
+
|
|
2345
|
+
|
|
2346
|
+
def _grok_turn_finished(lines: list[str]) -> bool:
|
|
2347
|
+
if _grok_running(lines) or _grok_block_reason(lines):
|
|
2348
|
+
return False
|
|
2349
|
+
return any(
|
|
2350
|
+
"Worked for" in ln or "Thought for" in ln or "Turn cancelled" in ln
|
|
2351
|
+
for ln in lines
|
|
2352
|
+
)
|
|
2353
|
+
|
|
2354
|
+
|
|
2355
|
+
def _grok_in_progress(target: str) -> bool:
|
|
2356
|
+
lines = _capture_pane_lines(target)
|
|
2357
|
+
if not lines or not _is_grok_pane(lines):
|
|
2358
|
+
return False
|
|
2359
|
+
return _grok_running(lines)
|
|
2360
|
+
|
|
2361
|
+
|
|
2362
|
+
def _grok_turn_finished_target(target: str) -> bool:
|
|
2363
|
+
lines = _capture_pane_lines(target)
|
|
2364
|
+
if not lines or not _is_grok_pane(lines):
|
|
2365
|
+
return False
|
|
2366
|
+
return _grok_turn_finished(lines)
|
|
2367
|
+
|
|
2368
|
+
|
|
2369
|
+
def _grok_composer_state(lines: list[str]) -> Optional[str]:
|
|
2370
|
+
text = _grok_input(lines)
|
|
2371
|
+
if text is None:
|
|
2372
|
+
return None
|
|
2373
|
+
if not text:
|
|
2374
|
+
return "clear"
|
|
2375
|
+
if _only_wake_text(text):
|
|
2376
|
+
return "wake"
|
|
2377
|
+
return "busy"
|
|
2378
|
+
|
|
2379
|
+
|
|
2380
|
+
def _wake_prompt_for(lines: Optional[list[str]]) -> str:
|
|
2381
|
+
if lines and _is_grok_pane(lines):
|
|
2382
|
+
return _GROK_WAKE_PROMPT
|
|
2383
|
+
if lines and _is_opencode_pane(lines):
|
|
2384
|
+
return _OPENCODE_WAKE_PROMPT
|
|
2385
|
+
return _WAKE_PROMPT
|
|
2386
|
+
|
|
2387
|
+
|
|
2248
2388
|
def _agent_running(target: str) -> Optional[bool]:
|
|
2249
2389
|
"""True while a keystroke would miss the idle composer.
|
|
2250
2390
|
|
|
@@ -2263,6 +2403,8 @@ def _agent_running(target: str) -> Optional[bool]:
|
|
|
2263
2403
|
return None
|
|
2264
2404
|
if _is_opencode_pane(lines):
|
|
2265
2405
|
return _opencode_running(lines)
|
|
2406
|
+
if _is_grok_pane(lines):
|
|
2407
|
+
return _grok_running(lines)
|
|
2266
2408
|
composer = _composer_line(lines)
|
|
2267
2409
|
if composer is None:
|
|
2268
2410
|
return None
|
|
@@ -2291,6 +2433,8 @@ def _composer_state(target: str) -> Optional[str]:
|
|
|
2291
2433
|
return None
|
|
2292
2434
|
if _is_opencode_pane(lines):
|
|
2293
2435
|
return _opencode_composer_state(lines)
|
|
2436
|
+
if _is_grok_pane(lines):
|
|
2437
|
+
return _grok_composer_state(lines)
|
|
2294
2438
|
composer = _composer_line(lines)
|
|
2295
2439
|
if composer is None:
|
|
2296
2440
|
return None
|
|
@@ -2379,8 +2523,7 @@ def _tmux_wake(target: str) -> Optional[bool]:
|
|
|
2379
2523
|
"""
|
|
2380
2524
|
try:
|
|
2381
2525
|
seen = _capture_pane_lines(target)
|
|
2382
|
-
prompt = (
|
|
2383
|
-
if seen and _is_opencode_pane(seen) else _WAKE_PROMPT)
|
|
2526
|
+
prompt = _wake_prompt_for(seen)
|
|
2384
2527
|
subprocess.run(
|
|
2385
2528
|
["tmux", "send-keys", "-t", target, "-l", prompt],
|
|
2386
2529
|
check=True,
|
|
@@ -2673,7 +2816,8 @@ def _monitor_deliver(state: MonitorState) -> None:
|
|
|
2673
2816
|
f"no failure counted",
|
|
2674
2817
|
flush=True)
|
|
2675
2818
|
continue
|
|
2676
|
-
|
|
2819
|
+
was_stalled = bool(led.get("deferred_ticks"))
|
|
2820
|
+
if was_stalled:
|
|
2677
2821
|
led["deferred_ticks"] = 0
|
|
2678
2822
|
if outcome:
|
|
2679
2823
|
led["last_delivered_id"] = observed
|
|
@@ -2682,6 +2826,10 @@ def _monitor_deliver(state: MonitorState) -> None:
|
|
|
2682
2826
|
state.deliveries[sink.name] = state.deliveries.get(sink.name, 0) + 1
|
|
2683
2827
|
print(f"{state.log_prefix} delivered to {sink.name} up to id {observed}",
|
|
2684
2828
|
flush=True)
|
|
2829
|
+
if was_stalled:
|
|
2830
|
+
from swarph_cli import stall_alert
|
|
2831
|
+
stall_alert.clear_stall_alert(
|
|
2832
|
+
state.gateway, state.token, state.self_name)
|
|
2685
2833
|
else:
|
|
2686
2834
|
# A dead sink is VISIBLE instead of silently freezing anything.
|
|
2687
2835
|
led["consecutive_failures"] = int(led["consecutive_failures"]) + 1
|
|
@@ -64,12 +64,71 @@ STATE_DIR="${1:-${SWARPH_STATE_DIR:-}}"
|
|
|
64
64
|
SD=()
|
|
65
65
|
[ -n "$STATE_DIR" ] && SD=(--state-dir "$STATE_DIR")
|
|
66
66
|
|
|
67
|
+
# >>> #960: a fresh channel heartbeat is already a reader. <<<
|
|
68
|
+
# swarph channel-serve writes channel_heartbeat.json beside the inbox every
|
|
69
|
+
# 60s and names no path on its command line, so `monitor status` cannot see
|
|
70
|
+
# it. Starting a pull monitor beside a heartbeat younger than 180s is the
|
|
71
|
+
# duplicate this script exists to prevent. Absent or older: start, as before.
|
|
72
|
+
# A missing python3 is treated as absent (start), never as a failed caller.
|
|
73
|
+
_heartbeat_fresh() {
|
|
74
|
+
local f="$1"
|
|
75
|
+
[ -n "$f" ] && [ -f "$f" ] || return 1
|
|
76
|
+
command -v python3 >/dev/null 2>&1 || return 1
|
|
77
|
+
python3 -c 'import json,os,sys,time
|
|
78
|
+
try:
|
|
79
|
+
data=json.load(open(sys.argv[1],encoding="utf-8"))
|
|
80
|
+
ts=float(data.get("ts") or 0)
|
|
81
|
+
except Exception:
|
|
82
|
+
raise SystemExit(1)
|
|
83
|
+
age=time.time()-ts
|
|
84
|
+
# A clock a few seconds ahead is still a live writer. An hour ahead is not.
|
|
85
|
+
if age < -30 or age >= 180:
|
|
86
|
+
raise SystemExit(1)
|
|
87
|
+
pid=data.get("pid")
|
|
88
|
+
if pid is None:
|
|
89
|
+
raise SystemExit(0)
|
|
90
|
+
try:
|
|
91
|
+
pid=int(pid)
|
|
92
|
+
except (TypeError, ValueError):
|
|
93
|
+
raise SystemExit(1)
|
|
94
|
+
# pid 0 is this process group and pid -1 is every process we can signal.
|
|
95
|
+
# Neither is the channel server. os.kill would report both as alive.
|
|
96
|
+
if pid <= 0:
|
|
97
|
+
raise SystemExit(1)
|
|
98
|
+
try:
|
|
99
|
+
os.kill(pid, 0)
|
|
100
|
+
except PermissionError:
|
|
101
|
+
pass
|
|
102
|
+
except OSError:
|
|
103
|
+
raise SystemExit(1)
|
|
104
|
+
try:
|
|
105
|
+
raw=open("/proc/%s/cmdline" % pid, "rb").read()
|
|
106
|
+
except OSError:
|
|
107
|
+
# No command line to read (macOS, or a pid we may not inspect).
|
|
108
|
+
# A live pid still counts, which is the check this host can make.
|
|
109
|
+
raise SystemExit(0)
|
|
110
|
+
text=raw.replace(b"\x00", b" ").decode("utf-8", "replace")
|
|
111
|
+
if "channel-serve" in text or "swarph_cli.channel" in text:
|
|
112
|
+
raise SystemExit(0)
|
|
113
|
+
raise SystemExit(1)' "$f"
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
HB_ROOT="${STATE_DIR:-$HOME/swarph_state/$SELF}"
|
|
117
|
+
HB_FILE=""
|
|
118
|
+
if [ -f "$HB_ROOT/channel_heartbeat.json" ]; then
|
|
119
|
+
HB_FILE="$HB_ROOT/channel_heartbeat.json"
|
|
120
|
+
elif [ -f "$HB_ROOT/mesh-sidecar/channel_heartbeat.json" ]; then
|
|
121
|
+
HB_FILE="$HB_ROOT/mesh-sidecar/channel_heartbeat.json"
|
|
122
|
+
fi
|
|
123
|
+
|
|
67
124
|
"$SW" monitor status --as "$SELF" ${SD[@]+"${SD[@]}"} >/dev/null 2>&1
|
|
68
125
|
case $? in
|
|
69
126
|
0) ;; # running, nothing pending
|
|
70
127
|
1) ;; # running, DMs pending — reported below
|
|
71
|
-
*) # not running (2) or unknown
|
|
72
|
-
if
|
|
128
|
+
*) # not running (2) or unknown.
|
|
129
|
+
if _heartbeat_fresh "$HB_FILE"; then
|
|
130
|
+
echo "[monitor] channel heartbeat is under 180s old — not starting a monitor"
|
|
131
|
+
elif "$SW" monitor start --as "$SELF" --deliver pull ${SD[@]+"${SD[@]}"} >/tmp/.swarph-monitor-start.$$ 2>&1; then
|
|
73
132
|
echo "[monitor] started (--deliver pull)"
|
|
74
133
|
else
|
|
75
134
|
# LOUD, not silent: an auto-start that fails quietly rebuilds the exact
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
"""stall_alert — surface a cell whose live session is perpetually busy so its
|
|
2
|
+
undelivered DMs don't rot silently. Exponential backoff (6,12,24,48…) prevents
|
|
3
|
+
the linear-flood failure (the 145-DM incident, feedback_modal_stalls_cell_wake).
|
|
4
|
+
|
|
5
|
+
Card #989: the surface is one obligation held by lab-ovh, never a DM to
|
|
6
|
+
commander. A second stall tick while that row is open opens nothing new. A
|
|
7
|
+
drain closes it. Fail-safe: a failed POST never raises and never blocks
|
|
8
|
+
delivery."""
|
|
9
|
+
from __future__ import annotations
|
|
10
|
+
|
|
11
|
+
import json
|
|
12
|
+
import sys
|
|
13
|
+
import urllib.error
|
|
14
|
+
import urllib.parse
|
|
15
|
+
import urllib.request
|
|
16
|
+
from swarph_cli.console_safe import print_safe
|
|
17
|
+
|
|
18
|
+
_STALL_FIRST = 6 # first alert after this many consecutive deferred ticks
|
|
19
|
+
_CARD = 989
|
|
20
|
+
_HOLDER = "lab-ovh"
|
|
21
|
+
# lab-ovh's own stall cannot escalate to lab-ovh. Another orchestrator holds it.
|
|
22
|
+
_OTHER = "drop-on-meta-edge"
|
|
23
|
+
_TIMEOUT_HOURS = 24
|
|
24
|
+
_PREFIX = f"STALL card #{_CARD}"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def is_alert_tick(deferred_ticks: int) -> bool:
|
|
28
|
+
"""True exactly at 6, 12, 24, 48, 96 … — first at _STALL_FIRST, doubling."""
|
|
29
|
+
if deferred_ticks < _STALL_FIRST or deferred_ticks % _STALL_FIRST != 0:
|
|
30
|
+
return False
|
|
31
|
+
k = deferred_ticks // _STALL_FIRST
|
|
32
|
+
return (k & (k - 1)) == 0 # k is a power of two
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _accept(self_name: str, count: int, pending_n: int) -> str:
|
|
36
|
+
return (
|
|
37
|
+
f"{_PREFIX} cell={self_name} ticks={count} pending={pending_n} | "
|
|
38
|
+
"PASS = the cell drained and this row is closed | "
|
|
39
|
+
"FAIL = the cell is still unable to deliver"
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def _what(self_name: str, count: int, pending_n: int) -> str:
|
|
44
|
+
return (
|
|
45
|
+
f"{_PREFIX}\n"
|
|
46
|
+
f"cell={self_name} ticks={count} pending_dms={pending_n}"
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _open_url(req, timeout):
|
|
51
|
+
host = urllib.parse.urlparse(req.full_url).hostname
|
|
52
|
+
if host in {"127.0.0.1", "localhost", "::1"}:
|
|
53
|
+
return urllib.request.build_opener(
|
|
54
|
+
urllib.request.ProxyHandler({})).open(req, timeout=timeout)
|
|
55
|
+
return urllib.request.urlopen(req, timeout=timeout)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def _request(method: str, url: str, token: str, payload: dict | None = None) -> dict:
|
|
59
|
+
data = None if payload is None else json.dumps(payload).encode()
|
|
60
|
+
req = urllib.request.Request(
|
|
61
|
+
url, data=data, method=method,
|
|
62
|
+
headers={"Authorization": f"Bearer {token}",
|
|
63
|
+
"Content-Type": "application/json"})
|
|
64
|
+
with _open_url(req, timeout=10) as resp:
|
|
65
|
+
raw = resp.read().decode()
|
|
66
|
+
if not (200 <= resp.status < 300):
|
|
67
|
+
raise urllib.error.URLError(f"HTTP {resp.status}")
|
|
68
|
+
return json.loads(raw) if raw else {}
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _holder_for(self_name: str) -> str:
|
|
72
|
+
return _OTHER if self_name == _HOLDER else _HOLDER
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _cell_row(row: dict, self_name: str) -> bool:
|
|
76
|
+
needle = f"cell={self_name} "
|
|
77
|
+
return needle in (row.get("accept") or "") or needle in (row.get("what") or "")
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _live(gateway: str, token: str, self_name: str) -> list[dict]:
|
|
81
|
+
rows: list[dict] = []
|
|
82
|
+
for status in ("open", "fallback_fired"):
|
|
83
|
+
data = _request(
|
|
84
|
+
"GET",
|
|
85
|
+
f"{gateway}/board/obligations?card_id={_CARD}&status={status}",
|
|
86
|
+
token)
|
|
87
|
+
rows.extend(data.get("obligations") or [])
|
|
88
|
+
seen = set()
|
|
89
|
+
live = []
|
|
90
|
+
for row in rows:
|
|
91
|
+
if row.get("id") in seen:
|
|
92
|
+
continue
|
|
93
|
+
seen.add(row.get("id"))
|
|
94
|
+
if _cell_row(row, self_name):
|
|
95
|
+
live.append(row)
|
|
96
|
+
return live
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def send_stall_alert(gateway: str, token: str, self_name: str,
|
|
100
|
+
count: int, pending_n: int) -> bool:
|
|
101
|
+
"""Open one obligation on card #989 held by lab-ovh. True when that row
|
|
102
|
+
is open (newly or already). False on any error. Never raises, never
|
|
103
|
+
DMs commander, and a second call while the row is open opens nothing."""
|
|
104
|
+
try:
|
|
105
|
+
gateway = gateway.rstrip("/")
|
|
106
|
+
if _live(gateway, token, self_name):
|
|
107
|
+
return True
|
|
108
|
+
holder = _holder_for(self_name)
|
|
109
|
+
_request(
|
|
110
|
+
"POST", f"{gateway}/board/cards/{_CARD}/ask", token,
|
|
111
|
+
{"holder": holder,
|
|
112
|
+
"what": _what(self_name, count, pending_n),
|
|
113
|
+
"created_by": self_name,
|
|
114
|
+
"kind": "action",
|
|
115
|
+
"fallback_mode": "escalate",
|
|
116
|
+
"fallback_target": holder,
|
|
117
|
+
"timeout_hours": _TIMEOUT_HOURS,
|
|
118
|
+
"accept": _accept(self_name, count, pending_n)})
|
|
119
|
+
return True
|
|
120
|
+
except (urllib.error.URLError, OSError, ValueError, json.JSONDecodeError) as exc:
|
|
121
|
+
print_safe(f"[swarph-daemon] stall-alert POST failed: {exc}",
|
|
122
|
+
file=sys.stderr, flush=True)
|
|
123
|
+
return False
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def clear_stall_alert(gateway: str, token: str, self_name: str) -> bool:
|
|
127
|
+
"""Close the open stall row after a drain. True when none remain open.
|
|
128
|
+
False on any error. Never raises."""
|
|
129
|
+
evidence = (
|
|
130
|
+
f"{_PREFIX} cell={self_name} drained. "
|
|
131
|
+
"The deferred delivery completed, so this row is closed."
|
|
132
|
+
)
|
|
133
|
+
try:
|
|
134
|
+
gateway = gateway.rstrip("/")
|
|
135
|
+
live = _live(gateway, token, self_name)
|
|
136
|
+
for row in live:
|
|
137
|
+
_request(
|
|
138
|
+
"POST", f"{gateway}/board/obligations/{row['id']}/close", token,
|
|
139
|
+
{"outcome": "pass", "evidence": evidence})
|
|
140
|
+
return True
|
|
141
|
+
except (urllib.error.URLError, OSError, ValueError, json.JSONDecodeError) as exc:
|
|
142
|
+
print_safe(f"[swarph-daemon] stall-alert close failed: {exc}",
|
|
143
|
+
file=sys.stderr, flush=True)
|
|
144
|
+
return False
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: swarph-cli
|
|
3
|
-
Version: 0.69.
|
|
3
|
+
Version: 0.69.6
|
|
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
|
|
@@ -219,7 +219,9 @@ tests/test_654_reexec.py
|
|
|
219
219
|
tests/test_656_owned_slm.py
|
|
220
220
|
tests/test_684_dreaming_verb.py
|
|
221
221
|
tests/test_689_pkg_version_order.py
|
|
222
|
+
tests/test_715_grok_wake.py
|
|
222
223
|
tests/test_716_timeline_one_path.py
|
|
224
|
+
tests/test_723_grok_0251.py
|
|
223
225
|
tests/test_724_ps1_ascii_or_bom.py
|
|
224
226
|
tests/test_725_stdio_reconfigure.py
|
|
225
227
|
tests/test_729_dm_once.py
|
|
@@ -250,6 +252,7 @@ tests/test_955_prompt_audit.py
|
|
|
250
252
|
tests/test_957_auth_http.py
|
|
251
253
|
tests/test_959_venv_interpreter.py
|
|
252
254
|
tests/test_960_channel.py
|
|
255
|
+
tests/test_960_ensure_monitor_heartbeat.py
|
|
253
256
|
tests/test_963_plugin_manifest.py
|
|
254
257
|
tests/test_964_channel_cell.py
|
|
255
258
|
tests/test_965_mesh_wait.py
|