swarph-cli 0.66.0__tar.gz → 0.67.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {swarph_cli-0.66.0/src/swarph_cli.egg-info → swarph_cli-0.67.0}/PKG-INFO +16 -1
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/README.md +15 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/pyproject.toml +1 -1
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/__init__.py +1 -1
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/bench/backends.py +148 -2
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/bench/pack.py +15 -0
- swarph_cli-0.67.0/src/swarph_cli/bench/providers.py +94 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/bench/runner.py +153 -11
- swarph_cli-0.67.0/src/swarph_cli/channel.py +275 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/bench.py +37 -3
- swarph_cli-0.67.0/src/swarph_cli/commands/hook_interpreter.py +37 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/install_codex_hooks.py +4 -1
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/install_opencode_plugin.py +9 -1
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/install_postcompact_hook.py +3 -1
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/install_wake_hook.py +4 -1
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/mcp_server.py +19 -10
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/memory.py +18 -3
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/spawn.py +5 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/guide/GUIDE.md +1 -1
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/scripts/dm_notify_filter.py +54 -0
- swarph_cli-0.67.0/src/swarph_cli/scripts/wake_watchdog.py +201 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0/src/swarph_cli.egg-info}/PKG-INFO +16 -1
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli.egg-info/SOURCES.txt +10 -0
- swarph_cli-0.67.0/tests/test_138_memory_navigate.py +50 -0
- swarph_cli-0.67.0/tests/test_729_dm_once.py +84 -0
- swarph_cli-0.67.0/tests/test_729_wake_watchdog.py +220 -0
- swarph_cli-0.67.0/tests/test_957_auth_http.py +169 -0
- swarph_cli-0.67.0/tests/test_959_venv_interpreter.py +85 -0
- swarph_cli-0.67.0/tests/test_960_channel.py +186 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_memory_command.py +6 -3
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/LICENSE +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/setup.cfg +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/__main__.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/bench/__init__.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/bench/prices.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/bench/quality.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/bench/refresh_prices.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/bench/rules.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/bench/validate.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/caller.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/capture/__init__.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/capture/harden.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/capture/lineage.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/capture/liveness.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/capture/manifest.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/capture/paths.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/capture/verify.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/cell.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/chain_token.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/__init__.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/_content.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/_display.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/_gateway_client.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/add.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/board.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/board_merge_check.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/brain.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/brain_ask.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/cell.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/cell_probe.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/channel.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/chat.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/codegraph.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/codegraph_hook.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/codex_hook_output.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/codex_waker.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/compress.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/daemon.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/dreaming.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/event.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/gateway.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/gh_route.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/group.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/guide.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/highlight.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/hook_output.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/hooks.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/import_session.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/init.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/install_hook.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/install_multiplexer.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/lane.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/memory_sync.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/mesh.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/monitor.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/okf_links.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/onboard.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/protocol_handler.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/ratify.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/schedule.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/security.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/service.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/timeline.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/version.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/wake_hook_output.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/commands/watchdog.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/compress/__init__.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/compress/levers.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/compress/marker.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/compress/verify.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/console_safe.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/delivery_queue.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/dreaming/__init__.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/dreaming/candidates.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/dreaming/clone.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/dreaming/enrich.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/dreaming/index_check.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/dreaming/notify.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/dreaming/organize.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/dreaming/probes.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/dreaming/report.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/dreaming/run.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/dreaming/slm.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/dreaming/transcripts.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/dreaming/verify.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/gateway/__init__.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/gateway/feature_registry.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/gateway/lanes_control.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/gateway/schema.sql +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/gateway/server.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/gateway/services_control.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/gateway_default.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/gh_identity.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/main.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/multiplexer.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/orphan_daemons.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/pane_probe.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/parsers/__init__.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/parsers/claude.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/peer_executor.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/peer_executor_windows.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/peer_reconciliation.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/peer_reply_outbox.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/peer_service_host.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/peer_service_payload.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/peer_service_reply.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/peer_staging.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/query_filters.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/scripts/__init__.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/service/__init__.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/service/app.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/service/providers.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/session_bridge.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/source_text.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/stall_alert.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/systemd/codex-waker.default +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/timeline_paths.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli/tokens.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli.egg-info/entry_points.txt +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli.egg-info/requires.txt +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/src/swarph_cli.egg-info/top_level.txt +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_124_register_merge.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_126_pending_from_complete.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_132_ensure_monitor_state_dir.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_139_tool_probe.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_183_card_anchor.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_201_win32_bash_refusal.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_243_onboard_per_peer_token.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_307_task4_ask_reply.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_319_refusal_exit_codes.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_323_mistral_backend.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_332_report_organize_findings.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_356_query_filters.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_397_gh_identity_router.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_397_router_preflight.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_467b_onboard_operator_credential.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_523_guide.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_524_conftest_hermeticity.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_532_accept_flag.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_535_version_capability.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_546_default_gateway.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_547_verbs_from_registry.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_548_gateway_remainder.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_552_codegraph_gateway.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_555_relay_row_shape.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_556_relay_freshness.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_564_bootstrap_first_peer.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_579_register_bootstrap_message.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_591_cli_step_graph.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_644_monitor_supervision.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_648_install_unit.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_654_reexec.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_656_owned_slm.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_684_dreaming_verb.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_689_pkg_version_order.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_716_timeline_one_path.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_724_ps1_ascii_or_bom.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_725_stdio_reconfigure.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_734_dreaming_enrich_degrade.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_735_dm_mcp_tools.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_740_edit_project.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_762_cards_edit_priority.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_764_enrich_collapse.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_801_confirm_flow_cli.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_802_stage_history.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_825_codegraph_hook_noise.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_825_remaining_cap_shred_bind.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_829_counterfactual_column.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_830_hook_binding_version.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_84_cli_labels.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_866_doctrine_guide.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_872_identity_refuses_not_warns.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_874_source_text.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_937_dreaming_notify.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_938_organize_line_limit.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_941_bench_backends.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_941_harness_342.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_945_same_line_pairing.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_955_prompt_audit.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_add_security_gate.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_artifact_add.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_artifact_hash.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_artifact_lib.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_artifact_mcp.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_artifact_skill.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_artifact_tool.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_artifact_uri.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_bench_backends.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_bench_command.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_bench_pack.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_bench_prices.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_bench_quality.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_bench_runner.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_bench_validate.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_board_card_thread.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_board_command.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_board_merge_check.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_board_obligation_close.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_brain_ask_command.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_brain_ask_env_diagnosis.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_brain_ask_identity_explicitness.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_brain_command.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_caller_meta_guard.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_capture_lineage.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_capture_liveness.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_capture_manifest.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_capture_paths.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_capture_security.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_cell_command_dispatch.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_cell_harden.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_cell_loader.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_cell_selfcheck.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_cell_verify.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_chain_token.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_channel_command.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_channel_poll_never_breaks_dms.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_channel_post.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_channel_wake_policy_filter.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_chat_command.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_claude_parser.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_claude_tmux_template.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_codegraph_caller_scope.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_codegraph_command.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_codegraph_hook.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_codegraph_mcp.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_codex_waker.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_compress_caller_convention.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_compress_command.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_compress_levers.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_compress_marker.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_compress_verify.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_console_encoding_e2e.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_content_composition.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_cursor_membrane.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_daemon_command.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_daemon_console_encoding.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_daemon_delivery.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_delivery_queue.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_display_sanitize.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_ensure_monitor_no_peer_default.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_event_emit.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_feature_to_uri.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_gateway_brain.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_gateway_client.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_gateway_command.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_gateway_message_idempotency.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_gh_route_doctor_mesh_name.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_group_command.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_guide_commands_parse.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_guide_selfcheck.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_heartbeat_check.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_highlight_command.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_hook_output.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_hooks_activity_marker.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_hooks_add.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_hooks_bundle.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_hooks_init.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_hooks_legacy_generation_removal.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_hooks_lifecycle.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_hooks_merge.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_hooks_verify.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_hooks_win32_bash_wrapper.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_hooks_win32_command_path.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_import_command.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_inbox_identity_provenance.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_init_command.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_init_provider_menu.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_install_codex_hooks.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_install_hook.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_install_multiplexer_command.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_install_postcompact_hook.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_install_wake_hook.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_lane_command.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_main.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_main_unknown_verb_guard.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_mcp_missing_hint.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_mcp_server.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_memory_emit_snippet.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_memory_gateway.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_memory_sync.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_memory_sync_membrane.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_mesh_command.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_mesh_inbox_mark_read.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_mesh_inbox_sanitize.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_mesh_send_unknown_peer.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_mesh_sidecar.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_meta_edge_identity.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_monitor_channel_poll.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_monitor_cli.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_monitor_daemon_collision.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_monitor_self_name_verification.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_monitor_state_split.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_multiplexer.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_muse_membrane.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_muse_own_cli.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_obligation_close_is_surfaced.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_okf_links.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_onboard_command.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_opencode_membrane.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_opencode_plugin.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_orphan_daemons.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_packaged_artifacts_exist.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_packaged_skills.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_peer_executor.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_peer_executor_windows.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_peer_reply_drain_command.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_peer_reply_drain_supervisor.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_peer_reply_outbox.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_peer_reply_transport.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_peer_service_e2e.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_peer_service_host.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_peer_service_payload.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_peer_service_reply.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_postcompact_recall.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_process_liveness.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_protocol_handler.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_providers_isolation.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_ratify_command.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_readme_claims_no_gateway_default.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_repl_caller_convention.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_schedule_command.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_schedule_create_contract.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_security.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_service_app.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_service_command.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_service_providers.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_session_bridge_inject.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_session_bridge_probe.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_session_bridge_resolve.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_session_path_role_gate.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_shipped_units_match_the_cli.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_smoke_chat.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_smoke_one_shot.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_smoke_phase_5_5.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_spawn_base_hoist.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_spawn_command.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_spawn_identity_stamp.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_spawn_live_pin.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_spawn_mitosis_lineage.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_spawn_tmux_session.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_spawn_windows_relaunch.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_stall_alert.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_subprocess_decode_encoding.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_timeline_command.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_tmux_wake_runstate_defer.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_tmux_wake_session_reanchor.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_tmux_wake_stale.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_tmux_wake_submit_verify.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_token_file_one_parser.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_token_precedence_explicit_first.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_unified_token_resolver.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_version_command.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_version_consistency.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_vibe_membrane.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_watchdog.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_watchdog_dm_wake.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_watchdog_dm_wake_wiring.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_watchdog_emit_health.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_watchdog_install_flags.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_watchdog_model_rung.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_watchdog_stale_peers.py +0 -0
- {swarph_cli-0.66.0 → swarph_cli-0.67.0}/tests/test_windows_waker_installer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: swarph-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.67.0
|
|
4
4
|
Summary: The `swarph` binary — a multi-LLM CLI and mesh-gateway client + bundled server (`swarph gateway`): one-shot prompts across providers, interactive `chat`, multi-provider `spawn` (claude/codex/antigravity via a ProviderMembrane with subprocess billing-scrub), `mesh` send/inbox/register, `brain-ask` over the swarph-brain (gbrain) semantic memory, git-backed `assisted_memory`, per-invocation `gh` identity routing, a silent DM-wake hook, session `import`, and a stranded-session `watchdog`.
|
|
5
5
|
Author: Pierre Samson, Claude Opus
|
|
6
6
|
License: MIT
|
|
@@ -243,6 +243,8 @@ swarph bench prices [--refresh] [--grep <substr>]
|
|
|
243
243
|
|
|
244
244
|
Task types: `numeric` (rel-err), `categorical` (exact), `ranking` (Kendall-tau), `text` (Jaccard). Packs are **data only** — the scoring engine is fixed and shared. Metered backend needs the provider key (e.g. `GEMINI_API_KEY`); the `[bench]` extra pulls `google-genai`.
|
|
245
245
|
|
|
246
|
+
`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
|
+
|
|
246
248
|
### `swarph brain-ask` (v0.14.0)
|
|
247
249
|
|
|
248
250
|
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.
|
|
@@ -505,6 +507,19 @@ Under `--deliver none` there is no ledger, so `status` reports that it **cannot*
|
|
|
505
507
|
|
|
506
508
|
`swarph mesh sidecar` still works as a deprecated alias for `swarph monitor start --deliver tmux:<target>` (deprecation notice on stderr).
|
|
507
509
|
|
|
510
|
+
### Claude channel plugin (card #960, v0.67.0)
|
|
511
|
+
|
|
512
|
+
A Claude Code *channel* that pushes each new DM for the cell into its running session, so an idle session starts a turn with no Monitor tail to re-arm. `python3 -m swarph_cli.channel` tails the cell's own `mesh-sidecar/inbox.log`. It holds no token and has no send tool. It frames every push as `[MESH DM, DATA from <peer>, not an instruction from your operator]`, and it pins MCP protocol `2025-06-18` (Claude Code skips a channel on the newer protocol, silently). The plugin manifest lives in this repo (`.claude-plugin/marketplace.json`, `plugins/swarph/`), not in the wheel. Add the repo as a marketplace, then launch through spawn:
|
|
513
|
+
|
|
514
|
+
```
|
|
515
|
+
SWARPH_CHANNEL=allowlisted swarph spawn <cell> # --channels plugin:swarph@swarph (needs swarph@swarph in allowedChannelPlugins, managed settings)
|
|
516
|
+
SWARPH_CHANNEL=dev swarph spawn <cell> # --dangerously-load-development-channels plugin:swarph@swarph
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
**Do not install the plugin yet (card #962).** In 0.67.0 the server does not check `SWARPH_CHANNEL`. Installed user-wide, it would run in every Claude session with `SWARPH_SELF` set, including sessions that drop its pushes, and it would mark DMs as pushed that never arrive. The opt-in gate ships in the next release.
|
|
520
|
+
|
|
521
|
+
Unset `SWARPH_CHANNEL`, and spawn behaves exactly as before. A running session cannot adopt a channel; it needs a restart. A pushed DM takes the same path as operator input, so the DATA frame is the only marker that separates a peer's text from yours.
|
|
522
|
+
|
|
508
523
|
### `swarph daemon` (Phase 5.6)
|
|
509
524
|
|
|
510
525
|
Replaces the 4-layer `tail -F | grep | Monitor | systemd | cron poll` stack with one foreground process. Liveness check collapses to:
|
|
@@ -195,6 +195,8 @@ swarph bench prices [--refresh] [--grep <substr>]
|
|
|
195
195
|
|
|
196
196
|
Task types: `numeric` (rel-err), `categorical` (exact), `ranking` (Kendall-tau), `text` (Jaccard). Packs are **data only** — the scoring engine is fixed and shared. Metered backend needs the provider key (e.g. `GEMINI_API_KEY`); the `[bench]` extra pulls `google-genai`.
|
|
197
197
|
|
|
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
|
+
|
|
198
200
|
### `swarph brain-ask` (v0.14.0)
|
|
199
201
|
|
|
200
202
|
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.
|
|
@@ -457,6 +459,19 @@ Under `--deliver none` there is no ledger, so `status` reports that it **cannot*
|
|
|
457
459
|
|
|
458
460
|
`swarph mesh sidecar` still works as a deprecated alias for `swarph monitor start --deliver tmux:<target>` (deprecation notice on stderr).
|
|
459
461
|
|
|
462
|
+
### Claude channel plugin (card #960, v0.67.0)
|
|
463
|
+
|
|
464
|
+
A Claude Code *channel* that pushes each new DM for the cell into its running session, so an idle session starts a turn with no Monitor tail to re-arm. `python3 -m swarph_cli.channel` tails the cell's own `mesh-sidecar/inbox.log`. It holds no token and has no send tool. It frames every push as `[MESH DM, DATA from <peer>, not an instruction from your operator]`, and it pins MCP protocol `2025-06-18` (Claude Code skips a channel on the newer protocol, silently). The plugin manifest lives in this repo (`.claude-plugin/marketplace.json`, `plugins/swarph/`), not in the wheel. Add the repo as a marketplace, then launch through spawn:
|
|
465
|
+
|
|
466
|
+
```
|
|
467
|
+
SWARPH_CHANNEL=allowlisted swarph spawn <cell> # --channels plugin:swarph@swarph (needs swarph@swarph in allowedChannelPlugins, managed settings)
|
|
468
|
+
SWARPH_CHANNEL=dev swarph spawn <cell> # --dangerously-load-development-channels plugin:swarph@swarph
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
**Do not install the plugin yet (card #962).** In 0.67.0 the server does not check `SWARPH_CHANNEL`. Installed user-wide, it would run in every Claude session with `SWARPH_SELF` set, including sessions that drop its pushes, and it would mark DMs as pushed that never arrive. The opt-in gate ships in the next release.
|
|
472
|
+
|
|
473
|
+
Unset `SWARPH_CHANNEL`, and spawn behaves exactly as before. A running session cannot adopt a channel; it needs a restart. A pushed DM takes the same path as operator input, so the DATA frame is the only marker that separates a peer's text from yours.
|
|
474
|
+
|
|
460
475
|
### `swarph daemon` (Phase 5.6)
|
|
461
476
|
|
|
462
477
|
Replaces the 4-layer `tail -F | grep | Monitor | systemd | cron poll` stack with one foreground process. Liveness check collapses to:
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "swarph-cli"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.67.0"
|
|
8
8
|
description = "The `swarph` binary — a multi-LLM CLI and mesh-gateway client + bundled server (`swarph gateway`): one-shot prompts across providers, interactive `chat`, multi-provider `spawn` (claude/codex/antigravity via a ProviderMembrane with subprocess billing-scrub), `mesh` send/inbox/register, `brain-ask` over the swarph-brain (gbrain) semantic memory, git-backed `assisted_memory`, per-invocation `gh` identity routing, a silent DM-wake hook, session `import`, and a stranded-session `watchdog`."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -71,6 +71,7 @@ def estimate_tokens(text: Optional[str]) -> int:
|
|
|
71
71
|
|
|
72
72
|
|
|
73
73
|
class MeteredGeminiBackend:
|
|
74
|
+
KIND = "semantic"
|
|
74
75
|
"""google-genai Developer API — the v1 DEFAULT metered backend. Real
|
|
75
76
|
``usage_metadata`` (prompt/candidates/thoughts token counts), pennies per
|
|
76
77
|
call, no subscription-quota throttling.
|
|
@@ -142,6 +143,7 @@ class MeteredGeminiBackend:
|
|
|
142
143
|
|
|
143
144
|
|
|
144
145
|
class MeteredMistralBackend:
|
|
146
|
+
KIND = "semantic"
|
|
145
147
|
"""Mistral AI's own API — the fifth bench lane (card #323).
|
|
146
148
|
|
|
147
149
|
WHY THIS EXISTS: ``swarph bench`` could already PRICE mistral (90
|
|
@@ -228,6 +230,7 @@ class MeteredMistralBackend:
|
|
|
228
230
|
|
|
229
231
|
|
|
230
232
|
class SubscriptionBackend:
|
|
233
|
+
KIND = "semantic"
|
|
231
234
|
"""STUB/interface-only backend for a $0 subscription path (e.g. a node's
|
|
232
235
|
own CLI/OIDC lane). Deliberately NOT coupled to the reference stack's
|
|
233
236
|
:878x/:879x lane map (decision #3) — a caller supplies ``call_fn`` to
|
|
@@ -384,7 +387,8 @@ def _noul_labels(question: Optional[dict]) -> tuple[str, str]:
|
|
|
384
387
|
)
|
|
385
388
|
|
|
386
389
|
|
|
387
|
-
def _typed_value(payload: dict, request: Optional[dict] = None
|
|
390
|
+
def _typed_value(payload: dict, request: Optional[dict] = None,
|
|
391
|
+
threshold: Optional[float] = None) -> tuple[Any, Optional[float]]:
|
|
388
392
|
"""Pull the typed answer out of a Jev /v1/systemone response.
|
|
389
393
|
|
|
390
394
|
Prefers the question named ``answer``; otherwise the first question.
|
|
@@ -406,13 +410,15 @@ def _typed_value(payload: dict, request: Optional[dict] = None) -> tuple[Any, Op
|
|
|
406
410
|
if key == "noul":
|
|
407
411
|
p_true = float(block[key])
|
|
408
412
|
pos, neg = _noul_labels(_noul_question(request or {}))
|
|
409
|
-
|
|
413
|
+
cut = _NOUL_THRESHOLD if threshold is None else threshold
|
|
414
|
+
label = pos if p_true >= cut else neg
|
|
410
415
|
return label, p_true
|
|
411
416
|
return block[key], None
|
|
412
417
|
raise ValueError("systemone answer has no choice, noul, yes, or score")
|
|
413
418
|
|
|
414
419
|
|
|
415
420
|
class RuleBackend:
|
|
421
|
+
KIND = "typed"
|
|
416
422
|
"""In-process callable. ``model_id`` is ``module:callable``.
|
|
417
423
|
|
|
418
424
|
The callable receives ``(prompt, system)`` when it takes two arguments,
|
|
@@ -446,6 +452,7 @@ class RuleBackend:
|
|
|
446
452
|
|
|
447
453
|
|
|
448
454
|
class TypedHttpBackend:
|
|
455
|
+
KIND = "typed"
|
|
449
456
|
"""POST a bench item to a Jev-compatible ``/v1/systemone`` server.
|
|
450
457
|
|
|
451
458
|
``model_id`` is the server base (``http://127.0.0.1:<port>``) or a full
|
|
@@ -496,3 +503,142 @@ class TypedHttpBackend:
|
|
|
496
503
|
latency_s=round(time.perf_counter() - t0, 6),
|
|
497
504
|
estimated=False,
|
|
498
505
|
)
|
|
506
|
+
|
|
507
|
+
|
|
508
|
+
def _dig(payload: dict, dotted: str):
|
|
509
|
+
cur: Any = payload
|
|
510
|
+
for part in dotted.split("."):
|
|
511
|
+
if not isinstance(cur, dict):
|
|
512
|
+
return None
|
|
513
|
+
cur = cur.get(part)
|
|
514
|
+
return cur
|
|
515
|
+
|
|
516
|
+
|
|
517
|
+
def _redact(text: str, secrets: list[tuple[str, str, str]]) -> str:
|
|
518
|
+
"""Replace the exact key and ``<scheme> <key>`` with ``<redacted:ENVNAME>``."""
|
|
519
|
+
if not text:
|
|
520
|
+
return text
|
|
521
|
+
for value, envname, scheme in secrets:
|
|
522
|
+
if not value:
|
|
523
|
+
continue
|
|
524
|
+
if scheme:
|
|
525
|
+
text = text.replace(f"{scheme} {value}", f"<redacted:{envname}>")
|
|
526
|
+
text = text.replace(value, f"<redacted:{envname}>")
|
|
527
|
+
return text
|
|
528
|
+
|
|
529
|
+
|
|
530
|
+
class HttpBackend:
|
|
531
|
+
"""Authenticated HTTP arm. typed-http is the no-auth typed case of this shape.
|
|
532
|
+
|
|
533
|
+
The key is read from the named env var at dispatch and placed only in the
|
|
534
|
+
declared header. A non-2xx error is ``HTTP <status> <reason>`` and never
|
|
535
|
+
the response body.
|
|
536
|
+
"""
|
|
537
|
+
|
|
538
|
+
def __init__(self, *, name: str, kind: str, base_url: str, path: str,
|
|
539
|
+
auth: Optional[dict] = None, usage: Optional[dict] = None,
|
|
540
|
+
price: Optional[dict] = None, egress: str = "external",
|
|
541
|
+
noul_threshold: Optional[float] = None, transport=None):
|
|
542
|
+
self.name = name
|
|
543
|
+
self.KIND = kind
|
|
544
|
+
self.base_url = base_url.rstrip("/")
|
|
545
|
+
self.path = path
|
|
546
|
+
self.auth = auth
|
|
547
|
+
self.usage = usage or {}
|
|
548
|
+
self.price = price
|
|
549
|
+
self.egress = egress
|
|
550
|
+
self.noul_threshold = noul_threshold
|
|
551
|
+
self.transport = transport
|
|
552
|
+
self.calls = 0
|
|
553
|
+
|
|
554
|
+
def missing_creds(self) -> list[str]:
|
|
555
|
+
if not self.auth:
|
|
556
|
+
return []
|
|
557
|
+
env = self.auth.get("env") or ""
|
|
558
|
+
if not os.environ.get(env):
|
|
559
|
+
return [f"{env} (provider {self.name})"]
|
|
560
|
+
return []
|
|
561
|
+
|
|
562
|
+
def secrets(self) -> list[tuple[str, str, str]]:
|
|
563
|
+
if not self.auth:
|
|
564
|
+
return []
|
|
565
|
+
env = self.auth.get("env") or ""
|
|
566
|
+
value = os.environ.get(env) or ""
|
|
567
|
+
if not value:
|
|
568
|
+
return []
|
|
569
|
+
return [(value, env, self.auth.get("scheme") or "")]
|
|
570
|
+
|
|
571
|
+
def generate(self, model_id: str, prompt: str, system: str = "") -> BackendResult:
|
|
572
|
+
t0 = time.perf_counter()
|
|
573
|
+
secrets = self.secrets()
|
|
574
|
+
try:
|
|
575
|
+
endpoint = self.base_url + self.path
|
|
576
|
+
if self.KIND == "typed":
|
|
577
|
+
body = _request_body(prompt, system)
|
|
578
|
+
else:
|
|
579
|
+
body = {
|
|
580
|
+
"model": model_id or self.name,
|
|
581
|
+
"messages": [
|
|
582
|
+
{"role": "system", "content": system},
|
|
583
|
+
{"role": "user", "content": prompt},
|
|
584
|
+
],
|
|
585
|
+
}
|
|
586
|
+
headers = {"Content-Type": "application/json"}
|
|
587
|
+
if self.auth:
|
|
588
|
+
env = self.auth["env"]
|
|
589
|
+
value = os.environ.get(env) or ""
|
|
590
|
+
scheme = self.auth.get("scheme") or ""
|
|
591
|
+
token = f"{scheme} {value}".strip() if scheme else value
|
|
592
|
+
headers[self.auth["header"]] = token
|
|
593
|
+
raw_body = json.dumps(body).encode()
|
|
594
|
+
self.calls += 1
|
|
595
|
+
if self.transport is not None:
|
|
596
|
+
status, reason, raw = self.transport(endpoint, headers, raw_body)
|
|
597
|
+
else:
|
|
598
|
+
req = urllib.request.Request(
|
|
599
|
+
endpoint, data=raw_body, headers=headers, method="POST")
|
|
600
|
+
try:
|
|
601
|
+
with urllib.request.urlopen(req, timeout=60) as resp:
|
|
602
|
+
status, reason, raw = resp.status, resp.reason, resp.read()
|
|
603
|
+
except urllib.error.HTTPError as exc:
|
|
604
|
+
status, reason, raw = exc.code, exc.reason, exc.read()
|
|
605
|
+
if status < 200 or status >= 300:
|
|
606
|
+
return BackendResult(
|
|
607
|
+
text="", tokens_in=0, tokens_thought=0, tokens_out=0,
|
|
608
|
+
latency_s=round(time.perf_counter() - t0, 6),
|
|
609
|
+
estimated=False, error=_redact(f"HTTP {status} {reason}", secrets))
|
|
610
|
+
payload = json.loads(raw.decode() or "{}")
|
|
611
|
+
if not isinstance(payload, dict):
|
|
612
|
+
raise ValueError("response is not an object")
|
|
613
|
+
if self.KIND == "typed":
|
|
614
|
+
label, p_true = _typed_value(payload, body, self.noul_threshold)
|
|
615
|
+
rendered = {"answer": label}
|
|
616
|
+
if p_true is not None:
|
|
617
|
+
rendered["p_true"] = p_true
|
|
618
|
+
text = json.dumps(rendered)
|
|
619
|
+
in_path = self.usage.get("in") or "usage.input_tokens"
|
|
620
|
+
out_path = self.usage.get("out") or "usage.output_tokens"
|
|
621
|
+
else:
|
|
622
|
+
choices = payload.get("choices") or []
|
|
623
|
+
message = (choices[0].get("message") or {}) if choices else {}
|
|
624
|
+
text = message.get("content") or ""
|
|
625
|
+
if not text:
|
|
626
|
+
raise ValueError("semantic response has no content")
|
|
627
|
+
in_path = self.usage.get("in") or "usage.prompt_tokens"
|
|
628
|
+
out_path = self.usage.get("out") or "usage.completion_tokens"
|
|
629
|
+
tin = _dig(payload, in_path)
|
|
630
|
+
tout = _dig(payload, out_path)
|
|
631
|
+
text = _redact(text, secrets)
|
|
632
|
+
except Exception as exc:
|
|
633
|
+
return BackendResult(
|
|
634
|
+
text="", tokens_in=0, tokens_thought=0, tokens_out=0,
|
|
635
|
+
latency_s=round(time.perf_counter() - t0, 6),
|
|
636
|
+
estimated=False, error=_redact(str(exc), secrets))
|
|
637
|
+
return BackendResult(
|
|
638
|
+
text=text,
|
|
639
|
+
tokens_in=int(tin or 0),
|
|
640
|
+
tokens_thought=0,
|
|
641
|
+
tokens_out=int(tout or 0),
|
|
642
|
+
latency_s=round(time.perf_counter() - t0, 6),
|
|
643
|
+
estimated=tin is None or tout is None,
|
|
644
|
+
)
|
|
@@ -88,6 +88,14 @@ def validate_schema(pack: dict) -> tuple[list[str], list[str]]:
|
|
|
88
88
|
if "description" in pack and not isinstance(pack["description"], str):
|
|
89
89
|
errors.append("'description' must be a string when present")
|
|
90
90
|
|
|
91
|
+
kind = pack.get("request_kind", "semantic")
|
|
92
|
+
if "request_kind" in pack and kind not in {"typed", "semantic"}:
|
|
93
|
+
errors.append("'request_kind' must be typed or semantic")
|
|
94
|
+
if pack.get("egress") not in {None, "on_box_only"}:
|
|
95
|
+
errors.append("'egress' must be on_box_only when present")
|
|
96
|
+
if "noul_threshold" in pack and not isinstance(pack["noul_threshold"], (int, float)):
|
|
97
|
+
errors.append("'noul_threshold' must be a number")
|
|
98
|
+
|
|
91
99
|
tasks = pack.get("tasks")
|
|
92
100
|
if not isinstance(tasks, list) or len(tasks) < 1:
|
|
93
101
|
errors.append("'tasks' is required and must be a non-empty array")
|
|
@@ -111,6 +119,13 @@ def validate_schema(pack: dict) -> tuple[list[str], list[str]]:
|
|
|
111
119
|
prompt = task.get("prompt")
|
|
112
120
|
if not isinstance(prompt, str) or not prompt.strip():
|
|
113
121
|
errors.append(f"{where}.prompt is required and must be a non-empty string")
|
|
122
|
+
elif kind == "typed":
|
|
123
|
+
try:
|
|
124
|
+
parsed = json.loads(prompt)
|
|
125
|
+
except json.JSONDecodeError:
|
|
126
|
+
parsed = None
|
|
127
|
+
if not isinstance(parsed, dict) or "state" not in parsed or "questions" not in parsed:
|
|
128
|
+
errors.append(f"{where}.prompt must be JSON with state and questions when request_kind is typed")
|
|
114
129
|
|
|
115
130
|
ttype = task.get("type")
|
|
116
131
|
if ttype not in TASK_TYPES:
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
"""Provider registry for bench arms. TOML holds env-var names, never secrets."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import os
|
|
5
|
+
import tomllib
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from urllib.parse import urlparse
|
|
8
|
+
|
|
9
|
+
_FIELDS = {"kind", "base_url", "path", "auth", "usage", "price", "egress"}
|
|
10
|
+
_AUTH = {"env", "header", "scheme"}
|
|
11
|
+
_USAGE = {"in", "out"}
|
|
12
|
+
_PRICE = {"in_per_mtok", "out_per_mtok", "source"}
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class RegistryError(ValueError):
|
|
16
|
+
pass
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def registry_path(explicit: str | None = None) -> Path | None:
|
|
20
|
+
"""``--providers``, then ``$SWARPH_BENCH_PROVIDERS``, then the user config file."""
|
|
21
|
+
if explicit:
|
|
22
|
+
return Path(explicit)
|
|
23
|
+
env = os.environ.get("SWARPH_BENCH_PROVIDERS")
|
|
24
|
+
if env:
|
|
25
|
+
return Path(env)
|
|
26
|
+
home = Path.home() / ".config" / "swarph" / "bench_providers.toml"
|
|
27
|
+
if home.is_file():
|
|
28
|
+
return home
|
|
29
|
+
return None
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _loopback(url: str) -> bool:
|
|
33
|
+
host = (urlparse(url).hostname or "").lower()
|
|
34
|
+
if host in {"localhost", "::1"}:
|
|
35
|
+
return True
|
|
36
|
+
if host.startswith("127."):
|
|
37
|
+
return True
|
|
38
|
+
return False
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def egress_of_url(url: str) -> str:
|
|
42
|
+
return "local" if _loopback(url) else "external"
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _check(name: str, entry: dict) -> dict:
|
|
46
|
+
if not isinstance(entry, dict):
|
|
47
|
+
raise RegistryError(f"{name}: entry must be a table")
|
|
48
|
+
unknown = set(entry) - _FIELDS
|
|
49
|
+
if unknown:
|
|
50
|
+
raise RegistryError(f"{name}: unknown keys {sorted(unknown)}")
|
|
51
|
+
kind = entry.get("kind")
|
|
52
|
+
if kind not in {"typed", "semantic"}:
|
|
53
|
+
raise RegistryError(f"{name}: kind must be typed or semantic")
|
|
54
|
+
base = entry.get("base_url")
|
|
55
|
+
if not isinstance(base, str) or not base.strip():
|
|
56
|
+
raise RegistryError(f"{name}: base_url is required")
|
|
57
|
+
path = entry.get("path")
|
|
58
|
+
if path is None:
|
|
59
|
+
path = "/v1/systemone" if kind == "typed" else "/v1/chat/completions"
|
|
60
|
+
if not isinstance(path, str) or not path.startswith("/"):
|
|
61
|
+
raise RegistryError(f"{name}: path must start with /")
|
|
62
|
+
egress = entry.get("egress", "external")
|
|
63
|
+
if egress not in {"external", "local"}:
|
|
64
|
+
raise RegistryError(f"{name}: egress must be external or local")
|
|
65
|
+
if egress == "local" and not _loopback(base):
|
|
66
|
+
raise RegistryError(
|
|
67
|
+
f"{name}: egress=local is only valid for a loopback base_url")
|
|
68
|
+
auth = entry.get("auth")
|
|
69
|
+
if auth is not None:
|
|
70
|
+
if not isinstance(auth, dict) or set(auth) - _AUTH:
|
|
71
|
+
raise RegistryError(f"{name}: auth has unknown or missing fields")
|
|
72
|
+
if not auth.get("env") or not auth.get("header"):
|
|
73
|
+
raise RegistryError(f"{name}: auth.env and auth.header are required")
|
|
74
|
+
usage = entry.get("usage")
|
|
75
|
+
if usage is not None and (not isinstance(usage, dict) or set(usage) - _USAGE):
|
|
76
|
+
raise RegistryError(f"{name}: usage has unknown keys")
|
|
77
|
+
price = entry.get("price")
|
|
78
|
+
if price is not None:
|
|
79
|
+
if not isinstance(price, dict) or set(price) - _PRICE:
|
|
80
|
+
raise RegistryError(f"{name}: price has unknown keys")
|
|
81
|
+
if "in_per_mtok" not in price or "out_per_mtok" not in price:
|
|
82
|
+
raise RegistryError(f"{name}: price needs in_per_mtok and out_per_mtok")
|
|
83
|
+
out = dict(entry)
|
|
84
|
+
out["path"] = path
|
|
85
|
+
out["egress"] = egress
|
|
86
|
+
return out
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def load_registry(path: str | Path | None) -> dict[str, dict]:
|
|
90
|
+
if path is None:
|
|
91
|
+
return {}
|
|
92
|
+
raw = Path(path).read_text(encoding="utf-8")
|
|
93
|
+
data = tomllib.loads(raw)
|
|
94
|
+
return {name: _check(name, entry) for name, entry in data.items()}
|