swarph-cli 0.72.29__tar.gz → 0.72.30__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {swarph_cli-0.72.29/src/swarph_cli.egg-info → swarph_cli-0.72.30}/PKG-INFO +1 -1
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/pyproject.toml +1 -1
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/__init__.py +1 -1
- swarph_cli-0.72.30/src/swarph_cli/bench/prices.py +284 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/bench/refresh_prices.py +40 -7
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/scripts/wake_watchdog.py +251 -26
- {swarph_cli-0.72.29 → swarph_cli-0.72.30/src/swarph_cli.egg-info}/PKG-INFO +1 -1
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli.egg-info/SOURCES.txt +3 -0
- swarph_cli-0.72.30/tests/test_1248_ship_price_table.py +157 -0
- swarph_cli-0.72.30/tests/test_1250_price_cache.py +274 -0
- swarph_cli-0.72.30/tests/test_1256_commander_wake.py +172 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_729_wake_watchdog.py +45 -17
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_739_wake_writer.py +27 -1
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_960_channel.py +24 -5
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_bench_command.py +4 -1
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_bench_prices.py +6 -3
- swarph_cli-0.72.29/src/swarph_cli/bench/prices.py +0 -103
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/LICENSE +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/README.md +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/setup.cfg +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/__main__.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/bench/__init__.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/bench/backends.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/bench/claude_cli.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/bench/codex_cli.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/bench/pack.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/bench/providers.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/bench/quality.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/bench/rules.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/bench/runner.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/bench/signal_score.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/bench/validate.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/caller.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/capture/__init__.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/capture/harden.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/capture/lineage.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/capture/liveness.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/capture/manifest.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/capture/paths.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/capture/verify.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/cell.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/chain_token.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/channel.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/__init__.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/_content.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/_display.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/_gateway_client.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/add.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/bench.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/board.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/board_merge_check.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/brain.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/brain_ask.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/cell.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/cell_probe.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/channel.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/chat.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/codegraph.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/codegraph_hook.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/codex_hook_output.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/codex_waker.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/compress.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/daemon.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/dreaming.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/event.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/gateway.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/gh_route.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/group.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/guide.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/highlight.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/hook_interpreter.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/hook_output.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/hooks.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/import_session.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/init.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/install_hook.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/install_multiplexer.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/install_wake_hook.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/lane.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/mcp_server.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/memory.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/memory_sync.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/mesh.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/monitor.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/okf_links.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/onboard.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/protocol_handler.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/ratify.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/roster.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/rowclear_hook.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/schedule.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/security.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/service.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/spawn.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/sweep.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/timeline.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/triage.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/version.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/wake_hook_output.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/commands/watchdog.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/compress/__init__.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/compress/levers.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/compress/marker.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/compress/verify.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/console_safe.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/delivery_queue.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/dm_frame.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/__init__.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/candidates.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/clone.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/enrich.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/index_check.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/notify.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/organize.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/probes.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/report.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/run.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/slm.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/transcripts.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/dreaming/verify.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/duplicate_session.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/gateway/__init__.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/gateway/feature_registry.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/gateway/lanes_control.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/gateway/schema.sql +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/gateway/server.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/gateway/services_control.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/gateway_default.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/gh_identity.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/guide/GUIDE.md +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/identity.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/launchd/swarph-codex-outbox.plist +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/launchd/swarph-codex-outbox.timer.plist +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/launchd/swarph-monitor.plist +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/launchd/swarph-watchdog.plist +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/launchd/swarph-watchdog.timer.plist +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/launchd_render.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/main.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/multiplexer.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/operator/__init__.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/operator/__main__.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/operator/api.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/operator/cli.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/operator/config.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/operator/fmt.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/orphan_daemons.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/pane_probe.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/parsers/__init__.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/parsers/claude.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/peer_executor.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/peer_executor_windows.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/peer_reconciliation.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/peer_reply_outbox.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/peer_service_host.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/peer_service_payload.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/peer_service_reply.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/peer_staging.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/query_filters.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/quota_sync.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/rowclear.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/scripts/__init__.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/scrub.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/service/__init__.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/service/app.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/service/providers.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/session_bridge.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/source_text.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/stall_alert.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/systemd/codex-waker.default +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-stuck-sweep.service +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-stuck-sweep.timer +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-triage.service +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-triage.timer +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/the_ten.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/timeline_paths.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/tokens.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/triage/__init__.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/triage/backend.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/triage/job.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/triage/owners.json +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/triage/router.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/triage/rows.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli/triage/sensor.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli.egg-info/entry_points.txt +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli.egg-info/requires.txt +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/src/swarph_cli.egg-info/top_level.txt +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_1006_board_publisher.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_1006_session_board.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_1010_strand_only.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_1013_spawn_channel.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_1017_muse_tui.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_1034_copy_mode.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_1050_surrogate_wake.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_1052_opencode_deaf.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_1059_watchdog_unread_env.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_1061_roster_cli.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_1064_stuck_sweep.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_1065_release.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_1066_quiet_wakes.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_1068_structured_accept_cli.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_1071_quota_sync.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_1073_triage.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_1073_triage_board.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_1073_wheel_install.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_1075_task_cli.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_1083_openai_decisions.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_1144_cell_yaml_github.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_1197_commander_taps.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_1203_swarph_me_refusal.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_1206_offer_question.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_1214_egress.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_1219_egress_probe.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_1221_quota_windows.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_1222_maybe_start_cap.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_1228_quota_reach.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_1243_the_ten.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_124_register_merge.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_126_pending_from_complete.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_132_ensure_monitor_state_dir.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_138_memory_navigate.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_139_tool_probe.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_183_card_anchor.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_184_notify_honesty.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_201_win32_bash_refusal.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_243_onboard_per_peer_token.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_307_task4_ask_reply.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_319_refusal_exit_codes.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_323_mistral_backend.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_332_report_organize_findings.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_356_query_filters.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_397_gh_identity_router.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_397_router_preflight.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_402_one_identity_resolver.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_432_rowclear.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_467b_onboard_operator_credential.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_472_register_caller_binding.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_484_codex_queue_sink.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_513_duplicate_session.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_523_guide.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_524_conftest_hermeticity.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_532_accept_flag.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_535_version_capability.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_538_signal_bench.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_546_default_gateway.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_547_verbs_from_registry.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_548_gateway_remainder.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_552_codegraph_gateway.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_555_relay_row_shape.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_556_relay_freshness.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_559_edges_truncated.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_564_bootstrap_first_peer.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_579_register_bootstrap_message.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_591_cli_step_graph.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_644_monitor_supervision.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_648_install_unit.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_654_reexec.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_656_owned_slm.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_684_dreaming_verb.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_689_pkg_version_order.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_715_grok_wake.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_716_timeline_one_path.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_723_grok_0251.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_724_ps1_ascii_or_bom.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_725_stdio_reconfigure.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_726_grok_limit_screen.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_729_arm_check_delivery_gap.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_729_dm_once.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_734_dreaming_enrich_degrade.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_735_dm_mcp_tools.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_740_edit_project.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_762_cards_edit_priority.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_764_enrich_collapse.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_801_confirm_flow_cli.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_802_stage_history.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_825_codegraph_hook_noise.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_825_install_posttooluse_only.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_825_remaining_cap_shred_bind.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_829_counterfactual_column.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_830_hook_binding_version.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_84_cli_labels.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_866_doctrine_guide.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_872_identity_refuses_not_warns.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_874_source_text.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_931_board_search.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_935_deadline_clock.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_937_dreaming_notify.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_938_organize_line_limit.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_941_bench_backends.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_941_harness_342.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_945_same_line_pairing.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_955_prompt_audit.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_957_auth_http.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_959_venv_interpreter.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_960_ensure_monitor_heartbeat.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_961_cursor_grok_footer.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_961_loud_stop.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_963_plugin_manifest.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_964_channel_cell.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_965_mesh_wait.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_975_channel_hop.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_add_security_gate.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_artifact_add.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_artifact_hash.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_artifact_lib.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_artifact_mcp.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_artifact_skill.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_artifact_tool.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_artifact_uri.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_bench_backends.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_bench_claude_cli.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_bench_codex_cli.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_bench_pack.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_bench_quality.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_bench_runner.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_bench_validate.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_board_card_thread.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_board_command.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_board_merge_check.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_board_obligation_close.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_brain_ask_command.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_brain_ask_env_diagnosis.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_brain_ask_identity_explicitness.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_brain_command.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_caller_meta_guard.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_capture_lineage.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_capture_liveness.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_capture_manifest.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_capture_paths.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_capture_security.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_cell_command_dispatch.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_cell_harden.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_cell_loader.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_cell_selfcheck.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_cell_verify.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_chain_token.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_channel_command.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_channel_poll_never_breaks_dms.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_channel_post.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_channel_wake_policy_filter.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_chat_command.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_claude_parser.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_claude_tmux_template.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_codegraph_caller_scope.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_codegraph_command.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_codegraph_hook.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_codegraph_mcp.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_codex_waker.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_compress_caller_convention.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_compress_command.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_compress_levers.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_compress_marker.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_compress_verify.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_console_encoding_e2e.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_content_composition.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_copilot_membrane.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_copilot_pane_reader.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_cursor_membrane.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_daemon_command.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_daemon_console_encoding.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_daemon_delivery.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_delivery_queue.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_display_sanitize.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_ensure_monitor_no_peer_default.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_event_emit.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_feature_to_uri.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_gateway_brain.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_gateway_client.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_gateway_command.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_gateway_message_idempotency.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_gh_route_doctor_mesh_name.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_group_command.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_guide_commands_parse.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_guide_selfcheck.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_heartbeat_check.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_highlight_command.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_hook_output.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_hooks_activity_marker.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_hooks_add.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_hooks_bundle.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_hooks_init.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_hooks_legacy_generation_removal.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_hooks_lifecycle.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_hooks_merge.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_hooks_verify.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_hooks_win32_bash_wrapper.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_hooks_win32_command_path.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_import_command.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_inbox_identity_provenance.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_init_command.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_init_provider_menu.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_install_codex_hooks.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_install_hook.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_install_multiplexer_command.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_install_postcompact_hook.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_install_wake_hook.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_lane_command.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_launchd_units.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_main.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_main_unknown_verb_guard.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_mcp_missing_hint.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_mcp_server.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_memory_command.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_memory_emit_snippet.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_memory_gateway.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_memory_sync.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_memory_sync_membrane.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_mesh_command.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_mesh_inbox_mark_read.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_mesh_inbox_sanitize.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_mesh_originate_thread_623.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_mesh_send_unknown_peer.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_mesh_sidecar.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_mesh_sidecar_muse_sink.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_meta_edge_identity.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_monitor_channel_poll.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_monitor_cli.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_monitor_daemon_collision.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_monitor_self_name_verification.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_monitor_state_split.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_multiplexer.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_muse_membrane.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_muse_own_cli.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_obligation_close_is_surfaced.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_okf_links.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_onboard_command.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_opencode_membrane.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_opencode_plugin.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_operator_me.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_orphan_daemons.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_packaged_artifacts_exist.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_packaged_skills.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_peer_executor.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_peer_executor_windows.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_peer_reply_drain_command.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_peer_reply_drain_supervisor.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_peer_reply_outbox.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_peer_reply_transport.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_peer_service_e2e.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_peer_service_host.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_peer_service_payload.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_peer_service_reply.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_postcompact_recall.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_process_liveness.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_protocol_handler.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_providers_isolation.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_ratify_command.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_readme_claims_no_gateway_default.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_repl_caller_convention.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_schedule_command.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_schedule_create_contract.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_scrub.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_security.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_service_app.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_service_command.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_service_providers.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_session_bridge_inject.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_session_bridge_probe.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_session_bridge_resolve.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_session_path_role_gate.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_shipped_units_match_the_cli.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_smoke_chat.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_smoke_one_shot.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_smoke_phase_5_5.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_spawn_base_hoist.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_spawn_command.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_spawn_identity_stamp.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_spawn_live_pin.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_spawn_mitosis_lineage.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_spawn_tmux_session.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_spawn_windows_relaunch.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_stall_alert.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_subprocess_decode_encoding.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_timeline_command.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_tmux_wake_runstate_defer.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_tmux_wake_session_reanchor.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_tmux_wake_stale.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_tmux_wake_submit_verify.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_token_file_one_parser.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_token_precedence_explicit_first.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_unified_token_resolver.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_version_command.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_version_consistency.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_vibe_membrane.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_watchdog.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_watchdog_dm_wake.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_watchdog_dm_wake_wiring.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_watchdog_emit_health.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_watchdog_install_flags.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_watchdog_model_rung.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_watchdog_stale_peers.py +0 -0
- {swarph_cli-0.72.29 → swarph_cli-0.72.30}/tests/test_windows_waker_installer.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: swarph-cli
|
|
3
|
-
Version: 0.72.
|
|
3
|
+
Version: 0.72.30
|
|
4
4
|
Summary: The `swarph` binary — a multi-LLM CLI and mesh-gateway client + bundled server (`swarph gateway`): one-shot prompts across providers, interactive `chat`, multi-provider `spawn` (claude/codex/antigravity via a ProviderMembrane with subprocess billing-scrub), `mesh` send/inbox/register, `brain-ask` over the swarph-brain (gbrain) semantic memory, git-backed `assisted_memory`, per-invocation `gh` identity routing, a silent DM-wake hook, session `import`, and a stranded-session `watchdog`.
|
|
5
5
|
Author: Pierre Samson, Claude Opus
|
|
6
6
|
License: MIT
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "swarph-cli"
|
|
7
|
-
version = "0.72.
|
|
7
|
+
version = "0.72.30"
|
|
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" }
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
"""The auto-refreshed LLM LIST-price table (spec §4) — LIFTED from the
|
|
2
|
+
reference lab's already-tested ``workers/llm_prices.py`` (adapted imports/
|
|
3
|
+
cache path only; lookup semantics, fallback table, and substring-order bug
|
|
4
|
+
guard are unchanged). Do NOT reinvent this — see
|
|
5
|
+
``docs/2026-07-24-model-showdown-findings.md`` shipped-alongside note: it
|
|
6
|
+
already caught a real ~5x under-count on gemini-2.5-flash-lite.
|
|
7
|
+
|
|
8
|
+
The LIST-price BASE is auto-refreshed from the community-maintained LiteLLM
|
|
9
|
+
repo (:mod:`swarph_cli.bench.refresh_prices` -> a local JSON cache; TODO wire
|
|
10
|
+
a weekly cron the way the reference lab does), so new models are never
|
|
11
|
+
silently mis-priced by a stale hardcoded bucket.
|
|
12
|
+
|
|
13
|
+
Public API:
|
|
14
|
+
lookup(model) -> (input_$/Mtok, output_$/Mtok) # never raises
|
|
15
|
+
cost_usd(model, tokens_in, tokens_thought, tokens_out) -> $
|
|
16
|
+
"""
|
|
17
|
+
from __future__ import annotations
|
|
18
|
+
|
|
19
|
+
import datetime
|
|
20
|
+
import json
|
|
21
|
+
import logging
|
|
22
|
+
import os
|
|
23
|
+
import tempfile
|
|
24
|
+
|
|
25
|
+
log = logging.getLogger("swarph.bench.prices")
|
|
26
|
+
|
|
27
|
+
# card #1250. A cache older than this is refreshed on load(). Exactly this
|
|
28
|
+
# age is still fresh: "older than", not "at least".
|
|
29
|
+
PRICE_CACHE_TTL_S = 7 * 24 * 60 * 60
|
|
30
|
+
# Both load()-triggered fetches. refresh_prices.main() keeps its own 30s.
|
|
31
|
+
LOAD_FETCH_TIMEOUT_S = 5
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def _default_cache_path() -> str:
|
|
35
|
+
"""User cache, never the installed package.
|
|
36
|
+
|
|
37
|
+
card #1248: a table written next to this file lands in site-packages and
|
|
38
|
+
goes stale in the wheel. The cache lives under XDG_CACHE_HOME (or
|
|
39
|
+
~/.cache). SWARPH_BENCH_PRICE_CACHE overrides it.
|
|
40
|
+
"""
|
|
41
|
+
override = os.environ.get("SWARPH_BENCH_PRICE_CACHE")
|
|
42
|
+
if override:
|
|
43
|
+
return override
|
|
44
|
+
base = os.environ.get("XDG_CACHE_HOME") or os.path.join(
|
|
45
|
+
os.path.expanduser("~"), ".cache")
|
|
46
|
+
return os.path.join(base, "swarph", "llm_prices.json")
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
_CACHE_PATH = _default_cache_path()
|
|
50
|
+
_MAP: dict | None = None
|
|
51
|
+
# lookup() warns once per model per process when a loaded table has no row.
|
|
52
|
+
_FALLBACK_WARNED: set[str] = set()
|
|
53
|
+
|
|
54
|
+
# Reconciled fallbacks ($/Mtok input, output) — used ONLY when a model isn't in
|
|
55
|
+
# the refreshed cache. The cache is the real source; these are the safety net.
|
|
56
|
+
_FALLBACK = {
|
|
57
|
+
"pro": (1.25, 10.0),
|
|
58
|
+
"lite": (0.019, 0.075),
|
|
59
|
+
"flash": (0.30, 2.50),
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _now() -> datetime.datetime:
|
|
64
|
+
return datetime.datetime.now(datetime.timezone.utc)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _read_payload() -> dict | None:
|
|
68
|
+
try:
|
|
69
|
+
with open(_CACHE_PATH) as f:
|
|
70
|
+
data = json.load(f) or {}
|
|
71
|
+
return data if isinstance(data, dict) else None
|
|
72
|
+
except Exception:
|
|
73
|
+
return None
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _prices_of(payload: dict | None) -> dict:
|
|
77
|
+
if not payload:
|
|
78
|
+
return {}
|
|
79
|
+
prices = payload.get("prices")
|
|
80
|
+
return prices if isinstance(prices, dict) else {}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _generated_at(payload: dict | None) -> datetime.datetime | None:
|
|
84
|
+
"""UTC timestamp, or None when the field is missing or unparseable."""
|
|
85
|
+
if not payload:
|
|
86
|
+
return None
|
|
87
|
+
raw = payload.get("generated_at")
|
|
88
|
+
if not isinstance(raw, str) or not raw.strip():
|
|
89
|
+
return None
|
|
90
|
+
try:
|
|
91
|
+
parsed = datetime.datetime.fromisoformat(raw.strip())
|
|
92
|
+
except ValueError:
|
|
93
|
+
return None
|
|
94
|
+
if parsed.tzinfo is None:
|
|
95
|
+
parsed = parsed.replace(tzinfo=datetime.timezone.utc)
|
|
96
|
+
return parsed
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def _age_days_label(generated: datetime.datetime | None) -> str:
|
|
100
|
+
if generated is None:
|
|
101
|
+
return "unknown"
|
|
102
|
+
return str(max(0, int((_now() - generated).total_seconds() // 86400)))
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def _read_cache() -> dict:
|
|
106
|
+
return _prices_of(_read_payload())
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def _write_cache(prices: dict) -> None:
|
|
110
|
+
"""Atomic write of the user cache. Never writes inside this package."""
|
|
111
|
+
dest = os.path.abspath(_CACHE_PATH)
|
|
112
|
+
pkg = os.path.dirname(os.path.abspath(__file__))
|
|
113
|
+
if dest == pkg or dest.startswith(pkg + os.sep):
|
|
114
|
+
log.warning(
|
|
115
|
+
"refusing to write the price table into the package tree (%s)", dest)
|
|
116
|
+
return
|
|
117
|
+
payload = {
|
|
118
|
+
"generated_at": _now().isoformat(),
|
|
119
|
+
"source": "BerriAI/litellm model_prices_and_context_window.json",
|
|
120
|
+
"count": len(prices),
|
|
121
|
+
"prices": prices,
|
|
122
|
+
}
|
|
123
|
+
try:
|
|
124
|
+
os.makedirs(os.path.dirname(dest), exist_ok=True)
|
|
125
|
+
fd, tmp = tempfile.mkstemp(dir=os.path.dirname(dest), suffix=".tmp")
|
|
126
|
+
with os.fdopen(fd, "w") as f:
|
|
127
|
+
json.dump(payload, f)
|
|
128
|
+
os.replace(tmp, dest)
|
|
129
|
+
except OSError as e:
|
|
130
|
+
log.warning("llm price cache not written (%s)", e)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def _fetch_into_cache() -> dict:
|
|
134
|
+
"""Fill the user cache from the price source. Never writes inside
|
|
135
|
+
this package (that path is site-packages once the wheel is installed).
|
|
136
|
+
|
|
137
|
+
This is the empty-cache path. A stale cache goes through
|
|
138
|
+
``refresh_prices.guard`` in ``load()`` before it is written.
|
|
139
|
+
"""
|
|
140
|
+
try:
|
|
141
|
+
from .refresh_prices import fetch
|
|
142
|
+
fresh = fetch(timeout=LOAD_FETCH_TIMEOUT_S)
|
|
143
|
+
except Exception as e:
|
|
144
|
+
log.warning("llm price fetch failed (%s)", e)
|
|
145
|
+
return {}
|
|
146
|
+
if not fresh:
|
|
147
|
+
return {}
|
|
148
|
+
_write_cache(fresh)
|
|
149
|
+
return fresh
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _keep_stale(prior: dict, generated: datetime.datetime | None) -> dict:
|
|
153
|
+
log.warning(
|
|
154
|
+
"llm price refresh failed — keeping the stale table, %s days old",
|
|
155
|
+
_age_days_label(generated),
|
|
156
|
+
)
|
|
157
|
+
return prior
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def load(force: bool = False) -> dict:
|
|
161
|
+
"""The cached ``{normalized_model: {"in": $/Mtok, "out": $/Mtok}}`` map.
|
|
162
|
+
Cached in-process; ``force=True`` re-reads. Never raises.
|
|
163
|
+
|
|
164
|
+
A missing cache is fetched once into the user cache. If that also fails,
|
|
165
|
+
the empty table is a WARNING, not a DEBUG line: every later lookup is
|
|
166
|
+
then a built-in fallback (card #1248).
|
|
167
|
+
|
|
168
|
+
A cache older than ``PRICE_CACHE_TTL_S``, or one whose ``generated_at``
|
|
169
|
+
is missing or unparseable, is refreshed through ``refresh_prices.guard``.
|
|
170
|
+
A failed refresh keeps the stale table (card #1250).
|
|
171
|
+
"""
|
|
172
|
+
global _MAP
|
|
173
|
+
if _MAP is not None and not force:
|
|
174
|
+
return _MAP
|
|
175
|
+
payload = _read_payload()
|
|
176
|
+
prior = _prices_of(payload)
|
|
177
|
+
generated = _generated_at(payload)
|
|
178
|
+
if prior and generated is not None:
|
|
179
|
+
age = (_now() - generated).total_seconds()
|
|
180
|
+
if age <= PRICE_CACHE_TTL_S:
|
|
181
|
+
_MAP = prior
|
|
182
|
+
return _MAP
|
|
183
|
+
if prior:
|
|
184
|
+
table = _refresh_stale(prior, generated)
|
|
185
|
+
else:
|
|
186
|
+
table = _fetch_into_cache()
|
|
187
|
+
if not table:
|
|
188
|
+
log.warning(
|
|
189
|
+
"llm price table unavailable — pricing from the built-in "
|
|
190
|
+
"fallbacks (pro/lite/flash). No cache at %s and the price "
|
|
191
|
+
"source could not be fetched.",
|
|
192
|
+
_CACHE_PATH,
|
|
193
|
+
)
|
|
194
|
+
_MAP = {}
|
|
195
|
+
return _MAP
|
|
196
|
+
_MAP = table
|
|
197
|
+
return _MAP
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
def _refresh_stale(prior: dict, generated: datetime.datetime | None) -> dict:
|
|
201
|
+
"""Refetch a stale table. The guard runs only on this path, and the
|
|
202
|
+
guarded-count WARNING is logged only here — a first fill has no prior
|
|
203
|
+
to guard."""
|
|
204
|
+
try:
|
|
205
|
+
from .refresh_prices import fetch, guard
|
|
206
|
+
fresh = fetch(timeout=LOAD_FETCH_TIMEOUT_S)
|
|
207
|
+
except Exception as e:
|
|
208
|
+
log.warning("llm price fetch failed (%s)", e)
|
|
209
|
+
return _keep_stale(prior, generated)
|
|
210
|
+
if not fresh:
|
|
211
|
+
return _keep_stale(prior, generated)
|
|
212
|
+
merged, flagged = guard(fresh, prior)
|
|
213
|
+
log.warning(
|
|
214
|
+
"llm price refresh guarded %d change(s) against the stale table",
|
|
215
|
+
len(flagged),
|
|
216
|
+
)
|
|
217
|
+
_write_cache(merged)
|
|
218
|
+
return merged
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
def _norm(model: str) -> str:
|
|
222
|
+
"""Strip provider prefix (gemini/, vertex_ai/, ...) + lowercase."""
|
|
223
|
+
return (model or "").split("/")[-1].strip().lower()
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def lookup(model: str) -> tuple[float, float]:
|
|
227
|
+
"""(input_$/Mtok, output_$/Mtok) for ``model``. Exact cache hit ->
|
|
228
|
+
substring fallback (lite before flash so 'flash-lite' resolves as lite —
|
|
229
|
+
order-sensitive, don't reorder). Never raises."""
|
|
230
|
+
m = _norm(model)
|
|
231
|
+
prices = load()
|
|
232
|
+
p = prices.get(m)
|
|
233
|
+
if p and "in" in p and "out" in p:
|
|
234
|
+
return (float(p["in"]), float(p["out"]))
|
|
235
|
+
# substring fallback — ORDER MATTERS: lite is a substring of flash-lite
|
|
236
|
+
# and must win over flash.
|
|
237
|
+
if "lite" in m:
|
|
238
|
+
chosen, label = _FALLBACK["lite"], "lite"
|
|
239
|
+
elif "pro" in m:
|
|
240
|
+
chosen, label = _FALLBACK["pro"], "pro"
|
|
241
|
+
else:
|
|
242
|
+
chosen, label = _FALLBACK["flash"], "flash"
|
|
243
|
+
# A loaded table that lacks this model is the #1248 defect class for a
|
|
244
|
+
# model upstream does not list yet. Once per model per process. An empty
|
|
245
|
+
# table already warned in load(); don't warn again on every lookup.
|
|
246
|
+
if prices and m not in _FALLBACK_WARNED:
|
|
247
|
+
_FALLBACK_WARNED.add(m)
|
|
248
|
+
log.warning(
|
|
249
|
+
"llm price for %s is not in the loaded table — using the %s "
|
|
250
|
+
"fallback %s",
|
|
251
|
+
m, label, chosen,
|
|
252
|
+
)
|
|
253
|
+
return chosen
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
def is_known(model_id: str) -> bool:
|
|
257
|
+
"""True if ``model_id`` has an exact hit in the refreshed cache (as
|
|
258
|
+
opposed to resolving via the substring fallback)."""
|
|
259
|
+
p = load().get(_norm(model_id))
|
|
260
|
+
return bool(p and "in" in p and "out" in p)
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def cost_usd(model_id: str, tokens_in: int, tokens_thought: int, tokens_out: int) -> float:
|
|
264
|
+
"""Metered $ for one call: in-tokens at the input price, (thought + out)
|
|
265
|
+
tokens at the output price (mirrors the reference ``metered_usd()``)."""
|
|
266
|
+
price_in, price_out = lookup(model_id)
|
|
267
|
+
thought = tokens_thought or 0
|
|
268
|
+
return (tokens_in * price_in + (thought + tokens_out) * price_out) / 1_000_000
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def all_cached(force: bool = False) -> dict[str, tuple[float, float]]:
|
|
272
|
+
"""Every ``{model_id: (in, out)}`` currently in the refreshed cache — the
|
|
273
|
+
full LiteLLM-sourced price list, not just the models ``bench`` happens to
|
|
274
|
+
price. Exposed so the cache is a reusable mesh asset (e.g. a
|
|
275
|
+
cost-governor/ledger can read the same cache via ``swarph bench prices``)
|
|
276
|
+
rather than plumbing private to the runner."""
|
|
277
|
+
return {m: (float(v["in"]), float(v["out"])) for m, v in load(force=force).items()
|
|
278
|
+
if isinstance(v, dict) and "in" in v and "out" in v}
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def fallback_buckets() -> dict[str, tuple[float, float]]:
|
|
282
|
+
"""The 3 built-in substring-fallback buckets (pro/lite/flash), for
|
|
283
|
+
display when the cache is empty/stale."""
|
|
284
|
+
return dict(_FALLBACK)
|
|
@@ -19,6 +19,10 @@ not wired to any scheduler in this PR, just the ported fetch+guard logic.
|
|
|
19
19
|
|
|
20
20
|
Network call — never exercised in the offline test suite (mocked or skipped).
|
|
21
21
|
Read-only wrt the rest of swarph-cli: only ever writes the price cache.
|
|
22
|
+
|
|
23
|
+
``SWARPH_BENCH_PRICE_URL`` overrides the source URL. Only the ``https``,
|
|
24
|
+
``http`` and ``file`` schemes are accepted (card #1250). ``load()`` passes a
|
|
25
|
+
5s timeout; this CLI keeps 30s.
|
|
22
26
|
"""
|
|
23
27
|
from __future__ import annotations
|
|
24
28
|
|
|
@@ -26,22 +30,45 @@ import json
|
|
|
26
30
|
import os
|
|
27
31
|
import sys
|
|
28
32
|
import tempfile
|
|
33
|
+
import urllib.parse
|
|
29
34
|
import urllib.request
|
|
30
35
|
from datetime import datetime, timezone
|
|
31
36
|
|
|
32
|
-
|
|
37
|
+
def _cache_path() -> str:
|
|
38
|
+
"""Read at call time so a monkeypatch of prices._CACHE_PATH is honored,
|
|
39
|
+
and so a refresh never keeps an import-time path inside the package."""
|
|
40
|
+
from .prices import _CACHE_PATH
|
|
41
|
+
return _CACHE_PATH
|
|
33
42
|
|
|
34
43
|
LITELLM_URL = ("https://raw.githubusercontent.com/BerriAI/litellm/main/"
|
|
35
44
|
"model_prices_and_context_window.json")
|
|
36
45
|
GUARD_RATIO = 3.0 # reject a new price >3x or <1/3x the prior cached value
|
|
46
|
+
# The CLI's own fetch. load() passes prices.LOAD_FETCH_TIMEOUT_S instead.
|
|
47
|
+
CLI_FETCH_TIMEOUT_S = 30
|
|
48
|
+
_ALLOWED_SOURCE_SCHEMES = frozenset({"https", "http", "file"})
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def price_source_url() -> str:
|
|
52
|
+
"""The JSON URL ``fetch`` reads.
|
|
53
|
+
|
|
54
|
+
``SWARPH_BENCH_PRICE_URL`` overrides ``LITELLM_URL``. Schemes other than
|
|
55
|
+
https, http and file are refused.
|
|
56
|
+
"""
|
|
57
|
+
raw = (os.environ.get("SWARPH_BENCH_PRICE_URL") or LITELLM_URL).strip()
|
|
58
|
+
scheme = urllib.parse.urlparse(raw).scheme.lower()
|
|
59
|
+
if scheme not in _ALLOWED_SOURCE_SCHEMES:
|
|
60
|
+
raise ValueError(
|
|
61
|
+
f"SWARPH_BENCH_PRICE_URL scheme {scheme!r} is not allowed "
|
|
62
|
+
"(https, http, file)")
|
|
63
|
+
return raw
|
|
37
64
|
|
|
38
65
|
|
|
39
66
|
def _norm(key: str) -> str:
|
|
40
67
|
return key.split("/")[-1].strip().lower()
|
|
41
68
|
|
|
42
69
|
|
|
43
|
-
def fetch() -> dict:
|
|
44
|
-
raw = urllib.request.urlopen(
|
|
70
|
+
def fetch(timeout: float = CLI_FETCH_TIMEOUT_S) -> dict:
|
|
71
|
+
raw = urllib.request.urlopen(price_source_url(), timeout=timeout).read()
|
|
45
72
|
d = json.loads(raw)
|
|
46
73
|
out: dict[str, dict] = {}
|
|
47
74
|
for key, v in d.items():
|
|
@@ -59,7 +86,7 @@ def fetch() -> dict:
|
|
|
59
86
|
|
|
60
87
|
def load_prior() -> dict:
|
|
61
88
|
try:
|
|
62
|
-
with open(
|
|
89
|
+
with open(_cache_path()) as f:
|
|
63
90
|
return (json.load(f) or {}).get("prices", {}) or {}
|
|
64
91
|
except Exception:
|
|
65
92
|
return {}
|
|
@@ -98,11 +125,17 @@ def main() -> int:
|
|
|
98
125
|
payload = {"generated_at": datetime.now(timezone.utc).isoformat(),
|
|
99
126
|
"source": "BerriAI/litellm model_prices_and_context_window.json",
|
|
100
127
|
"count": len(merged), "prices": merged}
|
|
101
|
-
|
|
102
|
-
|
|
128
|
+
dest = _cache_path()
|
|
129
|
+
pkg = os.path.dirname(os.path.abspath(os.path.join(os.path.dirname(__file__), "prices.py")))
|
|
130
|
+
if os.path.abspath(dest).startswith(pkg + os.sep):
|
|
131
|
+
print(f"refresh_prices: refusing to write into the package tree ({dest})",
|
|
132
|
+
file=sys.stderr)
|
|
133
|
+
return 1
|
|
134
|
+
os.makedirs(os.path.dirname(dest), exist_ok=True)
|
|
135
|
+
fd, tmp = tempfile.mkstemp(dir=os.path.dirname(dest), suffix=".tmp")
|
|
103
136
|
with os.fdopen(fd, "w") as f:
|
|
104
137
|
json.dump(payload, f, indent=2, sort_keys=True)
|
|
105
|
-
os.replace(tmp,
|
|
138
|
+
os.replace(tmp, dest)
|
|
106
139
|
print(f"refresh_prices: {len(merged)} models cached "
|
|
107
140
|
f"({len(fresh)} fetched, {len(merged) - len(fresh)} prior-only kept). "
|
|
108
141
|
f"{len(flagged)} guarded.")
|