swarph-cli 0.72.32__tar.gz → 0.72.34__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.32/src/swarph_cli.egg-info → swarph_cli-0.72.34}/PKG-INFO +1 -1
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/pyproject.toml +1 -1
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/__init__.py +1 -1
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/mcp_server.py +83 -9
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/dreaming/index_check.py +28 -4
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/dreaming/notify.py +53 -2
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/triage/backend.py +6 -2
- {swarph_cli-0.72.32 → swarph_cli-0.72.34/src/swarph_cli.egg-info}/PKG-INFO +1 -1
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli.egg-info/SOURCES.txt +3 -0
- swarph_cli-0.72.34/tests/test_1269_triage_label.py +25 -0
- swarph_cli-0.72.34/tests/test_1278_dreaming_reach.py +70 -0
- swarph_cli-0.72.34/tests/test_1286_dm_unread_cursor.py +192 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_735_dm_mcp_tools.py +4 -1
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/LICENSE +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/README.md +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/setup.cfg +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/__main__.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/bench/__init__.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/bench/backends.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/bench/claude_cli.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/bench/codex_cli.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/bench/pack.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/bench/prices.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/bench/providers.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/bench/quality.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/bench/refresh_prices.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/bench/rules.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/bench/runner.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/bench/signal_score.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/bench/validate.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/caller.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/capture/__init__.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/capture/harden.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/capture/lineage.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/capture/liveness.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/capture/manifest.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/capture/paths.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/capture/verify.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/cell.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/chain_token.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/channel.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/__init__.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/_content.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/_display.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/_gateway_client.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/add.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/bench.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/board.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/board_merge_check.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/brain.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/brain_ask.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/cell.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/cell_probe.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/channel.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/chat.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/codegraph.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/codegraph_hook.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/codex_hook_output.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/codex_waker.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/compress.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/daemon.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/dreaming.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/event.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/gateway.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/gh_route.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/group.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/guide.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/highlight.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/hook_interpreter.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/hook_output.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/hooks.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/import_session.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/init.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/install_hook.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/install_multiplexer.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/install_wake_hook.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/lane.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/memory.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/memory_sync.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/mesh.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/monitor.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/okf_links.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/onboard.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/protocol_handler.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/ratify.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/roster.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/rowclear_hook.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/schedule.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/security.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/service.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/spawn.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/sweep.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/timeline.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/triage.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/version.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/wake_hook_output.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/commands/watchdog.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/compress/__init__.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/compress/levers.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/compress/marker.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/compress/verify.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/console_safe.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/delivery_queue.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/dm_frame.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/dreaming/__init__.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/dreaming/candidates.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/dreaming/clone.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/dreaming/enrich.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/dreaming/organize.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/dreaming/probes.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/dreaming/report.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/dreaming/run.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/dreaming/slm.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/dreaming/transcripts.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/dreaming/verify.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/duplicate_session.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/gateway/__init__.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/gateway/feature_registry.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/gateway/lanes_control.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/gateway/schema.sql +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/gateway/server.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/gateway/services_control.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/gateway_default.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/gh_identity.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/guide/GUIDE.md +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/identity.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/launchd/swarph-codex-outbox.plist +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/launchd/swarph-codex-outbox.timer.plist +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/launchd/swarph-monitor.plist +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/launchd/swarph-watchdog.plist +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/launchd/swarph-watchdog.timer.plist +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/launchd_render.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/main.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/multiplexer.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/operator/__init__.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/operator/__main__.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/operator/api.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/operator/cli.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/operator/config.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/operator/fmt.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/orphan_daemons.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/pane_probe.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/parsers/__init__.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/parsers/claude.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/peer_executor.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/peer_executor_windows.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/peer_reconciliation.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/peer_reply_outbox.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/peer_service_host.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/peer_service_payload.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/peer_service_reply.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/peer_staging.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/query_filters.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/quota_sync.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/rowclear.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/scripts/__init__.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/scripts/wake_watchdog.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/scrub.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/service/__init__.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/service/app.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/service/providers.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/session_bridge.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/source_text.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/stall_alert.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/systemd/codex-waker.default +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/systemd/swarph-stuck-sweep.service +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/systemd/swarph-stuck-sweep.timer +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/systemd/swarph-triage.service +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/systemd/swarph-triage.timer +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/the_ten.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/timeline_paths.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/tokens.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/triage/__init__.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/triage/job.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/triage/owners.json +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/triage/router.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/triage/rows.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli/triage/sensor.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli.egg-info/entry_points.txt +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli.egg-info/requires.txt +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/src/swarph_cli.egg-info/top_level.txt +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1006_board_publisher.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1006_session_board.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1010_strand_only.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1013_spawn_channel.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1017_muse_tui.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1034_copy_mode.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1050_surrogate_wake.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1052_opencode_deaf.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1059_watchdog_unread_env.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1061_roster_cli.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1064_stuck_sweep.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1065_release.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1066_quiet_wakes.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1068_structured_accept_cli.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1071_quota_sync.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1073_triage.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1073_triage_board.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1073_wheel_install.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1075_task_cli.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1083_openai_decisions.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1144_cell_yaml_github.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1197_commander_taps.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1203_swarph_me_refusal.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1206_offer_question.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1214_egress.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1219_egress_probe.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1221_quota_windows.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1222_maybe_start_cap.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1228_quota_reach.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1243_the_ten.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1248_ship_price_table.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_124_register_merge.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1250_price_cache.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1256_commander_wake.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1261_mcp_install.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1262_wake_followups.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_1265_wake_note.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_126_pending_from_complete.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_132_ensure_monitor_state_dir.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_138_memory_navigate.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_139_tool_probe.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_183_card_anchor.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_184_notify_honesty.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_201_win32_bash_refusal.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_243_onboard_per_peer_token.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_307_task4_ask_reply.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_319_refusal_exit_codes.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_323_mistral_backend.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_332_report_organize_findings.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_356_query_filters.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_397_gh_identity_router.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_397_router_preflight.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_402_one_identity_resolver.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_432_rowclear.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_467b_onboard_operator_credential.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_472_register_caller_binding.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_484_codex_queue_sink.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_513_duplicate_session.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_523_guide.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_524_conftest_hermeticity.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_532_accept_flag.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_535_version_capability.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_538_signal_bench.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_546_default_gateway.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_547_verbs_from_registry.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_548_gateway_remainder.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_552_codegraph_gateway.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_555_relay_row_shape.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_556_relay_freshness.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_559_edges_truncated.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_564_bootstrap_first_peer.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_579_register_bootstrap_message.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_591_cli_step_graph.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_644_monitor_supervision.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_648_install_unit.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_654_reexec.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_656_owned_slm.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_684_dreaming_verb.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_689_pkg_version_order.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_715_grok_wake.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_716_timeline_one_path.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_723_grok_0251.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_724_ps1_ascii_or_bom.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_725_stdio_reconfigure.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_726_grok_limit_screen.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_729_arm_check_delivery_gap.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_729_dm_once.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_729_wake_watchdog.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_734_dreaming_enrich_degrade.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_739_wake_writer.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_740_edit_project.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_762_cards_edit_priority.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_764_enrich_collapse.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_801_confirm_flow_cli.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_802_stage_history.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_825_codegraph_hook_noise.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_825_install_posttooluse_only.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_825_remaining_cap_shred_bind.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_829_counterfactual_column.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_830_hook_binding_version.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_84_cli_labels.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_866_doctrine_guide.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_872_identity_refuses_not_warns.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_874_source_text.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_931_board_search.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_935_deadline_clock.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_937_dreaming_notify.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_938_organize_line_limit.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_941_bench_backends.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_941_harness_342.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_945_same_line_pairing.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_955_prompt_audit.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_957_auth_http.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_959_venv_interpreter.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_960_channel.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_960_ensure_monitor_heartbeat.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_961_cursor_grok_footer.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_961_loud_stop.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_963_plugin_manifest.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_964_channel_cell.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_965_mesh_wait.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_975_channel_hop.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_add_security_gate.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_artifact_add.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_artifact_hash.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_artifact_lib.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_artifact_mcp.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_artifact_skill.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_artifact_tool.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_artifact_uri.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_bench_backends.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_bench_claude_cli.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_bench_codex_cli.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_bench_command.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_bench_pack.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_bench_prices.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_bench_quality.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_bench_runner.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_bench_validate.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_board_card_thread.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_board_command.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_board_merge_check.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_board_obligation_close.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_brain_ask_command.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_brain_ask_env_diagnosis.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_brain_ask_identity_explicitness.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_brain_command.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_caller_meta_guard.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_capture_lineage.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_capture_liveness.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_capture_manifest.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_capture_paths.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_capture_security.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_cell_command_dispatch.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_cell_harden.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_cell_loader.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_cell_selfcheck.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_cell_verify.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_chain_token.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_channel_command.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_channel_poll_never_breaks_dms.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_channel_post.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_channel_wake_policy_filter.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_chat_command.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_claude_parser.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_claude_tmux_template.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_codegraph_caller_scope.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_codegraph_command.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_codegraph_hook.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_codegraph_mcp.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_codex_waker.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_compress_caller_convention.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_compress_command.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_compress_levers.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_compress_marker.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_compress_verify.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_console_encoding_e2e.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_content_composition.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_copilot_membrane.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_copilot_pane_reader.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_cursor_membrane.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_daemon_command.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_daemon_console_encoding.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_daemon_delivery.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_delivery_queue.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_display_sanitize.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_ensure_monitor_no_peer_default.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_event_emit.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_feature_to_uri.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_gateway_brain.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_gateway_client.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_gateway_command.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_gateway_message_idempotency.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_gh_route_doctor_mesh_name.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_group_command.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_guide_commands_parse.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_guide_selfcheck.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_heartbeat_check.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_highlight_command.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_hook_output.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_hooks_activity_marker.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_hooks_add.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_hooks_bundle.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_hooks_init.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_hooks_legacy_generation_removal.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_hooks_lifecycle.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_hooks_merge.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_hooks_verify.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_hooks_win32_bash_wrapper.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_hooks_win32_command_path.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_import_command.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_inbox_identity_provenance.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_init_command.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_init_provider_menu.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_install_codex_hooks.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_install_hook.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_install_multiplexer_command.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_install_postcompact_hook.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_install_wake_hook.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_lane_command.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_launchd_units.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_main.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_main_unknown_verb_guard.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_mcp_missing_hint.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_mcp_server.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_memory_command.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_memory_emit_snippet.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_memory_gateway.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_memory_sync.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_memory_sync_membrane.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_mesh_command.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_mesh_inbox_mark_read.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_mesh_inbox_sanitize.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_mesh_originate_thread_623.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_mesh_send_unknown_peer.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_mesh_sidecar.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_mesh_sidecar_muse_sink.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_meta_edge_identity.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_monitor_channel_poll.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_monitor_cli.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_monitor_daemon_collision.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_monitor_self_name_verification.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_monitor_state_split.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_multiplexer.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_muse_membrane.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_muse_own_cli.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_obligation_close_is_surfaced.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_okf_links.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_onboard_command.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_opencode_membrane.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_opencode_plugin.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_operator_me.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_orphan_daemons.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_packaged_artifacts_exist.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_packaged_skills.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_peer_executor.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_peer_executor_windows.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_peer_reply_drain_command.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_peer_reply_drain_supervisor.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_peer_reply_outbox.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_peer_reply_transport.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_peer_service_e2e.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_peer_service_host.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_peer_service_payload.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_peer_service_reply.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_postcompact_recall.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_process_liveness.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_protocol_handler.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_providers_isolation.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_ratify_command.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_readme_claims_no_gateway_default.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_repl_caller_convention.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_schedule_command.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_schedule_create_contract.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_scrub.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_security.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_service_app.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_service_command.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_service_providers.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_session_bridge_inject.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_session_bridge_probe.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_session_bridge_resolve.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_session_path_role_gate.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_shipped_units_match_the_cli.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_smoke_chat.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_smoke_one_shot.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_smoke_phase_5_5.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_spawn_base_hoist.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_spawn_command.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_spawn_identity_stamp.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_spawn_live_pin.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_spawn_mitosis_lineage.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_spawn_tmux_session.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_spawn_windows_relaunch.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_stall_alert.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_subprocess_decode_encoding.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_timeline_command.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_tmux_wake_runstate_defer.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_tmux_wake_session_reanchor.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_tmux_wake_stale.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_tmux_wake_submit_verify.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_token_file_one_parser.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_token_precedence_explicit_first.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_unified_token_resolver.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_version_command.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_version_consistency.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_vibe_membrane.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_watchdog.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_watchdog_dm_wake.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_watchdog_dm_wake_wiring.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_watchdog_emit_health.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_watchdog_install_flags.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_watchdog_model_rung.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/tests/test_watchdog_stale_peers.py +0 -0
- {swarph_cli-0.72.32 → swarph_cli-0.72.34}/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.34
|
|
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.34"
|
|
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" }
|
|
@@ -33,9 +33,11 @@ without the SDK installed.
|
|
|
33
33
|
from __future__ import annotations
|
|
34
34
|
|
|
35
35
|
import argparse
|
|
36
|
+
import datetime
|
|
36
37
|
import importlib.metadata
|
|
37
38
|
import json
|
|
38
39
|
import os
|
|
40
|
+
import time
|
|
39
41
|
import urllib.error
|
|
40
42
|
import urllib.request
|
|
41
43
|
|
|
@@ -301,17 +303,82 @@ def _dm_error(exc_or_text) -> dict:
|
|
|
301
303
|
return {"error": str(exc_or_text), "messages": [], "n": 0}
|
|
302
304
|
|
|
303
305
|
|
|
306
|
+
# One poll records the cursor at the newest id it saw. The earlier DMs in that
|
|
307
|
+
# poll are behind the cursor and were not delivered. They stay visible while
|
|
308
|
+
# they are younger than this (card #1286). Older than this, they are hidden
|
|
309
|
+
# and counted.
|
|
310
|
+
_DM_GRACE_S = 24 * 60 * 60
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
def _created_epoch(message: dict) -> float | None:
|
|
314
|
+
raw = message.get("created_at")
|
|
315
|
+
if raw is None or raw == "":
|
|
316
|
+
return None
|
|
317
|
+
if isinstance(raw, bool):
|
|
318
|
+
return None
|
|
319
|
+
if isinstance(raw, (int, float)):
|
|
320
|
+
return float(raw)
|
|
321
|
+
text = str(raw).strip()
|
|
322
|
+
if text.endswith("Z"):
|
|
323
|
+
text = text[:-1] + "+00:00"
|
|
324
|
+
try:
|
|
325
|
+
return datetime.datetime.fromisoformat(text).timestamp()
|
|
326
|
+
except ValueError:
|
|
327
|
+
return None
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def _younger_than_grace(message: dict, now: float) -> bool:
|
|
331
|
+
ts = _created_epoch(message)
|
|
332
|
+
if ts is None:
|
|
333
|
+
return False
|
|
334
|
+
return (now - ts) < _DM_GRACE_S
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
def _observation_cursor(who: str) -> int:
|
|
338
|
+
"""The monitor's last_msg_id for this cell. Missing or unreadable is 0.
|
|
339
|
+
|
|
340
|
+
Peek only: nothing here writes cursor.json or marks a message read.
|
|
341
|
+
"""
|
|
342
|
+
path = _mesh._default_sidecar_state_dir(who) / "cursor.json"
|
|
343
|
+
try:
|
|
344
|
+
return int(_mesh._read_cursor(path).get("last_msg_id") or 0)
|
|
345
|
+
except (TypeError, ValueError):
|
|
346
|
+
return 0
|
|
347
|
+
|
|
348
|
+
|
|
304
349
|
def _dm_unread(self_name: str | None = None) -> dict:
|
|
305
|
-
"""
|
|
306
|
-
|
|
307
|
-
`
|
|
350
|
+
"""Unread DMs the monitor has not yet delivered, full bodies, one call.
|
|
351
|
+
|
|
352
|
+
`read_at` is still the reply-ack (#735). The observation cursor advances
|
|
353
|
+
before delivery, so a DM with `id == last_msg_id` was observed and must
|
|
354
|
+
be returned (card #1286). One poll records the cursor at the newest id,
|
|
355
|
+
so the earlier DMs in that batch are behind it. A DM younger than 24 h
|
|
356
|
+
(`created_at`) is never hidden for that reason. A DM strictly behind the
|
|
357
|
+
cursor and not younger than 24 h stays hidden, and `hidden_stale` counts
|
|
358
|
+
those read_at-null DMs. Peek semantics: marks nothing read and does not
|
|
359
|
+
advance the cursor. Above DM_CEILING the oldest DM_CEILING are returned
|
|
360
|
+
in full and `not_returned` says how many remain.
|
|
361
|
+
"""
|
|
308
362
|
try:
|
|
309
363
|
who, gw, tok = _dm_session(self_name)
|
|
310
364
|
st, d = _mesh._http_get_json(
|
|
311
365
|
f"{gw}/messages?to={who}&unread_only=true&limit={DM_GATEWAY_WINDOW}", tok)
|
|
312
366
|
if not (200 <= st < 300):
|
|
313
367
|
return _dm_error(f"gateway {st}: {d.get('detail', d) if isinstance(d, dict) else d}")
|
|
314
|
-
|
|
368
|
+
raw = sorted(d.get("messages", []), key=lambda m: int(m.get("id") or 0))
|
|
369
|
+
last_id = _observation_cursor(who)
|
|
370
|
+
now = time.time()
|
|
371
|
+
msgs = []
|
|
372
|
+
hidden_stale = 0
|
|
373
|
+
for message in raw:
|
|
374
|
+
if message.get("from_node") == who:
|
|
375
|
+
continue
|
|
376
|
+
mid = int(message.get("id") or 0)
|
|
377
|
+
if mid >= last_id or _younger_than_grace(message, now):
|
|
378
|
+
msgs.append(message)
|
|
379
|
+
continue
|
|
380
|
+
if message.get("read_at") in (None, ""):
|
|
381
|
+
hidden_stale += 1
|
|
315
382
|
keep = msgs[:DM_CEILING]
|
|
316
383
|
return {
|
|
317
384
|
"self": who,
|
|
@@ -319,7 +386,8 @@ def _dm_unread(self_name: str | None = None) -> dict:
|
|
|
319
386
|
"n": len(keep),
|
|
320
387
|
"unread_seen": len(msgs),
|
|
321
388
|
"not_returned": len(msgs) - len(keep),
|
|
322
|
-
"
|
|
389
|
+
"hidden_stale": hidden_stale,
|
|
390
|
+
"window_full": len(raw) >= DM_GATEWAY_WINDOW,
|
|
323
391
|
}
|
|
324
392
|
except Exception as exc: # loud, never an empty inbox
|
|
325
393
|
return _dm_error(f"{type(exc).__name__}: {exc}")
|
|
@@ -473,10 +541,16 @@ try:
|
|
|
473
541
|
|
|
474
542
|
@mcp.tool()
|
|
475
543
|
def swarph_dm_unread() -> dict:
|
|
476
|
-
"""
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
544
|
+
"""Unread DMs for this cell, FULL bodies, ONE call (card #735, card #1286).
|
|
545
|
+
|
|
546
|
+
Peek semantics: nothing is marked read and the monitor cursor is not moved.
|
|
547
|
+
The DM whose id equals the cursor was observed and not yet delivered, so
|
|
548
|
+
it is returned. A DM strictly behind the cursor is still returned when
|
|
549
|
+
its created_at is younger than 24 h. Older than that, it stays hidden
|
|
550
|
+
and hidden_stale counts it. No paging parameter.
|
|
551
|
+
Replying with swarph_dm_reply is the ack.
|
|
552
|
+
`not_returned` > 0 means more than the ceiling were unread and the oldest
|
|
553
|
+
were returned in full."""
|
|
480
554
|
return _dm_unread()
|
|
481
555
|
|
|
482
556
|
@mcp.tool()
|
|
@@ -41,6 +41,25 @@ MEM = pathlib.Path(
|
|
|
41
41
|
next((a.split("=", 1)[1] for a in sys.argv if a.startswith("--mem-dir=")),
|
|
42
42
|
str(pathlib.Path.home() / ".claude/projects/-home-ubuntu/memory")))
|
|
43
43
|
INDEXES = ("MEMORY.md", "MEMORY_FULL.md")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def index_names(mem: pathlib.Path) -> list[str]:
|
|
47
|
+
"""Files whose pointers count: every MEMORY*.md and index_*.md.
|
|
48
|
+
|
|
49
|
+
MEMORY.md and MEMORY_FULL.md stay required, so a missing one is still
|
|
50
|
+
MISSING INDEX. Pointers also live in index_*.md after the index-of-indexes
|
|
51
|
+
restructure. A file named only there is not an orphan (card #1278).
|
|
52
|
+
"""
|
|
53
|
+
found: list[str] = []
|
|
54
|
+
if mem.is_dir():
|
|
55
|
+
found = [
|
|
56
|
+
p.name for p in sorted(mem.glob("*.md"))
|
|
57
|
+
if p.name.startswith("MEMORY") or p.name.startswith("index_")
|
|
58
|
+
]
|
|
59
|
+
for name in INDEXES:
|
|
60
|
+
if name not in found:
|
|
61
|
+
found.append(name)
|
|
62
|
+
return found
|
|
44
63
|
BUDGET = 24985 # bytes, MEMORY.md only — stated in the file's own header comment
|
|
45
64
|
# THE THIRD STATE. Without WARN this check reported "clean" at 24,972 bytes —
|
|
46
65
|
# 13 bytes of headroom — which is the exact condition ORGANIZE exists to
|
|
@@ -189,9 +208,10 @@ def main() -> int:
|
|
|
189
208
|
print(f"memory-index-check: {MEM} is not a directory", file=sys.stderr)
|
|
190
209
|
return 1
|
|
191
210
|
|
|
192
|
-
|
|
211
|
+
indexes = index_names(MEM)
|
|
212
|
+
files = {p.name for p in MEM.glob("*.md")} - set(indexes)
|
|
193
213
|
linked: set[str] = set()
|
|
194
|
-
for i in
|
|
214
|
+
for i in indexes:
|
|
195
215
|
linked |= links_in(i)
|
|
196
216
|
|
|
197
217
|
findings: list[str] = []
|
|
@@ -203,7 +223,7 @@ def main() -> int:
|
|
|
203
223
|
if len(orphans) > 20:
|
|
204
224
|
findings.append(f" … and {len(orphans) - 20} more")
|
|
205
225
|
|
|
206
|
-
for idx in
|
|
226
|
+
for idx in indexes:
|
|
207
227
|
p = MEM / idx
|
|
208
228
|
if not p.exists():
|
|
209
229
|
findings.append(f"MISSING INDEX: {idx}")
|
|
@@ -213,7 +233,11 @@ def main() -> int:
|
|
|
213
233
|
if line.startswith("- ") and TRUNC.search(line):
|
|
214
234
|
findings.append(f"TRUNCATED: {idx}:{n} ends mid-link — ...{line[-46:]}")
|
|
215
235
|
|
|
216
|
-
|
|
236
|
+
# An index file is not a missing memory. MEMORY.md (the index of indexes)
|
|
237
|
+
# links at index_*.md, and those names were removed from `files` above, so
|
|
238
|
+
# linked - files reported every one of them DANGLING (card #1278 rework).
|
|
239
|
+
# Subtract the index names too: dangling is a pointer to no existing .md.
|
|
240
|
+
dangling = sorted(linked - files - set(indexes))
|
|
217
241
|
if dangling:
|
|
218
242
|
findings.append(f"DANGLING: {len(dangling)} pointer(s) to a file that does not exist")
|
|
219
243
|
findings += [f" {d}" for d in dangling[:20]]
|
|
@@ -141,13 +141,64 @@ def apply(out: Path, verdicts, organized, cell: str, sender) -> bool:
|
|
|
141
141
|
return True
|
|
142
142
|
|
|
143
143
|
|
|
144
|
+
def _post_to_card(card: str, content: str) -> None:
|
|
145
|
+
"""Put the finding on a card. A DM to the sender is refused (#185 / #409).
|
|
146
|
+
|
|
147
|
+
Card #864: a finding goes on a card. Card #1278: the dreaming unit is
|
|
148
|
+
lab-ovh notifying lab-ovh, so the send never arrives. Default card is 656,
|
|
149
|
+
whose rows are closed; a holder who posts to a card thread closes their
|
|
150
|
+
oldest open row on it, so do not point this at a card the sender holds.
|
|
151
|
+
"""
|
|
152
|
+
from swarph_cli.commands.board import (
|
|
153
|
+
_card_say_payload,
|
|
154
|
+
_http_get_json,
|
|
155
|
+
_post_json,
|
|
156
|
+
_project_owner_orchestrator,
|
|
157
|
+
_thread_recipient,
|
|
158
|
+
)
|
|
159
|
+
from swarph_cli.commands.mesh import _resolve_self_name, _resolve_token
|
|
160
|
+
from swarph_cli.gateway_default import env_gateway
|
|
161
|
+
|
|
162
|
+
gw = env_gateway()
|
|
163
|
+
self_name = _resolve_self_name(None)
|
|
164
|
+
token = _resolve_token(self_name, None)
|
|
165
|
+
status, card_row = _http_get_json(f"{gw}/board/cards/{card}", token)
|
|
166
|
+
if status != 200 or not isinstance(card_row, dict):
|
|
167
|
+
raise NotifySendError(f"cannot read card #{card}: {card_row}")
|
|
168
|
+
thread_uuid = card_row.get("thread_uuid")
|
|
169
|
+
if not thread_uuid:
|
|
170
|
+
raise NotifySendError(f"card #{card} has no bound thread")
|
|
171
|
+
owner = None
|
|
172
|
+
if not card_row.get("assignee"):
|
|
173
|
+
owner = _project_owner_orchestrator(gw, token, card_row.get("project_id"))
|
|
174
|
+
try:
|
|
175
|
+
to_node, _reason = _thread_recipient(card_row, None, owner_orchestrator=owner)
|
|
176
|
+
except RuntimeError as exc:
|
|
177
|
+
raise NotifySendError(str(exc)) from exc
|
|
178
|
+
status, payload = _post_json(
|
|
179
|
+
f"{gw}/messages",
|
|
180
|
+
_card_say_payload(self_name, to_node, "status", content, thread_uuid),
|
|
181
|
+
token,
|
|
182
|
+
)
|
|
183
|
+
if status < 200 or status >= 300:
|
|
184
|
+
raise NotifySendError(f"card #{card} post failed: {status} {payload}")
|
|
185
|
+
|
|
186
|
+
|
|
144
187
|
def mesh_sender(cell: str, content: str) -> None:
|
|
145
|
-
"""Same path as ``swarph mesh send
|
|
188
|
+
"""Same path as ``swarph mesh send``, unless that send would be a self-wake.
|
|
189
|
+
|
|
190
|
+
SWARPH_SELF equal to the notify cell is the dreaming unit. Mesh send
|
|
191
|
+
refuses it and the run exits 4. Post the same body to the dreaming card.
|
|
192
|
+
"""
|
|
146
193
|
import argparse
|
|
147
194
|
|
|
148
|
-
from swarph_cli.commands.mesh import _run_send
|
|
195
|
+
from swarph_cli.commands.mesh import _resolve_self_name, _run_send
|
|
149
196
|
from swarph_cli.gateway_default import env_gateway
|
|
150
197
|
|
|
198
|
+
self_name = _resolve_self_name(None)
|
|
199
|
+
if cell == self_name:
|
|
200
|
+
_post_to_card("656", content)
|
|
201
|
+
return
|
|
151
202
|
ns = argparse.Namespace(
|
|
152
203
|
to=cell,
|
|
153
204
|
kind="status",
|
|
@@ -34,7 +34,8 @@ class MemoryBackend:
|
|
|
34
34
|
f"create '{title}': project_id is required — the gateway "
|
|
35
35
|
f"refuses a card create without one (BoardCardCreate)")
|
|
36
36
|
row = {"id": self.next_id, "title": title, "owner": owner,
|
|
37
|
-
"project_id": self.project_id, "status": "open"
|
|
37
|
+
"project_id": self.project_id, "status": "open",
|
|
38
|
+
"labels": ["triage-row"]}
|
|
38
39
|
self.next_id += 1
|
|
39
40
|
self.rows[title] = row
|
|
40
41
|
self.posts[title] = []
|
|
@@ -119,7 +120,10 @@ class BoardBackend:
|
|
|
119
120
|
"body": f"Daily triage row for {owner} (card #1073). "
|
|
120
121
|
"Items are appended by the triage job; "
|
|
121
122
|
"owners work them off the row.",
|
|
122
|
-
"assignee": owner
|
|
123
|
+
"assignee": owner,
|
|
124
|
+
# card #1269: the label is how offer_dispatch leaves the
|
|
125
|
+
# minted rows unoffered. Rows stay requested.
|
|
126
|
+
"labels": ["triage-row"]}
|
|
123
127
|
try:
|
|
124
128
|
status, body = self._http_post(
|
|
125
129
|
f"{self.gateway}/board/cards", payload, self.token)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: swarph-cli
|
|
3
|
-
Version: 0.72.
|
|
3
|
+
Version: 0.72.34
|
|
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
|
|
@@ -249,7 +249,10 @@ tests/test_1256_commander_wake.py
|
|
|
249
249
|
tests/test_1261_mcp_install.py
|
|
250
250
|
tests/test_1262_wake_followups.py
|
|
251
251
|
tests/test_1265_wake_note.py
|
|
252
|
+
tests/test_1269_triage_label.py
|
|
252
253
|
tests/test_126_pending_from_complete.py
|
|
254
|
+
tests/test_1278_dreaming_reach.py
|
|
255
|
+
tests/test_1286_dm_unread_cursor.py
|
|
253
256
|
tests/test_132_ensure_monitor_state_dir.py
|
|
254
257
|
tests/test_138_memory_navigate.py
|
|
255
258
|
tests/test_139_tool_probe.py
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Card #1269. A new triage container is created with the triage-row
|
|
2
|
+
label. Fails at dba1ebc: the create payload has no labels.
|
|
3
|
+
"""
|
|
4
|
+
from swarph_cli.triage.backend import BoardBackend
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class _Http:
|
|
8
|
+
def __init__(self):
|
|
9
|
+
self.posts = []
|
|
10
|
+
|
|
11
|
+
def post(self, url, payload, token):
|
|
12
|
+
self.posts.append((url, payload))
|
|
13
|
+
return 200, {"id": 77}
|
|
14
|
+
|
|
15
|
+
def get(self, url, token):
|
|
16
|
+
raise AssertionError(url)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_create_labels_the_container_triage_row():
|
|
20
|
+
http = _Http()
|
|
21
|
+
backend = BoardBackend(
|
|
22
|
+
"http://gw:9", "tok", "lab-ovh", http.get, http.post, project_id=9)
|
|
23
|
+
backend.create_row("droplet triage", "droplet")
|
|
24
|
+
_url, payload = http.posts[-1]
|
|
25
|
+
assert payload.get("labels") == ["triage-row"]
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"""Card #1278: a self-notify must be delivered, and index_*.md pointers count."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import subprocess
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
from swarph_cli.dreaming import run as dreaming_run
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _run_check(mem: Path) -> tuple[int, str]:
|
|
12
|
+
script = Path(__file__).resolve().parents[1] / "src" / "swarph_cli" / "dreaming" / "index_check.py"
|
|
13
|
+
r = subprocess.run(
|
|
14
|
+
[sys.executable, str(script), "--mem-dir=%s" % mem, "--quiet"],
|
|
15
|
+
capture_output=True, text=True, encoding="utf-8", errors="replace")
|
|
16
|
+
return r.returncode, (r.stdout or "") + (r.stderr or "")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def test_1278_pointer_only_in_index_md_is_not_an_orphan(tmp_path):
|
|
20
|
+
mem = tmp_path / "mem"
|
|
21
|
+
mem.mkdir()
|
|
22
|
+
(mem / "MEMORY.md").write_text(
|
|
23
|
+
"- [a](a.md)\n- [topics](index_topics.md)\n", encoding="utf-8")
|
|
24
|
+
(mem / "MEMORY_FULL.md").write_text("- [a](a.md)\n", encoding="utf-8")
|
|
25
|
+
(mem / "index_topics.md").write_text("- [b](b.md)\n", encoding="utf-8")
|
|
26
|
+
(mem / "a.md").write_text("# a\n", encoding="utf-8")
|
|
27
|
+
(mem / "b.md").write_text("# b\n", encoding="utf-8")
|
|
28
|
+
rc, out = _run_check(mem)
|
|
29
|
+
assert "ORPHANS" not in out, out
|
|
30
|
+
assert "DANGLING" not in out, out
|
|
31
|
+
assert rc == 0, out
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def test_1278_self_notify_is_delivered_not_exit_4(tmp_path, monkeypatch):
|
|
35
|
+
monkeypatch.setenv("SWARPH_SELF", "lab-ovh")
|
|
36
|
+
posted = []
|
|
37
|
+
|
|
38
|
+
def clone(corpus, out):
|
|
39
|
+
Path(out).mkdir(parents=True, exist_ok=True)
|
|
40
|
+
return {}
|
|
41
|
+
|
|
42
|
+
monkeypatch.setattr(dreaming_run, "clone_corpus", clone)
|
|
43
|
+
monkeypatch.setattr(
|
|
44
|
+
dreaming_run, "verify",
|
|
45
|
+
lambda out, manifest: [{"verdict": "agree", "file": "a.md", "line": 1, "kind": "x"}],
|
|
46
|
+
)
|
|
47
|
+
monkeypatch.setattr(
|
|
48
|
+
dreaming_run, "organize",
|
|
49
|
+
lambda out: {"findings": [], "index_bytes_before": 0,
|
|
50
|
+
"index_bytes_after": 0, "trimmed": []},
|
|
51
|
+
)
|
|
52
|
+
monkeypatch.setattr(dreaming_run, "render", lambda *a, **k: "# dreaming report\n")
|
|
53
|
+
monkeypatch.setattr(
|
|
54
|
+
"swarph_cli.dreaming.notify._post_to_card",
|
|
55
|
+
lambda card, body: posted.append((str(card), body)),
|
|
56
|
+
raising=False,
|
|
57
|
+
)
|
|
58
|
+
monkeypatch.setattr("swarph_cli.commands.mesh._run_send", lambda ns: 1)
|
|
59
|
+
|
|
60
|
+
corpus = tmp_path / "corpus"
|
|
61
|
+
corpus.mkdir()
|
|
62
|
+
rc = dreaming_run.main([
|
|
63
|
+
"--corpus", str(corpus),
|
|
64
|
+
"--out", str(tmp_path / "runs" / "run-1"),
|
|
65
|
+
"--no-enrich",
|
|
66
|
+
"--notify", "lab-ovh",
|
|
67
|
+
])
|
|
68
|
+
assert rc != 4
|
|
69
|
+
assert posted and posted[0][0] == "656"
|
|
70
|
+
assert "dreaming" in posted[0][1] or posted[0][1]
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
"""Card #1286: swarph_dm_unread agrees with the monitor cursor.
|
|
2
|
+
|
|
3
|
+
A DM with read_at NULL that the cursor has already passed is not unread.
|
|
4
|
+
The call is a peek: cursor.json is not rewritten and nothing is marked read.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import datetime
|
|
9
|
+
import json
|
|
10
|
+
import time
|
|
11
|
+
|
|
12
|
+
from swarph_cli.commands import mesh
|
|
13
|
+
from swarph_cli.commands import mcp_server
|
|
14
|
+
from swarph_cli.dm_frame import frame_text
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def test_1286_stale_unread_behind_the_cursor_is_not_new(monkeypatch, tmp_path):
|
|
18
|
+
monkeypatch.setattr(
|
|
19
|
+
mcp_server, "_dm_session", lambda self_name=None: ("lab-ovh", "http://gw", "tok"),
|
|
20
|
+
)
|
|
21
|
+
state = tmp_path / "mesh-sidecar"
|
|
22
|
+
state.mkdir()
|
|
23
|
+
# Path.home() ignores HOME on Windows, so point the sidecar helper itself.
|
|
24
|
+
monkeypatch.setattr(mcp_server._mesh, "_default_sidecar_state_dir", lambda who: state)
|
|
25
|
+
cursor = state / "cursor.json"
|
|
26
|
+
cursor.write_text(json.dumps({"last_msg_id": 200}), encoding="utf-8")
|
|
27
|
+
before = cursor.read_bytes()
|
|
28
|
+
rows = [
|
|
29
|
+
{"id": 100, "from_node": "cursor-lin", "to_node": "lab-ovh", "kind": "status",
|
|
30
|
+
"content": "STALE1286", "read_at": None},
|
|
31
|
+
{"id": 200, "from_node": "cursor-lin", "to_node": "lab-ovh", "kind": "status",
|
|
32
|
+
"content": "BOUNDARY1286", "read_at": None},
|
|
33
|
+
{"id": 300, "from_node": "cursor-lin", "to_node": "lab-ovh", "kind": "status",
|
|
34
|
+
"content": "NEW1286 full body", "read_at": None},
|
|
35
|
+
{"id": 400, "from_node": "lab-ovh", "to_node": "cursor-lin", "kind": "status",
|
|
36
|
+
"content": "SELF1286", "read_at": None},
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
def fake_get(url, token, **kw):
|
|
40
|
+
assert "unread_only=true" in url
|
|
41
|
+
return 200, {"messages": list(reversed(rows)), "n": len(rows)}
|
|
42
|
+
|
|
43
|
+
monkeypatch.setattr(mcp_server._mesh, "_http_get_json", fake_get)
|
|
44
|
+
out = mcp_server._dm_unread()
|
|
45
|
+
# 200 equals the cursor: observed, not yet delivered, so it is returned.
|
|
46
|
+
# 100 is strictly behind and stays hidden.
|
|
47
|
+
assert [m["id"] for m in out["messages"]] == [200, 300]
|
|
48
|
+
assert out["messages"][1]["content"] == frame_text(rows[2])
|
|
49
|
+
assert "NEW1286 full body" in out["messages"][1]["content"]
|
|
50
|
+
assert "BOUNDARY1286" in out["messages"][0]["content"]
|
|
51
|
+
assert "STALE1286" not in json.dumps(out)
|
|
52
|
+
assert "SELF1286" not in json.dumps(out)
|
|
53
|
+
assert out["n"] == 2 and out["not_returned"] == 0
|
|
54
|
+
# 100 is behind the cursor and has no created_at, so it is not within 24 h.
|
|
55
|
+
assert out.get("hidden_stale") == 1
|
|
56
|
+
assert cursor.read_bytes() == before
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def test_1286_monitor_iteration_then_unread_returns_the_woken_dm(monkeypatch, tmp_path):
|
|
60
|
+
"""One real monitor iteration observes a new DM, then unread still returns it.
|
|
61
|
+
|
|
62
|
+
The cursor advances before delivery, so the woken id equals last_msg_id.
|
|
63
|
+
On 74347164 unread uses a strict `>` and the DM is gone.
|
|
64
|
+
"""
|
|
65
|
+
state_dir = tmp_path / "mesh-sidecar"
|
|
66
|
+
state_dir.mkdir()
|
|
67
|
+
(state_dir / "cursor.json").write_text(
|
|
68
|
+
json.dumps({"last_msg_id": 100}), encoding="utf-8")
|
|
69
|
+
monkeypatch.setattr(mcp_server._mesh, "_default_sidecar_state_dir", lambda who: state_dir)
|
|
70
|
+
monkeypatch.setattr(
|
|
71
|
+
mcp_server, "_dm_session", lambda self_name=None: ("lab-ovh", "http://gw", "tok"),
|
|
72
|
+
)
|
|
73
|
+
woken = {
|
|
74
|
+
"id": 501, "from_node": "cursor-lin", "to_node": "lab-ovh", "kind": "question",
|
|
75
|
+
"content": "WOKEN1286 full body", "read_at": None,
|
|
76
|
+
}
|
|
77
|
+
stale = {
|
|
78
|
+
"id": 50, "from_node": "cursor-lin", "to_node": "lab-ovh", "kind": "status",
|
|
79
|
+
"content": "BEHIND1286", "read_at": None,
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
def fake_get(url, token, **kw):
|
|
83
|
+
return 200, {"messages": [stale, woken]}
|
|
84
|
+
|
|
85
|
+
monkeypatch.setattr(mesh, "_http_get_json", fake_get)
|
|
86
|
+
state = mesh.MonitorState(
|
|
87
|
+
self_name="lab-ovh",
|
|
88
|
+
state_dir=state_dir,
|
|
89
|
+
gateway="http://gw",
|
|
90
|
+
token="tok",
|
|
91
|
+
sinks=[mesh.parse_sink("none")],
|
|
92
|
+
)
|
|
93
|
+
mesh._monitor_iteration(state, poll_channels=False)
|
|
94
|
+
out = mcp_server._dm_unread()
|
|
95
|
+
ids = [m["id"] for m in out["messages"]]
|
|
96
|
+
assert ids == [501]
|
|
97
|
+
assert "WOKEN1286 full body" in out["messages"][0]["content"]
|
|
98
|
+
assert "BEHIND1286" not in json.dumps(out)
|
|
99
|
+
assert out.get("hidden_stale") == 1
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def _iso(offset_s: float) -> str:
|
|
103
|
+
stamp = datetime.datetime.fromtimestamp(time.time() + offset_s, datetime.timezone.utc)
|
|
104
|
+
return stamp.isoformat()
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def _row(mid: int, content: str, created_at: str) -> dict:
|
|
108
|
+
return {
|
|
109
|
+
"id": mid, "from_node": "cursor-lin", "to_node": "lab-ovh", "kind": "question",
|
|
110
|
+
"content": content, "read_at": None, "created_at": created_at,
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _monitor(monkeypatch, tmp_path, rows, *, polls: int = 1, windows=None):
|
|
115
|
+
state_dir = tmp_path / "mesh-sidecar"
|
|
116
|
+
state_dir.mkdir()
|
|
117
|
+
(state_dir / "cursor.json").write_text(
|
|
118
|
+
json.dumps({"last_msg_id": 100}), encoding="utf-8")
|
|
119
|
+
monkeypatch.setattr(mcp_server._mesh, "_default_sidecar_state_dir", lambda who: state_dir)
|
|
120
|
+
monkeypatch.setattr(
|
|
121
|
+
mcp_server, "_dm_session", lambda self_name=None: ("lab-ovh", "http://gw", "tok"),
|
|
122
|
+
)
|
|
123
|
+
seen = {"n": 0}
|
|
124
|
+
|
|
125
|
+
def fake_get(url, token, **kw):
|
|
126
|
+
if "unread_only" in url:
|
|
127
|
+
return 200, {"messages": list(rows)}
|
|
128
|
+
if windows is None:
|
|
129
|
+
return 200, {"messages": list(rows)}
|
|
130
|
+
index = min(seen["n"], len(windows) - 1)
|
|
131
|
+
seen["n"] += 1
|
|
132
|
+
wanted = windows[index]
|
|
133
|
+
return 200, {"messages": [row for row in rows if row["id"] in wanted]}
|
|
134
|
+
|
|
135
|
+
monkeypatch.setattr(mesh, "_http_get_json", fake_get)
|
|
136
|
+
state = mesh.MonitorState(
|
|
137
|
+
self_name="lab-ovh",
|
|
138
|
+
state_dir=state_dir,
|
|
139
|
+
gateway="http://gw",
|
|
140
|
+
token="tok",
|
|
141
|
+
sinks=[mesh.parse_sink("none")],
|
|
142
|
+
)
|
|
143
|
+
for _ in range(polls):
|
|
144
|
+
mesh._monitor_iteration(state, poll_channels=False)
|
|
145
|
+
return mcp_server._dm_unread(), state
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def test_1286_one_poll_observing_three_returns_all_three(monkeypatch, tmp_path):
|
|
149
|
+
"""One poll records the cursor at the newest id. The two earlier DMs are young."""
|
|
150
|
+
young = _iso(0)
|
|
151
|
+
rows = [
|
|
152
|
+
_row(501, "ONE1286", young),
|
|
153
|
+
_row(502, "TWO1286", young),
|
|
154
|
+
_row(503, "THREE1286", young),
|
|
155
|
+
]
|
|
156
|
+
out, state = _monitor(monkeypatch, tmp_path, rows)
|
|
157
|
+
assert state.observed["last_msg_id"] == 503
|
|
158
|
+
assert [m["id"] for m in out["messages"]] == [501, 502, 503]
|
|
159
|
+
assert out.get("hidden_stale") == 0
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
def test_1286_three_polls_then_unread_returns_all_three(monkeypatch, tmp_path):
|
|
163
|
+
"""A busy host calls the tool after three polls, one new DM each."""
|
|
164
|
+
young = _iso(-60)
|
|
165
|
+
rows = [
|
|
166
|
+
_row(501, "POLL1286a", young),
|
|
167
|
+
_row(502, "POLL1286b", young),
|
|
168
|
+
_row(503, "POLL1286c", young),
|
|
169
|
+
]
|
|
170
|
+
out, state = _monitor(
|
|
171
|
+
monkeypatch, tmp_path, rows, polls=3, windows=([501], [501, 502], [501, 502, 503]),
|
|
172
|
+
)
|
|
173
|
+
assert state.observed["last_msg_id"] == 503
|
|
174
|
+
assert [m["id"] for m in out["messages"]] == [501, 502, 503]
|
|
175
|
+
assert "POLL1286a" in out["messages"][0]["content"]
|
|
176
|
+
assert out.get("hidden_stale") == 0
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def test_1286_old_dm_behind_the_cursor_is_hidden_and_counted(monkeypatch, tmp_path):
|
|
180
|
+
young = _iso(0)
|
|
181
|
+
old = _iso(-(25 * 60 * 60))
|
|
182
|
+
rows = [
|
|
183
|
+
_row(50, "OLD1286", old),
|
|
184
|
+
_row(80, "YOUNGBEHIND1286", young),
|
|
185
|
+
_row(501, "FRESH1286", young),
|
|
186
|
+
]
|
|
187
|
+
out, state = _monitor(monkeypatch, tmp_path, rows)
|
|
188
|
+
assert state.observed["last_msg_id"] == 501
|
|
189
|
+
assert "OLD1286" not in json.dumps(out)
|
|
190
|
+
assert out.get("hidden_stale") == 1
|
|
191
|
+
assert [m["id"] for m in out["messages"]] == [80, 501]
|
|
192
|
+
assert "YOUNGBEHIND1286" in out["messages"][0]["content"]
|
|
@@ -44,7 +44,10 @@ FIXTURE = [
|
|
|
44
44
|
|
|
45
45
|
|
|
46
46
|
@pytest.fixture(autouse=True)
|
|
47
|
-
def _session(monkeypatch: pytest.MonkeyPatch):
|
|
47
|
+
def _session(monkeypatch: pytest.MonkeyPatch, tmp_path):
|
|
48
|
+
# Card #1286 reads the monitor cursor. These tests are about the unread
|
|
49
|
+
# payload, so they observe from a private home whose cursor is absent.
|
|
50
|
+
monkeypatch.setenv("HOME", str(tmp_path))
|
|
48
51
|
monkeypatch.setattr(mcp_server, "_dm_session", lambda self_name=None: ("lab-ovh", "http://gw", "tok"))
|
|
49
52
|
if MUTATE == "truncate":
|
|
50
53
|
real = mcp_server._lean
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|