swarph-cli 0.72.4__tar.gz → 0.72.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.72.4/src/swarph_cli.egg-info → swarph_cli-0.72.6}/PKG-INFO +1 -1
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/pyproject.toml +1 -1
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/__init__.py +1 -1
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/mesh.py +32 -0
- swarph_cli-0.72.6/src/swarph_cli/commands/rowclear_hook.py +42 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/guide/GUIDE.md +1 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/main.py +8 -2
- swarph_cli-0.72.6/src/swarph_cli/rowclear.py +239 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/scripts/dm_notify_filter.py +33 -10
- {swarph_cli-0.72.4 → swarph_cli-0.72.6/src/swarph_cli.egg-info}/PKG-INFO +1 -1
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli.egg-info/SOURCES.txt +4 -0
- swarph_cli-0.72.6/tests/test_1050_surrogate_wake.py +115 -0
- swarph_cli-0.72.6/tests/test_432_rowclear.py +278 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/LICENSE +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/README.md +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/setup.cfg +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/__main__.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/bench/__init__.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/bench/backends.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/bench/claude_cli.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/bench/codex_cli.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/bench/pack.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/bench/prices.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/bench/providers.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/bench/quality.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/bench/refresh_prices.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/bench/rules.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/bench/runner.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/bench/signal_score.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/bench/validate.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/caller.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/capture/__init__.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/capture/harden.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/capture/lineage.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/capture/liveness.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/capture/manifest.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/capture/paths.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/capture/verify.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/cell.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/chain_token.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/channel.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/__init__.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/_content.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/_display.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/_gateway_client.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/add.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/bench.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/board.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/board_merge_check.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/brain.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/brain_ask.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/cell.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/cell_probe.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/channel.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/chat.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/codegraph.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/codegraph_hook.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/codex_hook_output.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/codex_waker.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/compress.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/daemon.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/dreaming.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/event.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/gateway.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/gh_route.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/group.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/guide.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/highlight.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/hook_interpreter.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/hook_output.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/hooks.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/import_session.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/init.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/install_hook.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/install_multiplexer.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/install_wake_hook.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/lane.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/mcp_server.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/memory.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/memory_sync.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/monitor.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/okf_links.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/onboard.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/protocol_handler.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/ratify.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/schedule.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/security.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/service.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/spawn.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/timeline.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/version.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/wake_hook_output.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/commands/watchdog.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/compress/__init__.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/compress/levers.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/compress/marker.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/compress/verify.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/console_safe.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/delivery_queue.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/dm_frame.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/dreaming/__init__.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/dreaming/candidates.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/dreaming/clone.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/dreaming/enrich.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/dreaming/index_check.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/dreaming/notify.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/dreaming/organize.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/dreaming/probes.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/dreaming/report.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/dreaming/run.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/dreaming/slm.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/dreaming/transcripts.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/dreaming/verify.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/gateway/__init__.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/gateway/feature_registry.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/gateway/lanes_control.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/gateway/schema.sql +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/gateway/server.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/gateway/services_control.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/gateway_default.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/gh_identity.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/identity.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/multiplexer.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/operator/__init__.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/operator/__main__.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/operator/api.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/operator/cli.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/operator/config.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/operator/fmt.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/orphan_daemons.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/pane_probe.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/parsers/__init__.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/parsers/claude.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/peer_executor.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/peer_executor_windows.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/peer_reconciliation.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/peer_reply_outbox.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/peer_service_host.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/peer_service_payload.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/peer_service_reply.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/peer_staging.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/query_filters.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/scripts/__init__.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/scripts/wake_watchdog.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/service/__init__.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/service/app.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/service/providers.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/session_bridge.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/source_text.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/stall_alert.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/systemd/codex-waker.default +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/timeline_paths.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli/tokens.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli.egg-info/entry_points.txt +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli.egg-info/requires.txt +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/src/swarph_cli.egg-info/top_level.txt +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_1006_board_publisher.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_1006_session_board.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_1010_strand_only.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_1013_spawn_channel.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_1017_muse_tui.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_124_register_merge.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_126_pending_from_complete.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_132_ensure_monitor_state_dir.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_138_memory_navigate.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_139_tool_probe.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_183_card_anchor.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_184_notify_honesty.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_201_win32_bash_refusal.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_243_onboard_per_peer_token.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_307_task4_ask_reply.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_319_refusal_exit_codes.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_323_mistral_backend.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_332_report_organize_findings.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_356_query_filters.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_397_gh_identity_router.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_397_router_preflight.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_402_one_identity_resolver.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_467b_onboard_operator_credential.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_472_register_caller_binding.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_484_codex_queue_sink.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_523_guide.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_524_conftest_hermeticity.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_532_accept_flag.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_535_version_capability.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_538_signal_bench.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_546_default_gateway.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_547_verbs_from_registry.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_548_gateway_remainder.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_552_codegraph_gateway.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_555_relay_row_shape.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_556_relay_freshness.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_559_edges_truncated.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_564_bootstrap_first_peer.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_579_register_bootstrap_message.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_591_cli_step_graph.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_644_monitor_supervision.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_648_install_unit.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_654_reexec.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_656_owned_slm.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_684_dreaming_verb.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_689_pkg_version_order.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_715_grok_wake.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_716_timeline_one_path.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_723_grok_0251.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_724_ps1_ascii_or_bom.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_725_stdio_reconfigure.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_726_grok_limit_screen.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_729_arm_check_delivery_gap.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_729_dm_once.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_729_wake_watchdog.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_734_dreaming_enrich_degrade.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_735_dm_mcp_tools.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_739_wake_writer.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_740_edit_project.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_762_cards_edit_priority.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_764_enrich_collapse.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_801_confirm_flow_cli.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_802_stage_history.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_825_codegraph_hook_noise.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_825_install_posttooluse_only.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_825_remaining_cap_shred_bind.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_829_counterfactual_column.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_830_hook_binding_version.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_84_cli_labels.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_866_doctrine_guide.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_872_identity_refuses_not_warns.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_874_source_text.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_937_dreaming_notify.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_938_organize_line_limit.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_941_bench_backends.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_941_harness_342.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_945_same_line_pairing.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_955_prompt_audit.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_957_auth_http.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_959_venv_interpreter.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_960_channel.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_960_ensure_monitor_heartbeat.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_961_cursor_grok_footer.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_961_loud_stop.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_963_plugin_manifest.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_964_channel_cell.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_965_mesh_wait.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_975_channel_hop.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_add_security_gate.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_artifact_add.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_artifact_hash.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_artifact_lib.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_artifact_mcp.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_artifact_skill.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_artifact_tool.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_artifact_uri.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_bench_backends.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_bench_claude_cli.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_bench_codex_cli.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_bench_command.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_bench_pack.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_bench_prices.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_bench_quality.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_bench_runner.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_bench_validate.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_board_card_thread.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_board_command.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_board_merge_check.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_board_obligation_close.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_brain_ask_command.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_brain_ask_env_diagnosis.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_brain_ask_identity_explicitness.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_brain_command.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_caller_meta_guard.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_capture_lineage.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_capture_liveness.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_capture_manifest.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_capture_paths.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_capture_security.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_cell_command_dispatch.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_cell_harden.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_cell_loader.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_cell_selfcheck.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_cell_verify.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_chain_token.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_channel_command.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_channel_poll_never_breaks_dms.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_channel_post.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_channel_wake_policy_filter.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_chat_command.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_claude_parser.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_claude_tmux_template.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_codegraph_caller_scope.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_codegraph_command.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_codegraph_hook.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_codegraph_mcp.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_codex_waker.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_compress_caller_convention.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_compress_command.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_compress_levers.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_compress_marker.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_compress_verify.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_console_encoding_e2e.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_content_composition.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_cursor_membrane.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_daemon_command.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_daemon_console_encoding.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_daemon_delivery.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_delivery_queue.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_display_sanitize.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_ensure_monitor_no_peer_default.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_event_emit.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_feature_to_uri.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_gateway_brain.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_gateway_client.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_gateway_command.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_gateway_message_idempotency.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_gh_route_doctor_mesh_name.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_group_command.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_guide_commands_parse.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_guide_selfcheck.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_heartbeat_check.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_highlight_command.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_hook_output.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_hooks_activity_marker.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_hooks_add.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_hooks_bundle.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_hooks_init.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_hooks_legacy_generation_removal.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_hooks_lifecycle.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_hooks_merge.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_hooks_verify.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_hooks_win32_bash_wrapper.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_hooks_win32_command_path.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_import_command.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_inbox_identity_provenance.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_init_command.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_init_provider_menu.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_install_codex_hooks.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_install_hook.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_install_multiplexer_command.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_install_postcompact_hook.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_install_wake_hook.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_lane_command.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_main.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_main_unknown_verb_guard.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_mcp_missing_hint.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_mcp_server.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_memory_command.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_memory_emit_snippet.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_memory_gateway.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_memory_sync.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_memory_sync_membrane.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_mesh_command.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_mesh_inbox_mark_read.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_mesh_inbox_sanitize.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_mesh_originate_thread_623.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_mesh_send_unknown_peer.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_mesh_sidecar.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_mesh_sidecar_muse_sink.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_meta_edge_identity.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_monitor_channel_poll.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_monitor_cli.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_monitor_daemon_collision.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_monitor_self_name_verification.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_monitor_state_split.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_multiplexer.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_muse_membrane.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_muse_own_cli.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_obligation_close_is_surfaced.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_okf_links.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_onboard_command.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_opencode_membrane.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_opencode_plugin.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_operator_me.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_orphan_daemons.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_packaged_artifacts_exist.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_packaged_skills.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_peer_executor.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_peer_executor_windows.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_peer_reply_drain_command.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_peer_reply_drain_supervisor.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_peer_reply_outbox.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_peer_reply_transport.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_peer_service_e2e.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_peer_service_host.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_peer_service_payload.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_peer_service_reply.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_postcompact_recall.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_process_liveness.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_protocol_handler.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_providers_isolation.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_ratify_command.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_readme_claims_no_gateway_default.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_repl_caller_convention.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_schedule_command.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_schedule_create_contract.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_security.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_service_app.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_service_command.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_service_providers.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_session_bridge_inject.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_session_bridge_probe.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_session_bridge_resolve.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_session_path_role_gate.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_shipped_units_match_the_cli.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_smoke_chat.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_smoke_one_shot.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_smoke_phase_5_5.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_spawn_base_hoist.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_spawn_command.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_spawn_identity_stamp.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_spawn_live_pin.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_spawn_mitosis_lineage.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_spawn_tmux_session.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_spawn_windows_relaunch.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_stall_alert.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_subprocess_decode_encoding.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_timeline_command.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_tmux_wake_runstate_defer.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_tmux_wake_session_reanchor.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_tmux_wake_stale.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_tmux_wake_submit_verify.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_token_file_one_parser.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_token_precedence_explicit_first.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_unified_token_resolver.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_version_command.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_version_consistency.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_vibe_membrane.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_watchdog.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_watchdog_dm_wake.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_watchdog_dm_wake_wiring.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_watchdog_emit_health.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_watchdog_install_flags.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_watchdog_model_rung.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.6}/tests/test_watchdog_stale_peers.py +0 -0
- {swarph_cli-0.72.4 → swarph_cli-0.72.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.72.
|
|
3
|
+
Version: 0.72.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.72.
|
|
7
|
+
version = "0.72.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" }
|
|
@@ -3392,6 +3392,35 @@ def _monitor_deliver(state: MonitorState) -> None:
|
|
|
3392
3392
|
_write_ledgers_atomic(state.ledgers_path, state.ledgers)
|
|
3393
3393
|
|
|
3394
3394
|
|
|
3395
|
+
def _type_slash_clear(target: str) -> bool:
|
|
3396
|
+
"""Type ``/clear`` and submit it. One command, not a wake."""
|
|
3397
|
+
for argv in (
|
|
3398
|
+
["tmux", "send-keys", "-t", target, "-l", "/clear"],
|
|
3399
|
+
["tmux", "send-keys", "-t", target, "Enter"],
|
|
3400
|
+
):
|
|
3401
|
+
try:
|
|
3402
|
+
r = subprocess.run(
|
|
3403
|
+
argv, capture_output=True, timeout=5,
|
|
3404
|
+
text=True, encoding="utf-8", errors="replace",
|
|
3405
|
+
)
|
|
3406
|
+
except (OSError, subprocess.TimeoutExpired):
|
|
3407
|
+
return False
|
|
3408
|
+
if r.returncode != 0:
|
|
3409
|
+
return False
|
|
3410
|
+
return True
|
|
3411
|
+
|
|
3412
|
+
|
|
3413
|
+
def _maybe_rowclear(state: MonitorState) -> str:
|
|
3414
|
+
"""Card #432. Off and non-opted-in cells return before any pane read."""
|
|
3415
|
+
from swarph_cli.rowclear import maybe_rowclear
|
|
3416
|
+
return maybe_rowclear(
|
|
3417
|
+
state,
|
|
3418
|
+
agent_running=_agent_running,
|
|
3419
|
+
http_get=_http_get_json,
|
|
3420
|
+
type_clear=_type_slash_clear,
|
|
3421
|
+
)
|
|
3422
|
+
|
|
3423
|
+
|
|
3395
3424
|
def _monitor_iteration(state: MonitorState, *, poll_channels: bool = True) -> None:
|
|
3396
3425
|
state.iterations += 1
|
|
3397
3426
|
last_id = int(state.observed.get("last_msg_id", 0))
|
|
@@ -3456,6 +3485,9 @@ def _monitor_iteration(state: MonitorState, *, poll_channels: bool = True) -> No
|
|
|
3456
3485
|
# A delivery deferred by the idle guard (or failed against a dead sink) must
|
|
3457
3486
|
# still land even though no NEW mail arrived, so this runs on every poll.
|
|
3458
3487
|
_monitor_deliver(state)
|
|
3488
|
+
# Card #432: after delivery, so an owed DM (cursor still behind) blocks
|
|
3489
|
+
# the clear. off / not-opted-in return before any pane or board read.
|
|
3490
|
+
_maybe_rowclear(state)
|
|
3459
3491
|
# The observation cursor is written BEFORE delivery, while last_wake_at is
|
|
3460
3492
|
# still unknown. A wake that landed this poll has to be written again or
|
|
3461
3493
|
# cursor.json keeps the 0.0 default after a real keystroke (#405).
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""``swarph rowclear-hook`` — SessionStart hook for card #432.
|
|
2
|
+
|
|
3
|
+
Claude Code runs this on SessionStart. When the payload's ``source`` is
|
|
4
|
+
``clear``, the new ``session_id`` is written into the role's
|
|
5
|
+
``<role>.session-id`` pin (the role is ``SWARPH_SELF``). Spawn then
|
|
6
|
+
resumes that id instead of the pre-clear session.
|
|
7
|
+
|
|
8
|
+
Exit 0 on every path. A hook that fails closed refuses the session.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import json
|
|
14
|
+
import sys
|
|
15
|
+
|
|
16
|
+
from swarph_cli import identity
|
|
17
|
+
from swarph_cli.rowclear import apply_sessionstart_clear
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _read_stdin() -> dict:
|
|
21
|
+
try:
|
|
22
|
+
if not sys.stdin.isatty():
|
|
23
|
+
raw = sys.stdin.read()
|
|
24
|
+
if raw.strip():
|
|
25
|
+
return json.loads(raw.lstrip("\ufeff"))
|
|
26
|
+
except Exception:
|
|
27
|
+
return {}
|
|
28
|
+
return {}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def run_rowclear_hook(argv: list[str] | None = None) -> int:
|
|
32
|
+
del argv
|
|
33
|
+
role = identity.declared(env=identity.SELF_ONLY)[0] or ""
|
|
34
|
+
if not role:
|
|
35
|
+
print("rowclear-hook: SWARPH_SELF unset — pin not rewritten",
|
|
36
|
+
file=sys.stderr)
|
|
37
|
+
return 0
|
|
38
|
+
try:
|
|
39
|
+
apply_sessionstart_clear(_read_stdin(), role)
|
|
40
|
+
except Exception as exc:
|
|
41
|
+
print(f"rowclear-hook: {exc}", file=sys.stderr)
|
|
42
|
+
return 0
|
|
@@ -543,6 +543,7 @@ answers `--help`; hook-plumbing verbs are the callbacks hooks invoke, not comman
|
|
|
543
543
|
- `swarph protocol-handler` -- register `swarph://` as an OS URL-scheme handler.
|
|
544
544
|
- `swarph ratify` -- witness flip that admits an onboarded peer; takes the peer name and `--reason`.
|
|
545
545
|
- `swarph rights` -- RBAC rights over the gateway (companion of `swarph group`).
|
|
546
|
+
- `swarph rowclear-hook` -- SessionStart hook (card #432): when `source` is `clear`, write the new session id into the role's `<role>.session-id` pin so the next spawn resumes the cleared session.
|
|
546
547
|
- `swarph scan` -- statically scan an artifact for dangerous patterns before publish.
|
|
547
548
|
- `swarph schedule` -- the gateway's scheduled events: create, list, get, enable, disable, delete, fire-now.
|
|
548
549
|
- `swarph service` -- stand up a $0 subscription-LLM HTTP lane.
|
|
@@ -92,6 +92,7 @@ _VERB_HANDLERS: dict[str, str] = {
|
|
|
92
92
|
"install-wake-hook": "swarph_cli.commands.install_wake_hook.run_install_wake_hook",
|
|
93
93
|
"wake-hook-output": "swarph_cli.commands.wake_hook_output.run_wake_hook_output",
|
|
94
94
|
"postcompact-hook-output": "swarph_cli.commands.postcompact_hook_output.run_postcompact_hook_output",
|
|
95
|
+
"rowclear-hook": "swarph_cli.commands.rowclear_hook.run_rowclear_hook",
|
|
95
96
|
"install-postcompact-hook": "swarph_cli.commands.install_postcompact_hook.run_install_postcompact_hook",
|
|
96
97
|
"install-opencode-plugin": "swarph_cli.commands.install_opencode_plugin.run_install_opencode_plugin",
|
|
97
98
|
"memory-emit-hook": "swarph_cli.commands.memory_emit_hook.run_memory_emit_hook",
|
|
@@ -147,8 +148,13 @@ def _verbs_block(width: int = 78) -> str:
|
|
|
147
148
|
import textwrap
|
|
148
149
|
verbs = registered_verbs()
|
|
149
150
|
head = f"Verbs ({len(verbs)}) -- each has --help and a guide entry (swarph guide <verb>):"
|
|
150
|
-
|
|
151
|
-
|
|
151
|
+
# break_on_hyphens would split a verb at a wrap (wake-hook-output
|
|
152
|
+
# became "wake-hook-" / "output" once the list grew past the width).
|
|
153
|
+
return head + "\n" + textwrap.fill(
|
|
154
|
+
", ".join(verbs), width=width,
|
|
155
|
+
initial_indent=" ", subsequent_indent=" ",
|
|
156
|
+
break_on_hyphens=False, break_long_words=False,
|
|
157
|
+
) + "\n"
|
|
152
158
|
|
|
153
159
|
|
|
154
160
|
def _build_parser() -> argparse.ArgumentParser:
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"""Card #432: type ``/clear`` at a row boundary, and follow the new session.
|
|
2
|
+
|
|
3
|
+
The monitor types ``/clear`` into an opted-in role cell's tmux pane only
|
|
4
|
+
when the cell holds no live row, the pane reads idle, and no DM is owed.
|
|
5
|
+
``SWARPH_ROWCLEAR`` is ``off`` (default), ``shadow`` (log a would-clear,
|
|
6
|
+
type nothing), or ``live``. v1 opt-in is drop-on-meta-edge only: a cell
|
|
7
|
+
that owns a project keeps its context (design_1007 / optin_owners).
|
|
8
|
+
|
|
9
|
+
A ``/clear`` mints a new Claude session id. The SessionStart hook with
|
|
10
|
+
``source=clear`` writes that id into the role's ``<role>.session-id``
|
|
11
|
+
pin, so the next spawn resumes the cleared session instead of the long
|
|
12
|
+
one the pin still named.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import json
|
|
18
|
+
import os
|
|
19
|
+
import sys
|
|
20
|
+
from datetime import datetime, timezone
|
|
21
|
+
from typing import Any, Callable, Optional
|
|
22
|
+
|
|
23
|
+
from swarph_cli.cell import (
|
|
24
|
+
CellError,
|
|
25
|
+
_read_session_sidecar,
|
|
26
|
+
_write_session_sidecar,
|
|
27
|
+
session_state_path,
|
|
28
|
+
validate_uuid_str,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
# v1. Further cells join only by their own or the commander's opt-in.
|
|
32
|
+
OPT_IN = frozenset({"drop-on-meta-edge"})
|
|
33
|
+
|
|
34
|
+
# Gateway statuses are open, closed, and fallback_fired. ``open`` is a
|
|
35
|
+
# live row (taken or offered). ``closed`` and ``fallback_fired`` are
|
|
36
|
+
# finished and do not block: drop-on-meta-edge holds a fallback_fired
|
|
37
|
+
# row that would otherwise make the only opted-in cell never clear.
|
|
38
|
+
# Any other status fails closed — a status this code does not know must
|
|
39
|
+
# not look like an empty cell.
|
|
40
|
+
_LIVE = "open"
|
|
41
|
+
_FINISHED = frozenset({"closed", "fallback_fired"})
|
|
42
|
+
|
|
43
|
+
_MODES = frozenset({"off", "shadow", "live"})
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def mode_from_env() -> str:
|
|
47
|
+
raw = os.environ.get("SWARPH_ROWCLEAR", "off").strip().lower()
|
|
48
|
+
return raw if raw in _MODES else "off"
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _latch_path(state) -> "os.PathLike[str]":
|
|
52
|
+
return state.state_dir / "rowclear.json"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def _log_path(state) -> "os.PathLike[str]":
|
|
56
|
+
return state.state_dir / "rowclear.log"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _read_latch(state) -> Optional[str]:
|
|
60
|
+
path = _latch_path(state)
|
|
61
|
+
try:
|
|
62
|
+
data = json.loads(path.read_text(encoding="utf-8"))
|
|
63
|
+
except (OSError, ValueError):
|
|
64
|
+
return None
|
|
65
|
+
mode = data.get("latched_mode") if isinstance(data, dict) else None
|
|
66
|
+
return mode if isinstance(mode, str) else None
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _write_latch(state, mode: Optional[str]) -> None:
|
|
70
|
+
path = _latch_path(state)
|
|
71
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
72
|
+
path.write_text(
|
|
73
|
+
json.dumps({"latched_mode": mode}) + "\n", encoding="utf-8")
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _tmux_target(state) -> Optional[str]:
|
|
77
|
+
for sink in state.sinks:
|
|
78
|
+
if type(sink).__name__ == "TmuxSink":
|
|
79
|
+
target = getattr(sink, "target", None)
|
|
80
|
+
if target:
|
|
81
|
+
return target
|
|
82
|
+
return None
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _dm_owed(state) -> bool:
|
|
86
|
+
observed = int(state.observed.get("last_msg_id", 0))
|
|
87
|
+
for sink in state.sinks:
|
|
88
|
+
if not getattr(sink, "keeps_ledger", False):
|
|
89
|
+
continue
|
|
90
|
+
led = state.ledger(sink.name)
|
|
91
|
+
if int(led.get("last_delivered_id", 0)) < observed:
|
|
92
|
+
return True
|
|
93
|
+
if led.get("wake_outstanding"):
|
|
94
|
+
return True
|
|
95
|
+
return False
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _live_row_count(state, http_get: Callable) -> Optional[int]:
|
|
99
|
+
"""How many unfinished rows this cell holds, or None if unread.
|
|
100
|
+
|
|
101
|
+
None fails closed: an unreadable board must not look like an empty
|
|
102
|
+
one, or a live row we failed to see gets a ``/clear``.
|
|
103
|
+
"""
|
|
104
|
+
url = f"{state.gateway}/board/obligations?holder={state.self_name}"
|
|
105
|
+
try:
|
|
106
|
+
status, body = http_get(url, state.token)
|
|
107
|
+
except Exception:
|
|
108
|
+
return None
|
|
109
|
+
if status != 200 or not isinstance(body, dict):
|
|
110
|
+
return None
|
|
111
|
+
rows = body.get("obligations")
|
|
112
|
+
if not isinstance(rows, list):
|
|
113
|
+
return None
|
|
114
|
+
live = 0
|
|
115
|
+
for row in rows:
|
|
116
|
+
if not isinstance(row, dict):
|
|
117
|
+
return None
|
|
118
|
+
status = str(row.get("status") or "")
|
|
119
|
+
if status == _LIVE:
|
|
120
|
+
live += 1
|
|
121
|
+
elif status not in _FINISHED:
|
|
122
|
+
return None
|
|
123
|
+
return live
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _append_would_clear(state, *, open_rows: int, pane_state: str) -> None:
|
|
127
|
+
path = _log_path(state)
|
|
128
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
129
|
+
record = {
|
|
130
|
+
"cell": state.self_name,
|
|
131
|
+
"time": datetime.now(timezone.utc).isoformat(),
|
|
132
|
+
"open_rows": open_rows,
|
|
133
|
+
"pane_state": pane_state,
|
|
134
|
+
"action": "would-clear",
|
|
135
|
+
}
|
|
136
|
+
with path.open("a", encoding="utf-8") as fp:
|
|
137
|
+
fp.write(json.dumps(record) + "\n")
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
def maybe_rowclear(
|
|
141
|
+
state,
|
|
142
|
+
*,
|
|
143
|
+
agent_running: Callable[[str], Optional[bool]],
|
|
144
|
+
http_get: Callable,
|
|
145
|
+
type_clear: Callable[[str], bool],
|
|
146
|
+
) -> str:
|
|
147
|
+
"""One poll. Returns the decision, so a test can name why no key went.
|
|
148
|
+
|
|
149
|
+
``off`` and a cell not on the opt-in list return before any read or
|
|
150
|
+
write: they are unchanged. A live row, a busy pane, or an owed DM
|
|
151
|
+
unlatches the idle boundary and types nothing. Unknown rows or an
|
|
152
|
+
unknown pane type nothing and leave the latch alone.
|
|
153
|
+
"""
|
|
154
|
+
mode = mode_from_env()
|
|
155
|
+
if mode == "off":
|
|
156
|
+
return "off"
|
|
157
|
+
if state.self_name not in OPT_IN:
|
|
158
|
+
return "not-opted-in"
|
|
159
|
+
|
|
160
|
+
live = _live_row_count(state, http_get)
|
|
161
|
+
if live is None:
|
|
162
|
+
return "blocked-rows-unknown"
|
|
163
|
+
if live > 0:
|
|
164
|
+
_write_latch(state, None)
|
|
165
|
+
return "blocked-live-row"
|
|
166
|
+
|
|
167
|
+
target = _tmux_target(state)
|
|
168
|
+
if target is None:
|
|
169
|
+
return "blocked-no-pane"
|
|
170
|
+
pane = agent_running(target)
|
|
171
|
+
if pane is True:
|
|
172
|
+
_write_latch(state, None)
|
|
173
|
+
return "blocked-busy"
|
|
174
|
+
if pane is None:
|
|
175
|
+
return "blocked-unknown-pane"
|
|
176
|
+
if _dm_owed(state):
|
|
177
|
+
_write_latch(state, None)
|
|
178
|
+
return "blocked-owed-dm"
|
|
179
|
+
|
|
180
|
+
if _read_latch(state) == mode:
|
|
181
|
+
return "latched"
|
|
182
|
+
|
|
183
|
+
if mode == "shadow":
|
|
184
|
+
_append_would_clear(state, open_rows=live, pane_state="idle")
|
|
185
|
+
_write_latch(state, mode)
|
|
186
|
+
print(
|
|
187
|
+
f"[rowclear] {state.self_name}: would-clear "
|
|
188
|
+
f"(open_rows={live}, pane=idle) — shadow, zero keys",
|
|
189
|
+
flush=True,
|
|
190
|
+
)
|
|
191
|
+
return "shadow"
|
|
192
|
+
|
|
193
|
+
if not type_clear(target):
|
|
194
|
+
print(
|
|
195
|
+
f"[rowclear] {state.self_name}: /clear send failed — "
|
|
196
|
+
"not latched, retried next poll",
|
|
197
|
+
flush=True,
|
|
198
|
+
)
|
|
199
|
+
return "type-failed"
|
|
200
|
+
_write_latch(state, mode)
|
|
201
|
+
print(
|
|
202
|
+
f"[rowclear] {state.self_name}: typed /clear "
|
|
203
|
+
f"(open_rows={live}, pane=idle, no DM owed)",
|
|
204
|
+
flush=True,
|
|
205
|
+
)
|
|
206
|
+
return "live"
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def apply_sessionstart_clear(payload: dict[str, Any], role: str) -> bool:
|
|
210
|
+
"""Write ``session_id`` into the role pin when SessionStart source is clear.
|
|
211
|
+
|
|
212
|
+
Any other source leaves the pin alone: a resume must not clobber the
|
|
213
|
+
id the spawn will attach to. Returns True only when the pin was rewritten.
|
|
214
|
+
"""
|
|
215
|
+
if not isinstance(payload, dict) or payload.get("source") != "clear":
|
|
216
|
+
return False
|
|
217
|
+
raw = payload.get("session_id") or payload.get("sessionId") or ""
|
|
218
|
+
try:
|
|
219
|
+
session_id = validate_uuid_str(str(raw))
|
|
220
|
+
except (CellError, TypeError, ValueError):
|
|
221
|
+
return False
|
|
222
|
+
path = session_state_path(role)
|
|
223
|
+
_uuid, recorded_cwd = _read_session_sidecar(path)
|
|
224
|
+
payload_cwd = payload.get("cwd") or ""
|
|
225
|
+
# ruling_1236: a clear from a different project must not rewrite
|
|
226
|
+
# another role's pin (shared HOME, card #964). Refuse only when
|
|
227
|
+
# both sides name a cwd and they differ. A missing side keeps the
|
|
228
|
+
# old fallback.
|
|
229
|
+
if recorded_cwd and str(payload_cwd).strip():
|
|
230
|
+
if os.path.normpath(recorded_cwd) != os.path.normpath(str(payload_cwd)):
|
|
231
|
+
print(
|
|
232
|
+
f"rowclear: payload cwd {payload_cwd} differs from the "
|
|
233
|
+
f"pin's recorded cwd {recorded_cwd} — pin not rewritten",
|
|
234
|
+
file=sys.stderr,
|
|
235
|
+
)
|
|
236
|
+
return False
|
|
237
|
+
cwd = payload_cwd or recorded_cwd or ""
|
|
238
|
+
_write_session_sidecar(path, session_id, cwd)
|
|
239
|
+
return True
|
|
@@ -41,6 +41,12 @@ from typing import IO, Any, Optional
|
|
|
41
41
|
|
|
42
42
|
_IDLE_DEFAULT_SECONDS = 1800
|
|
43
43
|
|
|
44
|
+
# Card #1050: ANY per-line failure is a logged skip, never a process death
|
|
45
|
+
# (a death in --once mode reads as "no DM arrived"). The notice keeps the
|
|
46
|
+
# [MESH DM prefix and goes to stdout so a downstream waker wakes on it;
|
|
47
|
+
# it is ASCII-only so it cannot itself raise on a hostile-errors stdout.
|
|
48
|
+
_SKIP_NOTICE = "[MESH DM] unrenderable line skipped"
|
|
49
|
+
|
|
44
50
|
|
|
45
51
|
def _reader(stream: IO[str], q: "queue.Queue[Optional[str]]") -> None:
|
|
46
52
|
"""Daemon thread: blocking readline loop, None sentinel on EOF."""
|
|
@@ -110,11 +116,16 @@ def run_filter(
|
|
|
110
116
|
except Exception:
|
|
111
117
|
d = None
|
|
112
118
|
if isinstance(d, dict):
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
119
|
+
try:
|
|
120
|
+
d = d.get("dm") or d
|
|
121
|
+
rendered = _format_dm(d)
|
|
122
|
+
if rendered is not None:
|
|
123
|
+
print(rendered, file=stdout, flush=True)
|
|
124
|
+
quiet = False
|
|
125
|
+
except BrokenPipeError:
|
|
126
|
+
raise # a closed pipe stays loud (SIGPIPE contract)
|
|
127
|
+
except Exception:
|
|
128
|
+
print(_SKIP_NOTICE, file=stdout, flush=True)
|
|
118
129
|
processed += 1
|
|
119
130
|
if max_lines is not None and processed >= max_lines:
|
|
120
131
|
return 0
|
|
@@ -148,11 +159,16 @@ def run_once(path: str, stdout: IO[str], *, timeout: float = 2.0) -> int:
|
|
|
148
159
|
except Exception:
|
|
149
160
|
continue
|
|
150
161
|
if isinstance(d, dict):
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
162
|
+
try:
|
|
163
|
+
d = d.get("dm") or d
|
|
164
|
+
rendered = _format_dm(d) if isinstance(d, dict) else None
|
|
165
|
+
if rendered is not None:
|
|
166
|
+
print(rendered, file=stdout, flush=True)
|
|
167
|
+
return 0
|
|
168
|
+
except BrokenPipeError:
|
|
169
|
+
raise # a closed pipe stays loud (SIGPIPE contract)
|
|
170
|
+
except Exception:
|
|
171
|
+
print(_SKIP_NOTICE, file=stdout, flush=True)
|
|
156
172
|
time.sleep(0.05)
|
|
157
173
|
return 1
|
|
158
174
|
|
|
@@ -171,6 +187,13 @@ def main(argv: Optional[list[str]] = None) -> int:
|
|
|
171
187
|
p.add_argument("--timeout", type=float, default=2.0,
|
|
172
188
|
help="seconds --once waits for a new DM (default: %(default)s)")
|
|
173
189
|
args = p.parse_args(argv)
|
|
190
|
+
try:
|
|
191
|
+
# Card #1050: lone surrogates in DM content must render (as ?) and
|
|
192
|
+
# continue, never kill the watch on a strict-errors stdout. Set
|
|
193
|
+
# before EITHER print path: --once and filter mode both emit DMs.
|
|
194
|
+
sys.stdout.reconfigure(errors="replace")
|
|
195
|
+
except Exception:
|
|
196
|
+
pass
|
|
174
197
|
if args.once:
|
|
175
198
|
if not args.inbox:
|
|
176
199
|
print("dm_notify_filter --once needs --inbox", file=sys.stderr)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: swarph-cli
|
|
3
|
-
Version: 0.72.
|
|
3
|
+
Version: 0.72.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
|
|
@@ -26,6 +26,7 @@ src/swarph_cli/peer_service_payload.py
|
|
|
26
26
|
src/swarph_cli/peer_service_reply.py
|
|
27
27
|
src/swarph_cli/peer_staging.py
|
|
28
28
|
src/swarph_cli/query_filters.py
|
|
29
|
+
src/swarph_cli/rowclear.py
|
|
29
30
|
src/swarph_cli/session_bridge.py
|
|
30
31
|
src/swarph_cli/source_text.py
|
|
31
32
|
src/swarph_cli/stall_alert.py
|
|
@@ -109,6 +110,7 @@ src/swarph_cli/commands/peer_reply_drain.py
|
|
|
109
110
|
src/swarph_cli/commands/postcompact_hook_output.py
|
|
110
111
|
src/swarph_cli/commands/protocol_handler.py
|
|
111
112
|
src/swarph_cli/commands/ratify.py
|
|
113
|
+
src/swarph_cli/commands/rowclear_hook.py
|
|
112
114
|
src/swarph_cli/commands/schedule.py
|
|
113
115
|
src/swarph_cli/commands/security.py
|
|
114
116
|
src/swarph_cli/commands/service.py
|
|
@@ -191,6 +193,7 @@ tests/test_1006_session_board.py
|
|
|
191
193
|
tests/test_1010_strand_only.py
|
|
192
194
|
tests/test_1013_spawn_channel.py
|
|
193
195
|
tests/test_1017_muse_tui.py
|
|
196
|
+
tests/test_1050_surrogate_wake.py
|
|
194
197
|
tests/test_124_register_merge.py
|
|
195
198
|
tests/test_126_pending_from_complete.py
|
|
196
199
|
tests/test_132_ensure_monitor_state_dir.py
|
|
@@ -209,6 +212,7 @@ tests/test_356_query_filters.py
|
|
|
209
212
|
tests/test_397_gh_identity_router.py
|
|
210
213
|
tests/test_397_router_preflight.py
|
|
211
214
|
tests/test_402_one_identity_resolver.py
|
|
215
|
+
tests/test_432_rowclear.py
|
|
212
216
|
tests/test_467b_onboard_operator_credential.py
|
|
213
217
|
tests/test_472_register_caller_binding.py
|
|
214
218
|
tests/test_484_codex_queue_sink.py
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""#1050 — a DM whose content holds a lone surrogate escape must not kill
|
|
2
|
+
the wake filter.
|
|
3
|
+
|
|
4
|
+
Found by drop-on-meta-edge in #1298 (card #1049 Go A/B): a valid JSON line
|
|
5
|
+
carrying e.g. \\ud83d makes the filter raise UnicodeEncodeError on print —
|
|
6
|
+
exit 1, NO deaf-watch line, and every later DM is never surfaced. This is
|
|
7
|
+
the fleet's silent-wake filter (#482): any peer's DM can deafen a cell's
|
|
8
|
+
wake without the deaf alarm firing.
|
|
9
|
+
|
|
10
|
+
The escape MUST be outside Python's surrogateescape round-trip range
|
|
11
|
+
(DC80-DCFF): \\udcff survives on UTF-8-mode boxes (it round-trips raw
|
|
12
|
+
byte 0xFF through a pipe) and only kills on strict-errors stdouts, while
|
|
13
|
+
\\ud83d (lead) and \\udfff (trail, above DCFF) kill everywhere — measured
|
|
14
|
+
dead on the pre-fix filter under both default and strict stdouts (#1302).
|
|
15
|
+
|
|
16
|
+
Seam: the process boundary — stdin lines in, stdout lines out — exactly
|
|
17
|
+
what the accept check names.
|
|
18
|
+
"""
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import os
|
|
22
|
+
import subprocess
|
|
23
|
+
import sys
|
|
24
|
+
|
|
25
|
+
import pytest
|
|
26
|
+
|
|
27
|
+
MOD = [sys.executable, "-u", "-m", "swarph_cli.scripts.dm_notify_filter"]
|
|
28
|
+
|
|
29
|
+
# Lethal on every box (see module docstring for why these, not \\udcff).
|
|
30
|
+
LETHAL_ESCAPES = ("\\ud83d", "\\udfff")
|
|
31
|
+
|
|
32
|
+
GOOD = '{"id":12,"from_node":"b","kind":"fyi","content":"after bad"}'
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _run(payload: bytes) -> "subprocess.CompletedProcess[bytes]":
|
|
36
|
+
return subprocess.run(
|
|
37
|
+
MOD + ["--idle-seconds", "3600"],
|
|
38
|
+
input=payload,
|
|
39
|
+
capture_output=True,
|
|
40
|
+
timeout=60,
|
|
41
|
+
env=dict(os.environ),
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _bad(mid: int, esc: str) -> str:
|
|
46
|
+
return ('{"id":%d,"from_node":"a","kind":"fyi","content":"AB%scCD"}'
|
|
47
|
+
% (mid, esc))
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@pytest.mark.parametrize("esc", LETHAL_ESCAPES)
|
|
51
|
+
def test_lone_surrogate_then_good_dm_survives(esc: str):
|
|
52
|
+
"""The good DM after a lone-surrogate line is printed; the process
|
|
53
|
+
keeps running to EOF (deaf line) instead of dying on the bad line."""
|
|
54
|
+
p = _run((_bad(11, esc) + "\n" + GOOD + "\n").encode("ascii"))
|
|
55
|
+
assert b"id=12" in p.stdout, (
|
|
56
|
+
f"good DM lost after lone-surrogate line: rc={p.returncode} "
|
|
57
|
+
f"stdout={p.stdout!r} stderr={p.stderr[-300:]!r}"
|
|
58
|
+
)
|
|
59
|
+
assert b"DEAF" in p.stdout, (
|
|
60
|
+
f"watch died without its deaf-watch line: rc={p.returncode} "
|
|
61
|
+
f"stderr={p.stderr[-300:]!r}"
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_lone_surrogate_never_kills_process():
|
|
66
|
+
"""No per-line content may exit the filter: a whole stream of hostile
|
|
67
|
+
lines still ends at the EOF deaf line, not a traceback."""
|
|
68
|
+
lines = ([_bad(i, LETHAL_ESCAPES[i % 2]) for i in range(20)]
|
|
69
|
+
+ [GOOD])
|
|
70
|
+
p = _run(("\n".join(lines) + "\n").encode("ascii"))
|
|
71
|
+
assert b"id=12" in p.stdout
|
|
72
|
+
assert b"DEAF" in p.stdout
|
|
73
|
+
assert b"Traceback" not in p.stderr
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def test_once_skips_non_string_content_and_wakes_on_next(tmp_path):
|
|
77
|
+
"""#1310: run_once shares the never-die contract. A first-arriving DM
|
|
78
|
+
whose content is not a string (a number here) must become a logged
|
|
79
|
+
skip, not a death read as 'no DM arrived'; the next good DM still
|
|
80
|
+
wakes (exit 0). Seam: the --once process boundary, fed live."""
|
|
81
|
+
import threading
|
|
82
|
+
import time
|
|
83
|
+
|
|
84
|
+
inbox = tmp_path / "inbox.log"
|
|
85
|
+
inbox.write_text("")
|
|
86
|
+
proc = subprocess.Popen(
|
|
87
|
+
MOD + ["--once", "--inbox", str(inbox), "--timeout", "10"],
|
|
88
|
+
stdout=subprocess.PIPE,
|
|
89
|
+
stderr=subprocess.PIPE,
|
|
90
|
+
env=dict(os.environ),
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
def feed():
|
|
94
|
+
time.sleep(0.5)
|
|
95
|
+
with open(inbox, "a") as fh:
|
|
96
|
+
fh.write('{"id":41,"from_node":"a","kind":"fyi",'
|
|
97
|
+
'"content":42}\n')
|
|
98
|
+
time.sleep(0.5)
|
|
99
|
+
with open(inbox, "a") as fh:
|
|
100
|
+
fh.write(GOOD + "\n")
|
|
101
|
+
|
|
102
|
+
threading.Thread(target=feed, daemon=True).start()
|
|
103
|
+
out, err = proc.communicate(timeout=30)
|
|
104
|
+
assert b"Traceback" not in err, f"run_once died: {err[-300:]!r}"
|
|
105
|
+
assert b"unrenderable line skipped" in out, (
|
|
106
|
+
f"numeric content was not a logged skip: rc={proc.returncode} "
|
|
107
|
+
f"stdout={out!r}"
|
|
108
|
+
)
|
|
109
|
+
assert b"id=12" in out, (
|
|
110
|
+
f"good DM after the skip did not wake: rc={proc.returncode} "
|
|
111
|
+
f"stdout={out!r}"
|
|
112
|
+
)
|
|
113
|
+
assert proc.returncode == 0, (
|
|
114
|
+
f"wake on the good DM must exit 0, got {proc.returncode}"
|
|
115
|
+
)
|