swarph-cli 0.69.2__tar.gz → 0.69.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {swarph_cli-0.69.2/src/swarph_cli.egg-info → swarph_cli-0.69.4}/PKG-INFO +4 -1
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/README.md +2 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/pyproject.toml +4 -1
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/__init__.py +1 -1
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/bench/backends.py +33 -9
- swarph_cli-0.69.4/src/swarph_cli/bench/claude_cli.py +68 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/bench/providers.py +4 -1
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/bench/runner.py +84 -1
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/bench.py +13 -2
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/guide/GUIDE.md +1 -1
- {swarph_cli-0.69.2 → swarph_cli-0.69.4/src/swarph_cli.egg-info}/PKG-INFO +4 -1
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli.egg-info/SOURCES.txt +2 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli.egg-info/requires.txt +3 -0
- swarph_cli-0.69.4/tests/test_bench_claude_cli.py +67 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_bench_runner.py +72 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_brain_ask_command.py +4 -1
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_packaged_artifacts_exist.py +4 -1
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_watchdog.py +4 -1
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/LICENSE +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/setup.cfg +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/__main__.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/bench/__init__.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/bench/pack.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/bench/prices.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/bench/quality.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/bench/refresh_prices.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/bench/rules.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/bench/validate.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/caller.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/capture/__init__.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/capture/harden.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/capture/lineage.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/capture/liveness.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/capture/manifest.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/capture/paths.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/capture/verify.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/cell.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/chain_token.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/channel.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/__init__.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/_content.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/_display.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/_gateway_client.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/add.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/board.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/board_merge_check.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/brain.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/brain_ask.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/cell.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/cell_probe.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/channel.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/chat.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/codegraph.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/codegraph_hook.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/codex_hook_output.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/codex_waker.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/compress.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/daemon.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/dreaming.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/event.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/gateway.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/gh_route.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/group.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/guide.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/highlight.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/hook_interpreter.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/hook_output.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/hooks.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/import_session.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/init.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/install_hook.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/install_multiplexer.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/install_wake_hook.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/lane.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/mcp_server.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/memory.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/memory_sync.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/mesh.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/monitor.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/okf_links.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/onboard.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/protocol_handler.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/ratify.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/schedule.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/security.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/service.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/spawn.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/timeline.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/version.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/wake_hook_output.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/commands/watchdog.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/compress/__init__.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/compress/levers.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/compress/marker.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/compress/verify.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/console_safe.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/delivery_queue.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/dm_frame.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/dreaming/__init__.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/dreaming/candidates.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/dreaming/clone.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/dreaming/enrich.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/dreaming/index_check.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/dreaming/notify.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/dreaming/organize.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/dreaming/probes.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/dreaming/report.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/dreaming/run.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/dreaming/slm.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/dreaming/transcripts.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/dreaming/verify.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/gateway/__init__.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/gateway/feature_registry.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/gateway/lanes_control.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/gateway/schema.sql +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/gateway/server.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/gateway/services_control.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/gateway_default.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/gh_identity.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/identity.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/main.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/multiplexer.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/orphan_daemons.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/pane_probe.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/parsers/__init__.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/parsers/claude.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/peer_executor.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/peer_executor_windows.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/peer_reconciliation.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/peer_reply_outbox.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/peer_service_host.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/peer_service_payload.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/peer_service_reply.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/peer_staging.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/query_filters.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/scripts/__init__.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/scripts/wake_watchdog.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/service/__init__.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/service/app.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/service/providers.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/session_bridge.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/source_text.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/stall_alert.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/systemd/codex-waker.default +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/timeline_paths.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli/tokens.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli.egg-info/entry_points.txt +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/src/swarph_cli.egg-info/top_level.txt +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_124_register_merge.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_126_pending_from_complete.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_132_ensure_monitor_state_dir.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_138_memory_navigate.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_139_tool_probe.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_183_card_anchor.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_184_notify_honesty.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_201_win32_bash_refusal.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_243_onboard_per_peer_token.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_307_task4_ask_reply.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_319_refusal_exit_codes.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_323_mistral_backend.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_332_report_organize_findings.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_356_query_filters.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_397_gh_identity_router.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_397_router_preflight.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_402_one_identity_resolver.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_467b_onboard_operator_credential.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_472_register_caller_binding.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_523_guide.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_524_conftest_hermeticity.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_532_accept_flag.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_535_version_capability.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_546_default_gateway.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_547_verbs_from_registry.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_548_gateway_remainder.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_552_codegraph_gateway.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_555_relay_row_shape.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_556_relay_freshness.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_559_edges_truncated.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_564_bootstrap_first_peer.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_579_register_bootstrap_message.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_591_cli_step_graph.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_644_monitor_supervision.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_648_install_unit.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_654_reexec.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_656_owned_slm.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_684_dreaming_verb.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_689_pkg_version_order.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_716_timeline_one_path.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_724_ps1_ascii_or_bom.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_725_stdio_reconfigure.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_729_dm_once.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_729_wake_watchdog.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_734_dreaming_enrich_degrade.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_735_dm_mcp_tools.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_740_edit_project.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_762_cards_edit_priority.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_764_enrich_collapse.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_801_confirm_flow_cli.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_802_stage_history.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_825_codegraph_hook_noise.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_825_install_posttooluse_only.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_825_remaining_cap_shred_bind.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_829_counterfactual_column.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_830_hook_binding_version.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_84_cli_labels.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_866_doctrine_guide.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_872_identity_refuses_not_warns.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_874_source_text.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_937_dreaming_notify.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_938_organize_line_limit.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_941_bench_backends.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_941_harness_342.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_945_same_line_pairing.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_955_prompt_audit.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_957_auth_http.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_959_venv_interpreter.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_960_channel.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_963_plugin_manifest.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_964_channel_cell.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_965_mesh_wait.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_975_channel_hop.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_add_security_gate.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_artifact_add.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_artifact_hash.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_artifact_lib.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_artifact_mcp.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_artifact_skill.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_artifact_tool.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_artifact_uri.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_bench_backends.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_bench_command.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_bench_pack.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_bench_prices.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_bench_quality.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_bench_validate.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_board_card_thread.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_board_command.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_board_merge_check.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_board_obligation_close.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_brain_ask_env_diagnosis.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_brain_ask_identity_explicitness.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_brain_command.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_caller_meta_guard.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_capture_lineage.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_capture_liveness.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_capture_manifest.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_capture_paths.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_capture_security.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_cell_command_dispatch.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_cell_harden.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_cell_loader.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_cell_selfcheck.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_cell_verify.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_chain_token.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_channel_command.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_channel_poll_never_breaks_dms.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_channel_post.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_channel_wake_policy_filter.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_chat_command.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_claude_parser.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_claude_tmux_template.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_codegraph_caller_scope.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_codegraph_command.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_codegraph_hook.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_codegraph_mcp.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_codex_waker.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_compress_caller_convention.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_compress_command.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_compress_levers.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_compress_marker.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_compress_verify.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_console_encoding_e2e.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_content_composition.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_cursor_membrane.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_daemon_command.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_daemon_console_encoding.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_daemon_delivery.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_delivery_queue.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_display_sanitize.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_ensure_monitor_no_peer_default.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_event_emit.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_feature_to_uri.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_gateway_brain.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_gateway_client.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_gateway_command.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_gateway_message_idempotency.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_gh_route_doctor_mesh_name.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_group_command.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_guide_commands_parse.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_guide_selfcheck.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_heartbeat_check.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_highlight_command.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_hook_output.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_hooks_activity_marker.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_hooks_add.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_hooks_bundle.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_hooks_init.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_hooks_legacy_generation_removal.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_hooks_lifecycle.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_hooks_merge.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_hooks_verify.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_hooks_win32_bash_wrapper.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_hooks_win32_command_path.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_import_command.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_inbox_identity_provenance.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_init_command.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_init_provider_menu.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_install_codex_hooks.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_install_hook.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_install_multiplexer_command.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_install_postcompact_hook.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_install_wake_hook.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_lane_command.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_main.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_main_unknown_verb_guard.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_mcp_missing_hint.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_mcp_server.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_memory_command.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_memory_emit_snippet.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_memory_gateway.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_memory_sync.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_memory_sync_membrane.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_mesh_command.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_mesh_inbox_mark_read.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_mesh_inbox_sanitize.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_mesh_originate_thread_623.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_mesh_send_unknown_peer.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_mesh_sidecar.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_meta_edge_identity.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_monitor_channel_poll.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_monitor_cli.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_monitor_daemon_collision.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_monitor_self_name_verification.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_monitor_state_split.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_multiplexer.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_muse_membrane.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_muse_own_cli.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_obligation_close_is_surfaced.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_okf_links.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_onboard_command.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_opencode_membrane.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_opencode_plugin.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_orphan_daemons.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_packaged_skills.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_peer_executor.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_peer_executor_windows.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_peer_reply_drain_command.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_peer_reply_drain_supervisor.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_peer_reply_outbox.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_peer_reply_transport.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_peer_service_e2e.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_peer_service_host.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_peer_service_payload.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_peer_service_reply.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_postcompact_recall.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_process_liveness.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_protocol_handler.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_providers_isolation.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_ratify_command.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_readme_claims_no_gateway_default.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_repl_caller_convention.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_schedule_command.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_schedule_create_contract.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_security.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_service_app.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_service_command.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_service_providers.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_session_bridge_inject.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_session_bridge_probe.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_session_bridge_resolve.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_session_path_role_gate.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_shipped_units_match_the_cli.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_smoke_chat.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_smoke_one_shot.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_smoke_phase_5_5.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_spawn_base_hoist.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_spawn_command.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_spawn_identity_stamp.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_spawn_live_pin.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_spawn_mitosis_lineage.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_spawn_tmux_session.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_spawn_windows_relaunch.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_stall_alert.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_subprocess_decode_encoding.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_timeline_command.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_tmux_wake_runstate_defer.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_tmux_wake_session_reanchor.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_tmux_wake_stale.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_tmux_wake_submit_verify.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_token_file_one_parser.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_token_precedence_explicit_first.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_unified_token_resolver.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_version_command.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_version_consistency.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_vibe_membrane.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_watchdog_dm_wake.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_watchdog_dm_wake_wiring.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_watchdog_emit_health.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_watchdog_install_flags.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_watchdog_model_rung.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_watchdog_stale_peers.py +0 -0
- {swarph_cli-0.69.2 → swarph_cli-0.69.4}/tests/test_windows_waker_installer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: swarph-cli
|
|
3
|
-
Version: 0.69.
|
|
3
|
+
Version: 0.69.4
|
|
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 @@ License-File: LICENSE
|
|
|
26
26
|
Requires-Dist: swarph-mesh<0.10,>=0.5.0
|
|
27
27
|
Requires-Dist: swarph-shared<0.9,>=0.7.0
|
|
28
28
|
Requires-Dist: PyYAML>=6.0
|
|
29
|
+
Requires-Dist: tomli>=1.1; python_version < "3.11"
|
|
29
30
|
Provides-Extra: dev
|
|
30
31
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
31
32
|
Provides-Extra: mcp
|
|
@@ -245,6 +246,8 @@ Task types: `numeric` (rel-err), `categorical` (exact), `ranking` (Kendall-tau),
|
|
|
245
246
|
|
|
246
247
|
`provider:<name>` reads a TOML registry (`--providers`, else `$SWARPH_BENCH_PROVIDERS`, else `~/.config/swarph/bench_providers.toml`). The file names an env var; it does not hold the key. A worked example is `docs/examples/bench_providers.toml` (Jev, `env = "JEV_API_KEY"`, 0.42 USD per 1M input tokens, output free). `--max-usd` stops the run at the cap. A pack with `egress = "on_box_only"` refuses an external arm unless `--allow-egress <provider>` names it.
|
|
247
248
|
|
|
249
|
+
`claude-sonnet-5-5:subscription` runs `claude -p` on the Claude subscription when `claude` is on PATH. The argv is fixed: `--max-turns 1`, `--output-format stream-json`, `--strict-mcp-config`, an empty MCP config, `--tools ""`, `--setting-sources ""`. The prompt goes on stdin. The child environment drops `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, and every `SWARPH_CHANNEL*` name. A result whose `apiKeySource` is not `none` is `billing:<source>`, is not scored, and aborts the rest of the run on every arm. A usage limit is retried after 60s and then 300s; the arm then stops with `--ledger`, and the next run skips tasks that already completed. It is not scored as an answer.
|
|
250
|
+
|
|
248
251
|
### `swarph brain-ask` (v0.14.0)
|
|
249
252
|
|
|
250
253
|
Search the **swarph-brain** (gbrain) — the swarm's sovereign $0 semantic-retrieval memory — over MCP. The "does the swarm already know X?" reflex, as a one-shot.
|
|
@@ -197,6 +197,8 @@ Task types: `numeric` (rel-err), `categorical` (exact), `ranking` (Kendall-tau),
|
|
|
197
197
|
|
|
198
198
|
`provider:<name>` reads a TOML registry (`--providers`, else `$SWARPH_BENCH_PROVIDERS`, else `~/.config/swarph/bench_providers.toml`). The file names an env var; it does not hold the key. A worked example is `docs/examples/bench_providers.toml` (Jev, `env = "JEV_API_KEY"`, 0.42 USD per 1M input tokens, output free). `--max-usd` stops the run at the cap. A pack with `egress = "on_box_only"` refuses an external arm unless `--allow-egress <provider>` names it.
|
|
199
199
|
|
|
200
|
+
`claude-sonnet-5-5:subscription` runs `claude -p` on the Claude subscription when `claude` is on PATH. The argv is fixed: `--max-turns 1`, `--output-format stream-json`, `--strict-mcp-config`, an empty MCP config, `--tools ""`, `--setting-sources ""`. The prompt goes on stdin. The child environment drops `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, and every `SWARPH_CHANNEL*` name. A result whose `apiKeySource` is not `none` is `billing:<source>`, is not scored, and aborts the rest of the run on every arm. A usage limit is retried after 60s and then 300s; the arm then stops with `--ledger`, and the next run skips tasks that already completed. It is not scored as an answer.
|
|
201
|
+
|
|
200
202
|
### `swarph brain-ask` (v0.14.0)
|
|
201
203
|
|
|
202
204
|
Search the **swarph-brain** (gbrain) — the swarm's sovereign $0 semantic-retrieval memory — over MCP. The "does the swarm already know X?" reflex, as a one-shot.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "swarph-cli"
|
|
7
|
-
version = "0.69.
|
|
7
|
+
version = "0.69.4"
|
|
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" }
|
|
@@ -73,6 +73,9 @@ dependencies = [
|
|
|
73
73
|
# Stays a swarph-cli dep since file I/O is operator-tooling-layer
|
|
74
74
|
# concern; swarph-shared cell module is pure-stdlib.
|
|
75
75
|
"PyYAML>=6.0",
|
|
76
|
+
# tomllib is stdlib from 3.11. bench/providers.py (and the tests that read
|
|
77
|
+
# pyproject.toml) fall back to tomli on the declared 3.10 floor. card #1000.
|
|
78
|
+
"tomli>=1.1; python_version < \"3.11\"",
|
|
76
79
|
]
|
|
77
80
|
|
|
78
81
|
[project.urls]
|
|
@@ -14,11 +14,11 @@ Ships these backends:
|
|
|
14
14
|
passes ``vertexai=False`` EXPLICITLY — a ``GOOGLE_GENAI_USE_VERTEXAI=true``
|
|
15
15
|
env 401s API-key auth (this exact bug bit the reference lab; see
|
|
16
16
|
``model_showdown.py::run_metered``).
|
|
17
|
-
- :class:`SubscriptionBackend` — a
|
|
18
|
-
ESTIMATED (``~len(text)/4
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
- :class:`SubscriptionBackend` — a stub until a ``call_fn`` is wired. A
|
|
18
|
+
string return is still ESTIMATED (``~len(text)/4``). A
|
|
19
|
+
:class:`~swarph_cli.bench.claude_cli.CliResult` uses the CLI's reported
|
|
20
|
+
usage (``estimated=False``). ``swarph bench`` wires ``claude -p`` when
|
|
21
|
+
the ``claude`` binary is on PATH.
|
|
22
22
|
- :class:`RuleBackend` — in-process ``rule:<module:callable>``. No credentials.
|
|
23
23
|
- :class:`TypedHttpBackend` — POST to a Jev-compatible ``/v1/systemone``
|
|
24
24
|
(laya-serve). No credentials; per-item latency is the HTTP round trip.
|
|
@@ -229,6 +229,15 @@ class MeteredMistralBackend:
|
|
|
229
229
|
)
|
|
230
230
|
|
|
231
231
|
|
|
232
|
+
def normalize_judge_action(text: str) -> str:
|
|
233
|
+
"""BUY or SKIP, after trimming and uppercasing. Anything else is ABSTAIN."""
|
|
234
|
+
try:
|
|
235
|
+
action = str(json.loads(text).get("action", "")).strip().upper()
|
|
236
|
+
except Exception:
|
|
237
|
+
return "ABSTAIN"
|
|
238
|
+
return action if action in ("BUY", "SKIP") else "ABSTAIN"
|
|
239
|
+
|
|
240
|
+
|
|
232
241
|
class SubscriptionBackend:
|
|
233
242
|
KIND = "semantic"
|
|
234
243
|
"""STUB/interface-only backend for a $0 subscription path (e.g. a node's
|
|
@@ -271,20 +280,35 @@ class SubscriptionBackend:
|
|
|
271
280
|
)
|
|
272
281
|
t0 = time.time()
|
|
273
282
|
try:
|
|
274
|
-
|
|
283
|
+
raw = self._call_fn(model_id, prompt, system)
|
|
275
284
|
except Exception as exc:
|
|
276
285
|
return BackendResult(
|
|
277
286
|
text="", tokens_in=0, tokens_thought=0, tokens_out=0,
|
|
278
287
|
latency_s=round(time.time() - t0, 2), estimated=True, error=str(exc),
|
|
279
288
|
)
|
|
280
289
|
latency_s = round(time.time() - t0, 2)
|
|
290
|
+
if raw is None or isinstance(raw, str):
|
|
291
|
+
text = raw or ""
|
|
292
|
+
return BackendResult(
|
|
293
|
+
text=text,
|
|
294
|
+
tokens_in=estimate_tokens(prompt),
|
|
295
|
+
tokens_thought=0,
|
|
296
|
+
tokens_out=estimate_tokens(text),
|
|
297
|
+
latency_s=latency_s,
|
|
298
|
+
estimated=True,
|
|
299
|
+
)
|
|
300
|
+
# CliResult from claude_cli: real usage, and the billing/rate-limit error
|
|
301
|
+
# is the result, not an exception. An error discards the answer text.
|
|
302
|
+
err = getattr(raw, "error", None)
|
|
303
|
+
text = "" if err else (getattr(raw, "text", "") or "")
|
|
281
304
|
return BackendResult(
|
|
282
305
|
text=text,
|
|
283
|
-
tokens_in=
|
|
306
|
+
tokens_in=int(getattr(raw, "tokens_in", 0) or 0),
|
|
284
307
|
tokens_thought=0,
|
|
285
|
-
tokens_out=
|
|
308
|
+
tokens_out=int(getattr(raw, "tokens_out", 0) or 0),
|
|
286
309
|
latency_s=latency_s,
|
|
287
|
-
estimated=
|
|
310
|
+
estimated=False,
|
|
311
|
+
error=err,
|
|
288
312
|
)
|
|
289
313
|
|
|
290
314
|
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""claude -p transport for the bench `subscription` backend (#999). Subscription-only,
|
|
2
|
+
isolated, billing-guarded. Measured 2026-09-29: without the isolation flags one call loaded
|
|
3
|
+
286 tools and 213k cache-write tokens; with them, 0 tools and ~421 input tokens."""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import json
|
|
7
|
+
import subprocess
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from typing import Optional
|
|
10
|
+
|
|
11
|
+
_STRIP_EXACT = ("ANTHROPIC_API_KEY", "ANTHROPIC_AUTH_TOKEN")
|
|
12
|
+
_RATE = ("usage limit", "rate limit", "429", "overloaded")
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class CliResult:
|
|
17
|
+
text: str
|
|
18
|
+
tokens_in: int
|
|
19
|
+
tokens_out: int
|
|
20
|
+
api_key_source: Optional[str]
|
|
21
|
+
cost_equiv: Optional[float]
|
|
22
|
+
error: Optional[str] = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def build_argv(model_id: str, system: str) -> list:
|
|
26
|
+
return ["claude", "-p", "--model", model_id, "--system-prompt", system, "--max-turns", "1",
|
|
27
|
+
"--output-format", "stream-json", "--verbose", "--strict-mcp-config",
|
|
28
|
+
"--mcp-config", '{"mcpServers":{}}', "--tools", "", "--setting-sources", ""]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def clean_env(env: dict) -> dict:
|
|
32
|
+
"""Drop billing keys and every SWARPH_CHANNEL* name, including ones not listed yet."""
|
|
33
|
+
return {
|
|
34
|
+
k: v for k, v in env.items()
|
|
35
|
+
if k not in _STRIP_EXACT and not k.startswith("SWARPH_CHANNEL")
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def parse_stream(lines) -> CliResult:
|
|
40
|
+
key, res = None, None
|
|
41
|
+
for ln in lines:
|
|
42
|
+
try:
|
|
43
|
+
e = json.loads(ln)
|
|
44
|
+
except Exception:
|
|
45
|
+
continue
|
|
46
|
+
if e.get("type") == "system" and e.get("subtype") == "init":
|
|
47
|
+
key = e.get("apiKeySource")
|
|
48
|
+
elif e.get("type") == "result":
|
|
49
|
+
res = e
|
|
50
|
+
if res is None:
|
|
51
|
+
return CliResult("", 0, 0, key, None, error="no_result")
|
|
52
|
+
u = res.get("usage") or {}
|
|
53
|
+
tin = sum(int(u.get(k) or 0) for k in ("input_tokens", "cache_creation_input_tokens", "cache_read_input_tokens"))
|
|
54
|
+
out = CliResult(res.get("result") or "", tin, int(u.get("output_tokens") or 0), key, res.get("total_cost_usd"))
|
|
55
|
+
if key != "none":
|
|
56
|
+
return CliResult("", tin, out.tokens_out, key, out.cost_equiv, error=f"billing:{key}")
|
|
57
|
+
if res.get("is_error"):
|
|
58
|
+
msg = (res.get("result") or "").lower()
|
|
59
|
+
out.text = ""
|
|
60
|
+
out.error = "rate_limit" if any(t in msg for t in _RATE) else f"cli_error:{res.get('subtype')}"
|
|
61
|
+
return out
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def call(model_id: str, prompt: str, system: str, *, runner=subprocess.run, timeout: int = 300) -> CliResult:
|
|
65
|
+
import os
|
|
66
|
+
p = runner(build_argv(model_id, system), input=prompt, capture_output=True, text=True,
|
|
67
|
+
env=clean_env(dict(os.environ)), timeout=timeout)
|
|
68
|
+
return parse_stream((p.stdout or "").splitlines())
|
|
@@ -12,6 +12,7 @@ alongside the aggregate, and the CLI always PRINTS it.
|
|
|
12
12
|
from __future__ import annotations
|
|
13
13
|
|
|
14
14
|
import json
|
|
15
|
+
import time
|
|
15
16
|
from dataclasses import dataclass, field
|
|
16
17
|
from datetime import datetime, timezone
|
|
17
18
|
from typing import Optional
|
|
@@ -95,6 +96,7 @@ class TaskRow:
|
|
|
95
96
|
estimated: bool
|
|
96
97
|
error: Optional[str] = None
|
|
97
98
|
not_run: Optional[str] = None
|
|
99
|
+
text: str = ""
|
|
98
100
|
|
|
99
101
|
|
|
100
102
|
@dataclass
|
|
@@ -225,6 +227,33 @@ def _secrets_of(specs, backends) -> list:
|
|
|
225
227
|
return found
|
|
226
228
|
|
|
227
229
|
|
|
230
|
+
def _taskrow_from_ledger(item: dict) -> TaskRow:
|
|
231
|
+
return TaskRow(
|
|
232
|
+
task_id=item["task_id"],
|
|
233
|
+
cls=item.get("cls") or "",
|
|
234
|
+
distance=float(item.get("distance") or 0.0),
|
|
235
|
+
parse_ok=bool(item.get("parse_ok")),
|
|
236
|
+
tokens_in=int(item.get("tokens_in") or 0),
|
|
237
|
+
tokens_thought=int(item.get("tokens_thought") or 0),
|
|
238
|
+
tokens_out=int(item.get("tokens_out") or 0),
|
|
239
|
+
latency_s=float(item.get("latency_s") or 0.0),
|
|
240
|
+
cost_usd=float(item.get("cost_usd") or 0.0),
|
|
241
|
+
estimated=bool(item.get("estimated")),
|
|
242
|
+
error=item.get("error"),
|
|
243
|
+
not_run=item.get("not_run"),
|
|
244
|
+
text=item.get("text") or "",
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def _skipped(task: dict, reason: str) -> TaskRow:
|
|
249
|
+
"""A task that was not scored. rate_limit is retryable, never an ABSTAIN."""
|
|
250
|
+
return TaskRow(
|
|
251
|
+
task_id=task["id"], cls=class_of(task), distance=0.0, parse_ok=False,
|
|
252
|
+
tokens_in=0, tokens_thought=0, tokens_out=0, latency_s=0.0,
|
|
253
|
+
cost_usd=0.0, estimated=False, error=reason, not_run=reason, text="",
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
|
|
228
257
|
def run_pack(
|
|
229
258
|
specs: list[ModelSpec],
|
|
230
259
|
pack: dict,
|
|
@@ -235,6 +264,7 @@ def run_pack(
|
|
|
235
264
|
max_usd_per_arm: Optional[float] = None,
|
|
236
265
|
allow_egress: Optional[set[str]] = None,
|
|
237
266
|
ledger_path: Optional[str] = None,
|
|
267
|
+
sleeper=None,
|
|
238
268
|
) -> dict:
|
|
239
269
|
"""Run every spec against every task in ``pack`` (or a ``task_ids``
|
|
240
270
|
subset). ``backends`` maps backend-name ("metered"/"subscription"/...) ->
|
|
@@ -260,6 +290,17 @@ def run_pack(
|
|
|
260
290
|
secrets = _secrets_of(specs, backends)
|
|
261
291
|
allow_egress = allow_egress or set()
|
|
262
292
|
overrides = []
|
|
293
|
+
sleep = sleeper or time.sleep
|
|
294
|
+
run_aborted = False
|
|
295
|
+
prior_detail: dict = {}
|
|
296
|
+
if ledger_path:
|
|
297
|
+
from pathlib import Path
|
|
298
|
+
ledger = Path(ledger_path)
|
|
299
|
+
if ledger.is_file():
|
|
300
|
+
try:
|
|
301
|
+
prior_detail = json.loads(ledger.read_text(encoding="utf-8")).get("detail") or {}
|
|
302
|
+
except (OSError, json.JSONDecodeError):
|
|
303
|
+
prior_detail = {}
|
|
263
304
|
|
|
264
305
|
for spec in specs:
|
|
265
306
|
backend = _bound(spec, backends)
|
|
@@ -272,9 +313,24 @@ def run_pack(
|
|
|
272
313
|
"at": datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ"),
|
|
273
314
|
})
|
|
274
315
|
|
|
316
|
+
resumed = {}
|
|
317
|
+
for item in prior_detail.get(spec.label) or []:
|
|
318
|
+
if isinstance(item, dict) and item.get("task_id") and item.get("error") is None and not item.get("not_run"):
|
|
319
|
+
resumed[item["task_id"]] = _taskrow_from_ledger(item)
|
|
320
|
+
|
|
275
321
|
rows: list[TaskRow] = []
|
|
276
322
|
arm_spent = 0.0
|
|
323
|
+
arm_stopped = False
|
|
277
324
|
for task in tasks:
|
|
325
|
+
if task["id"] in resumed:
|
|
326
|
+
rows.append(resumed[task["id"]])
|
|
327
|
+
continue
|
|
328
|
+
if run_aborted:
|
|
329
|
+
rows.append(_skipped(task, "aborted"))
|
|
330
|
+
continue
|
|
331
|
+
if arm_stopped:
|
|
332
|
+
rows.append(_skipped(task, "rate_limit"))
|
|
333
|
+
continue
|
|
278
334
|
if (max_usd is not None and spent >= max_usd) or (
|
|
279
335
|
max_usd_per_arm is not None and arm_spent >= max_usd_per_arm):
|
|
280
336
|
partial = "spend_cap"
|
|
@@ -286,8 +342,31 @@ def run_pack(
|
|
|
286
342
|
))
|
|
287
343
|
continue
|
|
288
344
|
result = _dispatch(backend, spec.id, task["prompt"], system)
|
|
345
|
+
rate_hits = 0
|
|
346
|
+
while result.error == "rate_limit" and rate_hits < 2:
|
|
347
|
+
sleep(60 if rate_hits == 0 else 300)
|
|
348
|
+
rate_hits += 1
|
|
349
|
+
result = _dispatch(backend, spec.id, task["prompt"], system)
|
|
289
350
|
if result.error:
|
|
290
351
|
result.error = _redact(result.error, secrets)
|
|
352
|
+
if result.error and str(result.error).startswith("billing:"):
|
|
353
|
+
# A billing leak aborts every remaining task on every arm.
|
|
354
|
+
# The answer is already empty. One such call stops the run.
|
|
355
|
+
partial = "aborted"
|
|
356
|
+
run_aborted = True
|
|
357
|
+
rows.append(TaskRow(
|
|
358
|
+
task_id=task["id"], cls=class_of(task), distance=0.0, parse_ok=False,
|
|
359
|
+
tokens_in=result.tokens_in, tokens_thought=result.tokens_thought,
|
|
360
|
+
tokens_out=result.tokens_out, latency_s=result.latency_s,
|
|
361
|
+
cost_usd=0.0, estimated=result.estimated, error=result.error,
|
|
362
|
+
not_run="aborted", text="",
|
|
363
|
+
))
|
|
364
|
+
continue
|
|
365
|
+
if result.error == "rate_limit":
|
|
366
|
+
partial = "rate_limit"
|
|
367
|
+
arm_stopped = True
|
|
368
|
+
rows.append(_skipped(task, "rate_limit"))
|
|
369
|
+
continue
|
|
291
370
|
if result.error:
|
|
292
371
|
rows.append(TaskRow(
|
|
293
372
|
task_id=task["id"], cls=class_of(task), distance=1.0, parse_ok=False,
|
|
@@ -308,6 +387,7 @@ def run_pack(
|
|
|
308
387
|
latency_s=result.latency_s,
|
|
309
388
|
cost_usd=usd,
|
|
310
389
|
estimated=result.estimated or unknown,
|
|
390
|
+
text=result.text,
|
|
311
391
|
))
|
|
312
392
|
detail[spec.label] = rows
|
|
313
393
|
|
|
@@ -340,7 +420,9 @@ def run_pack(
|
|
|
340
420
|
estimated=any(r.estimated for r in done),
|
|
341
421
|
per_class=per_class,
|
|
342
422
|
errors=sum(1 for r in rows if r.error and not r.not_run),
|
|
343
|
-
partial="
|
|
423
|
+
partial=("aborted" if any(r.not_run == "aborted" for r in rows)
|
|
424
|
+
else "rate_limit" if any(r.not_run == "rate_limit" for r in rows)
|
|
425
|
+
else "spend_cap" if any(r.not_run for r in rows) else None),
|
|
344
426
|
price_source=(getattr(backend, "price", None) or {}).get("source", "")
|
|
345
427
|
if isinstance(getattr(backend, "price", None), dict) else "",
|
|
346
428
|
egress=_arm_egress(spec, backend),
|
|
@@ -354,6 +436,7 @@ def run_pack(
|
|
|
354
436
|
"theme": pack.get("theme"),
|
|
355
437
|
"tasks_total": len(tasks),
|
|
356
438
|
"partial": partial,
|
|
439
|
+
"aborted": partial == "aborted",
|
|
357
440
|
"exit_code": 1 if partial else 0,
|
|
358
441
|
"egress_overrides": overrides,
|
|
359
442
|
"board": [vars(b) for b in board],
|
|
@@ -17,6 +17,7 @@ from __future__ import annotations
|
|
|
17
17
|
|
|
18
18
|
import argparse
|
|
19
19
|
import json
|
|
20
|
+
import shutil
|
|
20
21
|
import sys
|
|
21
22
|
from pathlib import Path
|
|
22
23
|
from typing import Optional
|
|
@@ -53,10 +54,16 @@ def _default_backends() -> dict[str, Backend]:
|
|
|
53
54
|
`metered` stays bound to Gemini so existing `id:metered` specs keep their
|
|
54
55
|
meaning; mistral is its own key rather than a redefinition.
|
|
55
56
|
"""
|
|
57
|
+
# claude on PATH wires the subscription lane to an isolated `claude -p`
|
|
58
|
+
# (card #999). Without the binary the stub stays, and preflight skips it.
|
|
59
|
+
subscription = SubscriptionBackend()
|
|
60
|
+
if shutil.which("claude"):
|
|
61
|
+
from swarph_cli.bench.claude_cli import call as claude_call
|
|
62
|
+
subscription = SubscriptionBackend(call_fn=claude_call)
|
|
56
63
|
return {
|
|
57
64
|
"metered": MeteredGeminiBackend(),
|
|
58
65
|
"mistral": MeteredMistralBackend(),
|
|
59
|
-
"subscription":
|
|
66
|
+
"subscription": subscription,
|
|
60
67
|
"rule": RuleBackend(),
|
|
61
68
|
"typed-http": TypedHttpBackend(),
|
|
62
69
|
# Selectable class for the wiring lock. A real call is provider:<name>,
|
|
@@ -138,6 +145,9 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
138
145
|
r.add_argument("--max-usd-per-arm", type=float, default=None)
|
|
139
146
|
r.add_argument("--allow-egress", action="append", default=[], help="provider allowed off-box")
|
|
140
147
|
r.add_argument("--allow-unpriced", action="append", default=[], help="provider allowed with no price under a cap")
|
|
148
|
+
r.add_argument("--ledger", default=None,
|
|
149
|
+
help="JSON path. A re-run skips tasks that already completed; "
|
|
150
|
+
"a usage limit stops the arm here and the next run resumes")
|
|
141
151
|
r.add_argument("--strict", action="store_true",
|
|
142
152
|
help="abort (no dispatch) if ANY requested model lacks credentials, "
|
|
143
153
|
"instead of skipping it and running the rest")
|
|
@@ -223,7 +233,8 @@ def _cmd_run(args) -> int:
|
|
|
223
233
|
|
|
224
234
|
result = run_pack(
|
|
225
235
|
runnable, pack, backends, max_usd=args.max_usd,
|
|
226
|
-
max_usd_per_arm=args.max_usd_per_arm, allow_egress=allow_egress
|
|
236
|
+
max_usd_per_arm=args.max_usd_per_arm, allow_egress=allow_egress,
|
|
237
|
+
ledger_path=args.ledger)
|
|
227
238
|
if args.report == "json":
|
|
228
239
|
print(json.dumps(result, indent=2))
|
|
229
240
|
else:
|
|
@@ -488,7 +488,7 @@ registry name the SAME set, so a verb shipped without a line here is a red test.
|
|
|
488
488
|
answers `--help`; hook-plumbing verbs are the callbacks hooks invoke, not commands you type.
|
|
489
489
|
|
|
490
490
|
- `swarph add` -- install a swarph artifact by `swarph://` URI (magnet-link style).
|
|
491
|
-
- `swarph bench` -- deterministic LLM benchmark-pack runner (card #101). `provider:<name>` uses a TOML registry of env-var names (example `docs/examples/bench_providers.toml`, Jev at 0.42 USD per 1M input tokens, output free). `--max-usd` is a hard cap. `egress = "on_box_only"` refuses an external arm unless `--allow-egress` names that provider.
|
|
491
|
+
- `swarph bench` -- deterministic LLM benchmark-pack runner (card #101). `provider:<name>` uses a TOML registry of env-var names (example `docs/examples/bench_providers.toml`, Jev at 0.42 USD per 1M input tokens, output free). `--max-usd` is a hard cap. `egress = "on_box_only"` refuses an external arm unless `--allow-egress` names that provider. `claude-sonnet-5-5:subscription` is `claude -p` on the Claude subscription (isolation flags, billing guard requires `apiKeySource=none` and aborts every arm, prompt on stdin, every `SWARPH_CHANNEL*` name stripped). A usage limit retries 60s then 300s and resumes from `--ledger`; it is not scored.
|
|
492
492
|
- `swarph board` -- the mesh board: projects, cards, obligations (see [The board](#the-board)).
|
|
493
493
|
- `swarph brain` -- run the gbrain HTTP brain server, the $0 semantic memory.
|
|
494
494
|
- `swarph brain-ask` -- search the swarph-brain memory with a question; optional $0 synthesis.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: swarph-cli
|
|
3
|
-
Version: 0.69.
|
|
3
|
+
Version: 0.69.4
|
|
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 @@ License-File: LICENSE
|
|
|
26
26
|
Requires-Dist: swarph-mesh<0.10,>=0.5.0
|
|
27
27
|
Requires-Dist: swarph-shared<0.9,>=0.7.0
|
|
28
28
|
Requires-Dist: PyYAML>=6.0
|
|
29
|
+
Requires-Dist: tomli>=1.1; python_version < "3.11"
|
|
29
30
|
Provides-Extra: dev
|
|
30
31
|
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
31
32
|
Provides-Extra: mcp
|
|
@@ -245,6 +246,8 @@ Task types: `numeric` (rel-err), `categorical` (exact), `ranking` (Kendall-tau),
|
|
|
245
246
|
|
|
246
247
|
`provider:<name>` reads a TOML registry (`--providers`, else `$SWARPH_BENCH_PROVIDERS`, else `~/.config/swarph/bench_providers.toml`). The file names an env var; it does not hold the key. A worked example is `docs/examples/bench_providers.toml` (Jev, `env = "JEV_API_KEY"`, 0.42 USD per 1M input tokens, output free). `--max-usd` stops the run at the cap. A pack with `egress = "on_box_only"` refuses an external arm unless `--allow-egress <provider>` names it.
|
|
247
248
|
|
|
249
|
+
`claude-sonnet-5-5:subscription` runs `claude -p` on the Claude subscription when `claude` is on PATH. The argv is fixed: `--max-turns 1`, `--output-format stream-json`, `--strict-mcp-config`, an empty MCP config, `--tools ""`, `--setting-sources ""`. The prompt goes on stdin. The child environment drops `ANTHROPIC_API_KEY`, `ANTHROPIC_AUTH_TOKEN`, and every `SWARPH_CHANNEL*` name. A result whose `apiKeySource` is not `none` is `billing:<source>`, is not scored, and aborts the rest of the run on every arm. A usage limit is retried after 60s and then 300s; the arm then stops with `--ledger`, and the next run skips tasks that already completed. It is not scored as an answer.
|
|
250
|
+
|
|
248
251
|
### `swarph brain-ask` (v0.14.0)
|
|
249
252
|
|
|
250
253
|
Search the **swarph-brain** (gbrain) — the swarm's sovereign $0 semantic-retrieval memory — over MCP. The "does the swarm already know X?" reflex, as a one-shot.
|
|
@@ -39,6 +39,7 @@ src/swarph_cli.egg-info/requires.txt
|
|
|
39
39
|
src/swarph_cli.egg-info/top_level.txt
|
|
40
40
|
src/swarph_cli/bench/__init__.py
|
|
41
41
|
src/swarph_cli/bench/backends.py
|
|
42
|
+
src/swarph_cli/bench/claude_cli.py
|
|
42
43
|
src/swarph_cli/bench/pack.py
|
|
43
44
|
src/swarph_cli/bench/prices.py
|
|
44
45
|
src/swarph_cli/bench/providers.py
|
|
@@ -262,6 +263,7 @@ tests/test_artifact_skill.py
|
|
|
262
263
|
tests/test_artifact_tool.py
|
|
263
264
|
tests/test_artifact_uri.py
|
|
264
265
|
tests/test_bench_backends.py
|
|
266
|
+
tests/test_bench_claude_cli.py
|
|
265
267
|
tests/test_bench_command.py
|
|
266
268
|
tests/test_bench_pack.py
|
|
267
269
|
tests/test_bench_prices.py
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"""Subscription bench transport: isolated claude -p, billing guard, no live call."""
|
|
2
|
+
import json
|
|
3
|
+
import types
|
|
4
|
+
|
|
5
|
+
from swarph_cli.bench import backends as B
|
|
6
|
+
from swarph_cli.bench import claude_cli as C
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def lines(key="none", result='{"action": "BUY"}', is_error=False, subtype="success", usage=None):
|
|
10
|
+
u = usage or {"input_tokens": 400, "cache_creation_input_tokens": 0, "cache_read_input_tokens": 21, "output_tokens": 12}
|
|
11
|
+
return [json.dumps({"type": "system", "subtype": "init", "apiKeySource": key, "tools": []}),
|
|
12
|
+
json.dumps({"type": "result", "subtype": subtype, "is_error": is_error, "result": result,
|
|
13
|
+
"total_cost_usd": 0.001, "usage": u})]
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def test_argv_isolation_flags():
|
|
17
|
+
a = C.build_argv("claude-sonnet-5-5", "SYS")
|
|
18
|
+
for flag in ("--strict-mcp-config", "--setting-sources", "--tools", "--max-turns", "--system-prompt"):
|
|
19
|
+
assert flag in a
|
|
20
|
+
assert a[a.index("--mcp-config") + 1] == '{"mcpServers":{}}'
|
|
21
|
+
assert a[a.index("--tools") + 1] == "" and a[a.index("--setting-sources") + 1] == ""
|
|
22
|
+
assert "SYS" in a and not any("prompt text" in x for x in a) # prompt goes to stdin, never argv
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def test_clean_env_strips_keys():
|
|
26
|
+
e = C.clean_env({"ANTHROPIC_API_KEY": "k", "ANTHROPIC_AUTH_TOKEN": "t", "SWARPH_CHANNEL": "dev",
|
|
27
|
+
"SWARPH_CHANNEL_CELL": "droplet", "PATH": "/usr/bin"})
|
|
28
|
+
assert set(e) == {"PATH"}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def test_clean_env_strips_unseen_swarph_channel_prefix():
|
|
32
|
+
e = C.clean_env({"SWARPH_CHANNEL_X": "secret", "PATH": "/usr/bin", "HOME": "/root"})
|
|
33
|
+
assert "SWARPH_CHANNEL_X" not in e
|
|
34
|
+
assert e == {"PATH": "/usr/bin", "HOME": "/root"}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_parse_ok():
|
|
38
|
+
r = C.parse_stream(lines())
|
|
39
|
+
assert r.text == '{"action": "BUY"}' and r.tokens_in == 421 and r.tokens_out == 12 and r.error is None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def test_billing_guard_rejects_api_key_source():
|
|
43
|
+
r = C.parse_stream(lines(key="ANTHROPIC_API_KEY"))
|
|
44
|
+
assert r.error == "billing:ANTHROPIC_API_KEY" and r.text == ""
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_rate_limit_is_retryable_not_abstain():
|
|
48
|
+
r = C.parse_stream(lines(result="Claude usage limit reached", is_error=True, subtype="error_during_execution"))
|
|
49
|
+
assert r.error == "rate_limit"
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def test_call_passes_prompt_on_stdin():
|
|
53
|
+
seen = {}
|
|
54
|
+
|
|
55
|
+
def runner(argv, input, capture_output, text, env, timeout):
|
|
56
|
+
seen.update(argv=argv, input=input, env=env)
|
|
57
|
+
return types.SimpleNamespace(returncode=0, stdout="\n".join(lines()), stderr="")
|
|
58
|
+
|
|
59
|
+
C.call("claude-sonnet-5-5", "PROMPT TEXT", "SYS", runner=runner)
|
|
60
|
+
assert seen["input"] == "PROMPT TEXT" and "PROMPT TEXT" not in seen["argv"]
|
|
61
|
+
assert "ANTHROPIC_API_KEY" not in seen["env"]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def test_parse_action_normalization():
|
|
65
|
+
assert B.normalize_judge_action('{"action": " buy "}') == "BUY"
|
|
66
|
+
assert B.normalize_judge_action('{"action": "hold"}') == "ABSTAIN"
|
|
67
|
+
assert B.normalize_judge_action("not json") == "ABSTAIN"
|
|
@@ -241,3 +241,75 @@ def test_preflight_skips_and_warns_on_missing_creds():
|
|
|
241
241
|
runnable, warnings = preflight([ModelSpec(id="a", label="model-a")], {"metered": NoCredsBackend()})
|
|
242
242
|
assert runnable == []
|
|
243
243
|
assert warnings and "model-a" in warnings[0] and "SOME_API_KEY" in warnings[0]
|
|
244
|
+
|
|
245
|
+
|
|
246
|
+
def _two_tasks():
|
|
247
|
+
return {
|
|
248
|
+
"theme": "rate-limit",
|
|
249
|
+
"system": "s",
|
|
250
|
+
"tasks": [
|
|
251
|
+
{"id": "t1", "type": "categorical", "prompt": "one", "expected": "BUY"},
|
|
252
|
+
{"id": "t2", "type": "categorical", "prompt": "two", "expected": "BUY"},
|
|
253
|
+
],
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def test_rate_limit_backs_off_then_stops_without_scoring():
|
|
258
|
+
class Always:
|
|
259
|
+
def __init__(self):
|
|
260
|
+
self.n = 0
|
|
261
|
+
|
|
262
|
+
def generate(self, model_id, prompt, system=""):
|
|
263
|
+
self.n += 1
|
|
264
|
+
return BackendResult("", 0, 0, 0, 0.0, False, error="rate_limit")
|
|
265
|
+
|
|
266
|
+
def missing_creds(self):
|
|
267
|
+
return []
|
|
268
|
+
|
|
269
|
+
waits = []
|
|
270
|
+
backend = Always()
|
|
271
|
+
result = run_pack(
|
|
272
|
+
[ModelSpec(id="m", label="m")], _two_tasks(), {"metered": backend}, sleeper=waits.append)
|
|
273
|
+
assert waits == [60, 300]
|
|
274
|
+
assert backend.n == 3
|
|
275
|
+
rows = result["detail"]["m"]
|
|
276
|
+
assert [r["not_run"] for r in rows] == ["rate_limit", "rate_limit"]
|
|
277
|
+
assert result["partial"] == "rate_limit"
|
|
278
|
+
assert all(r["text"] == "" for r in rows)
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def test_billing_rejection_aborts_every_arm_after_one_call():
|
|
282
|
+
class Billing:
|
|
283
|
+
def __init__(self):
|
|
284
|
+
self.n = 0
|
|
285
|
+
|
|
286
|
+
def generate(self, model_id, prompt, system=""):
|
|
287
|
+
self.n += 1
|
|
288
|
+
return BackendResult("", 1, 0, 0, 0.0, False, error="billing:ANTHROPIC_API_KEY")
|
|
289
|
+
|
|
290
|
+
def missing_creds(self):
|
|
291
|
+
return []
|
|
292
|
+
|
|
293
|
+
backend = Billing()
|
|
294
|
+
result = run_pack(
|
|
295
|
+
[ModelSpec(id="a", label="arm-a"), ModelSpec(id="b", label="arm-b")],
|
|
296
|
+
_two_tasks(),
|
|
297
|
+
{"metered": backend},
|
|
298
|
+
)
|
|
299
|
+
assert backend.n == 1
|
|
300
|
+
assert result["partial"] == "aborted"
|
|
301
|
+
assert result["aborted"] is True
|
|
302
|
+
assert result["detail"]["arm-a"][0]["error"] == "billing:ANTHROPIC_API_KEY"
|
|
303
|
+
assert result["detail"]["arm-a"][0]["text"] == ""
|
|
304
|
+
assert all(r["not_run"] == "aborted" for r in result["detail"]["arm-b"])
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def test_resume_does_not_resend_completed_tasks(tmp_path):
|
|
308
|
+
backend = ScriptedBackend({"one": "BUY", "two": "BUY"})
|
|
309
|
+
ledger = tmp_path / "ledger.json"
|
|
310
|
+
spec = [ModelSpec(id="m", label="m")]
|
|
311
|
+
run_pack(spec, _two_tasks(), {"metered": backend}, ledger_path=str(ledger))
|
|
312
|
+
sent = len(backend.calls)
|
|
313
|
+
run_pack(spec, _two_tasks(), {"metered": backend}, ledger_path=str(ledger))
|
|
314
|
+
assert len(backend.calls) == sent
|
|
315
|
+
assert sent == 2
|
|
@@ -32,7 +32,10 @@ def test_version_matches_the_packaged_version():
|
|
|
32
32
|
agree, which is what its name promised all along.
|
|
33
33
|
"""
|
|
34
34
|
import pathlib
|
|
35
|
-
|
|
35
|
+
try:
|
|
36
|
+
import tomllib
|
|
37
|
+
except ModuleNotFoundError: # py3.10
|
|
38
|
+
import tomli as tomllib
|
|
36
39
|
|
|
37
40
|
import swarph_cli
|
|
38
41
|
|