swarph-cli 0.72.16__tar.gz → 0.72.18__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.16/src/swarph_cli.egg-info → swarph_cli-0.72.18}/PKG-INFO +9 -1
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/README.md +8 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/pyproject.toml +1 -1
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/__init__.py +1 -1
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/bench/backends.py +341 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/bench/providers.py +7 -3
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/bench.py +21 -5
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/board.py +117 -17
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/mesh.py +70 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/roster.py +196 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/schedule.py +10 -0
- swarph_cli-0.72.18/src/swarph_cli/quota_sync.py +252 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18/src/swarph_cli.egg-info}/PKG-INFO +9 -1
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli.egg-info/SOURCES.txt +5 -0
- swarph_cli-0.72.18/tests/test_1068_structured_accept_cli.py +104 -0
- swarph_cli-0.72.18/tests/test_1071_quota_sync.py +307 -0
- swarph_cli-0.72.18/tests/test_1083_openai_decisions.py +376 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_323_mistral_backend.py +12 -1
- swarph_cli-0.72.18/tests/test_copilot_pane_reader.py +165 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_schedule_command.py +31 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/LICENSE +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/setup.cfg +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/__main__.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/bench/__init__.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/bench/claude_cli.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/bench/codex_cli.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/bench/pack.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/bench/prices.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/bench/quality.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/bench/refresh_prices.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/bench/rules.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/bench/runner.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/bench/signal_score.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/bench/validate.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/caller.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/capture/__init__.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/capture/harden.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/capture/lineage.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/capture/liveness.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/capture/manifest.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/capture/paths.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/capture/verify.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/cell.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/chain_token.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/channel.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/__init__.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/_content.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/_display.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/_gateway_client.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/add.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/board_merge_check.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/brain.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/brain_ask.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/cell.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/cell_probe.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/cell_selfcheck.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/channel.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/chat.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/codegraph.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/codegraph_hook.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/codex_hook_output.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/codex_waker.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/compress.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/daemon.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/dreaming.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/event.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/gateway.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/gh_route.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/group.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/guide.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/highlight.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/hook_interpreter.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/hook_output.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/hooks.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/import_session.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/init.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/install_codex_hooks.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/install_hook.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/install_multiplexer.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/install_opencode_plugin.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/install_postcompact_hook.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/install_wake_hook.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/lane.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/mcp_server.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/memory.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/memory_emit_hook.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/memory_sync.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/monitor.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/okf_links.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/onboard.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/peer_reply_drain.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/postcompact_hook_output.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/protocol_handler.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/ratify.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/rowclear_hook.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/security.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/service.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/spawn.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/sweep.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/timeline.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/triage.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/version.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/wake_hook_output.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/commands/watchdog.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/compress/__init__.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/compress/levers.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/compress/marker.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/compress/verify.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/console_safe.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/delivery_queue.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/dm_frame.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/dreaming/__init__.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/dreaming/candidates.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/dreaming/clone.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/dreaming/enrich.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/dreaming/index_check.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/dreaming/notify.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/dreaming/organize.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/dreaming/probes.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/dreaming/report.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/dreaming/run.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/dreaming/slm.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/dreaming/transcripts.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/dreaming/verify.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/gateway/__init__.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/gateway/feature_registry.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/gateway/lanes_control.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/gateway/schema.sql +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/gateway/server.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/gateway/services_control.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/gateway_default.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/gh_identity.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/guide/GUIDE.md +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/identity.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/main.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/multiplexer.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/operator/__init__.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/operator/__main__.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/operator/api.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/operator/cli.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/operator/config.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/operator/fmt.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/orphan_daemons.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/pane_probe.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/parsers/__init__.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/parsers/claude.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/payloads/opencode/plugin.js +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/payloads/postcompact/_shim.cmd +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/payloads/postcompact/postcompact-recall-grok.sh +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/payloads/postcompact/postcompact-recall.sh +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/payloads/postcompact/posttooluse-emit-antigravity.sh +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/payloads/postcompact/posttooluse-emit-claude.sh +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/payloads/postcompact/posttooluse-emit-grok.sh +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/payloads/postcompact/posttooluse-emit.sh +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/payloads/postcompact/posttooluse-recall.sh +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/payloads/postcompact/precompact-flag.sh +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/payloads/postcompact/preinvocation-recall-antigravity.sh +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/payloads/postcompact/sessionstart-recall.sh +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/peer_executor.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/peer_executor_windows.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/peer_reconciliation.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/peer_reply_outbox.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/peer_service_host.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/peer_service_payload.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/peer_service_reply.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/peer_staging.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/query_filters.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/rowclear.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/scripts/__init__.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/scripts/dm_notify_filter.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/scripts/ensure_monitor.sh +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/scripts/install_codex_waker_windows.ps1 +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/scripts/install_monitor_task_windows.ps1 +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/scripts/wake_watchdog.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/scrub.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/service/__init__.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/service/app.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/service/providers.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/session_bridge.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/skills/i-have-adhd/LICENSE +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/skills/i-have-adhd/SKILL.md +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/source_text.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/stall_alert.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/codex-waker.default +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/peer-reply-drain.default +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-codex-outbox@.service +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-codex-outbox@.timer +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-codex-waker@.service +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-codex-waker@.timer +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-monitor-reexec.path +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-monitor-reexec.service +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-monitor@.service +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-peer-reply-drain@.service +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-peer-reply-drain@.timer +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-stuck-sweep.service +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-stuck-sweep.timer +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-triage.service +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-triage.timer +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-watchdog.default +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-watchdog.service +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/systemd/swarph-watchdog.timer +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/timeline_paths.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/tokens.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/triage/__init__.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/triage/backend.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/triage/job.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/triage/owners.json +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/triage/router.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/triage/rows.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli/triage/sensor.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli.egg-info/dependency_links.txt +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli.egg-info/entry_points.txt +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli.egg-info/requires.txt +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/src/swarph_cli.egg-info/top_level.txt +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1006_board_publisher.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1006_session_board.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1010_strand_only.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1013_spawn_channel.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1017_muse_tui.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1050_surrogate_wake.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1052_opencode_deaf.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1059_watchdog_unread_env.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1061_roster_cli.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1064_stuck_sweep.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1065_release.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1066_quiet_wakes.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1073_triage.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1073_triage_board.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1073_wheel_install.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_1075_task_cli.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_124_register_merge.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_126_pending_from_complete.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_132_ensure_monitor_state_dir.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_138_memory_navigate.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_139_tool_probe.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_183_card_anchor.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_184_notify_honesty.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_201_win32_bash_refusal.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_243_onboard_per_peer_token.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_307_task4_ask_reply.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_312_codegraph_fuzzy_verb.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_319_refusal_exit_codes.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_332_report_organize_findings.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_356_query_filters.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_397_gh_identity_router.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_397_router_preflight.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_402_one_identity_resolver.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_432_rowclear.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_467b_onboard_operator_credential.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_472_register_caller_binding.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_484_codex_queue_sink.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_523_guide.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_524_conftest_hermeticity.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_532_accept_flag.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_535_version_capability.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_538_signal_bench.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_546_default_gateway.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_547_verbs_from_registry.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_548_gateway_remainder.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_552_codegraph_gateway.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_555_relay_row_shape.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_556_relay_freshness.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_559_edges_truncated.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_564_bootstrap_first_peer.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_578_no_machine_specific_host_defaults.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_579_register_bootstrap_message.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_591_cli_step_graph.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_644_monitor_supervision.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_648_install_unit.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_654_reexec.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_656_owned_slm.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_684_dreaming_verb.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_689_pkg_version_order.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_715_grok_wake.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_716_timeline_one_path.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_723_grok_0251.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_724_ps1_ascii_or_bom.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_725_stdio_reconfigure.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_726_grok_limit_screen.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_729_arm_check_delivery_gap.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_729_dm_once.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_729_wake_watchdog.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_734_dreaming_enrich_degrade.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_735_dm_mcp_tools.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_739_wake_writer.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_740_edit_project.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_762_cards_edit_priority.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_764_enrich_collapse.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_801_confirm_flow_cli.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_802_stage_history.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_825_codegraph_hook_noise.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_825_install_posttooluse_only.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_825_remaining_cap_shred_bind.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_829_counterfactual_column.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_830_hook_binding_version.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_84_cli_labels.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_866_doctrine_guide.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_872_identity_refuses_not_warns.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_874_source_text.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_888_opencode_data_dir_can_fail.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_937_dreaming_notify.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_938_organize_line_limit.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_941_bench_backends.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_941_harness_342.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_945_same_line_pairing.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_955_prompt_audit.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_957_auth_http.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_959_venv_interpreter.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_960_channel.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_960_ensure_monitor_heartbeat.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_961_cursor_grok_footer.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_961_loud_stop.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_963_plugin_manifest.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_964_channel_cell.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_965_mesh_wait.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_975_channel_hop.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_add_security_gate.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_artifact_add.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_artifact_hash.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_artifact_lib.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_artifact_mcp.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_artifact_skill.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_artifact_tool.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_artifact_uri.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_bench_backends.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_bench_claude_cli.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_bench_codex_cli.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_bench_command.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_bench_pack.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_bench_prices.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_bench_quality.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_bench_runner.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_bench_validate.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_board_card_thread.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_board_command.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_board_merge_check.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_board_obligation_close.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_brain_ask_command.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_brain_ask_env_diagnosis.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_brain_ask_identity_explicitness.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_brain_command.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_caller_meta_guard.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_capture_lineage.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_capture_liveness.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_capture_manifest.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_capture_paths.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_capture_security.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_cell_command_dispatch.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_cell_harden.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_cell_loader.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_cell_selfcheck.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_cell_verify.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_chain_token.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_channel_command.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_channel_poll_never_breaks_dms.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_channel_post.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_channel_wake_policy_filter.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_chat_command.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_claude_parser.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_claude_tmux_template.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_codegraph_caller_scope.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_codegraph_command.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_codegraph_hook.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_codegraph_mcp.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_codex_waker.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_compress_caller_convention.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_compress_command.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_compress_levers.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_compress_marker.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_compress_verify.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_console_encoding_e2e.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_content_composition.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_copilot_membrane.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_cursor_membrane.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_daemon_command.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_daemon_console_encoding.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_daemon_delivery.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_delivery_queue.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_display_sanitize.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_ensure_monitor_no_peer_default.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_event_emit.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_feature_to_uri.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_gateway_brain.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_gateway_client.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_gateway_command.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_gateway_message_idempotency.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_gh_route_doctor_mesh_name.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_group_command.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_guide_commands_parse.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_guide_selfcheck.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_heartbeat_check.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_highlight_command.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_hook_output.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_hooks_activity_marker.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_hooks_add.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_hooks_bundle.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_hooks_init.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_hooks_legacy_generation_removal.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_hooks_lifecycle.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_hooks_merge.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_hooks_verify.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_hooks_win32_bash_wrapper.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_hooks_win32_command_path.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_import_command.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_inbox_identity_provenance.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_init_command.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_init_provider_menu.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_install_codex_hooks.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_install_hook.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_install_multiplexer_command.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_install_postcompact_hook.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_install_wake_hook.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_lane_command.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_main.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_main_unknown_verb_guard.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_mcp_missing_hint.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_mcp_server.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_memory_command.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_memory_emit_snippet.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_memory_gateway.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_memory_sync.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_memory_sync_membrane.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_mesh_command.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_mesh_inbox_mark_read.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_mesh_inbox_sanitize.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_mesh_originate_thread_623.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_mesh_send_unknown_peer.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_mesh_sidecar.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_mesh_sidecar_cursor_decoupled.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_mesh_sidecar_muse_sink.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_mesh_sidecar_wake_survives_read.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_meta_edge_identity.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_monitor_channel_poll.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_monitor_cli.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_monitor_daemon_collision.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_monitor_self_name_verification.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_monitor_state_split.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_multiplexer.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_muse_membrane.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_muse_own_cli.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_obligation_close_is_surfaced.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_okf_links.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_onboard_command.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_opencode_membrane.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_opencode_plugin.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_operator_me.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_orphan_daemons.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_packaged_artifacts_exist.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_packaged_skills.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_peer_executor.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_peer_executor_windows.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_peer_reply_drain_command.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_peer_reply_drain_supervisor.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_peer_reply_outbox.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_peer_reply_transport.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_peer_service_e2e.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_peer_service_host.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_peer_service_payload.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_peer_service_reply.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_postcompact_recall.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_process_liveness.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_protocol_handler.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_providers_isolation.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_ratify_command.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_readme_claims_no_gateway_default.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_repl_caller_convention.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_schedule_create_contract.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_scrub.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_security.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_service_app.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_service_command.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_service_providers.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_session_bridge_inject.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_session_bridge_probe.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_session_bridge_resolve.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_session_path_role_gate.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_shipped_units_match_the_cli.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_smoke_chat.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_smoke_one_shot.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_smoke_phase_5_5.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_spawn_base_hoist.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_spawn_command.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_spawn_identity_stamp.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_spawn_live_pin.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_spawn_mitosis_lineage.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_spawn_tmux_session.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_spawn_windows_relaunch.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_stall_alert.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_subprocess_decode_encoding.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_timeline_command.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_tmux_wake_runstate_defer.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_tmux_wake_session_reanchor.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_tmux_wake_stale.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_tmux_wake_submit_verify.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_token_file_one_parser.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_token_precedence_explicit_first.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_unified_token_resolver.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_version_command.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_version_consistency.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_vibe_membrane.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_watchdog.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_watchdog_dm_wake.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_watchdog_dm_wake_cooldown.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_watchdog_dm_wake_wiring.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_watchdog_emit_health.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_watchdog_install_flags.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_watchdog_model_rung.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/tests/test_watchdog_stale_peers.py +0 -0
- {swarph_cli-0.72.16 → swarph_cli-0.72.18}/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.18
|
|
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
|
|
@@ -497,6 +497,14 @@ swarph monitor stop
|
|
|
497
497
|
| `none` | genuinely nothing: observe, append `inbox.log`, no ledger, **no unread tracking**. |
|
|
498
498
|
| `webhook:<url>` | **held** pending an egress review — exits non-zero rather than silently no-op. |
|
|
499
499
|
|
|
500
|
+
**Copilot tmux panes.** The reader recognizes Copilot CLI 1.0.94's color input
|
|
501
|
+
box and `--no-color` composer using the bottom AIC summary, input borders, and
|
|
502
|
+
status footer. It defers while `esc interrupt` indicates a running turn or
|
|
503
|
+
the composer holds a human draft. The wake names `swarph_dm_unread`; delivery
|
|
504
|
+
is recorded only after the composer is observed clear. Missing or incomplete
|
|
505
|
+
pane captures send no keystrokes and leave delivery pending with a visible
|
|
506
|
+
failure. Keep `pull` for layouts that hide or customize this chrome.
|
|
507
|
+
|
|
500
508
|
**Two pieces of state that never share a variable.** The *observation cursor* (`cursor.json`) is what this monitor has READ from the gateway; it advances on observation, always, gated on nothing. A *delivery ledger* (`ledgers.json`, one per sink) advances only when that sink is satisfied and may lag arbitrarily far. So a dead tmux pane can no longer freeze the cursor, one dead sink cannot stall another, and a sink attached tomorrow starts with an empty ledger and replays from `inbox.log` (bounded, and it reports what it skipped).
|
|
501
509
|
|
|
502
510
|
**Pull beats push.** Every push sink's liveness is a precondition for hearing anything — if the pane dies, "no wake arrived" is indistinguishable from "no mail arrived". A pull check run *by* the cell lives one layer above tmux and cannot die with it. Designed to drop straight into a SessionStart hook:
|
|
@@ -448,6 +448,14 @@ swarph monitor stop
|
|
|
448
448
|
| `none` | genuinely nothing: observe, append `inbox.log`, no ledger, **no unread tracking**. |
|
|
449
449
|
| `webhook:<url>` | **held** pending an egress review — exits non-zero rather than silently no-op. |
|
|
450
450
|
|
|
451
|
+
**Copilot tmux panes.** The reader recognizes Copilot CLI 1.0.94's color input
|
|
452
|
+
box and `--no-color` composer using the bottom AIC summary, input borders, and
|
|
453
|
+
status footer. It defers while `esc interrupt` indicates a running turn or
|
|
454
|
+
the composer holds a human draft. The wake names `swarph_dm_unread`; delivery
|
|
455
|
+
is recorded only after the composer is observed clear. Missing or incomplete
|
|
456
|
+
pane captures send no keystrokes and leave delivery pending with a visible
|
|
457
|
+
failure. Keep `pull` for layouts that hide or customize this chrome.
|
|
458
|
+
|
|
451
459
|
**Two pieces of state that never share a variable.** The *observation cursor* (`cursor.json`) is what this monitor has READ from the gateway; it advances on observation, always, gated on nothing. A *delivery ledger* (`ledgers.json`, one per sink) advances only when that sink is satisfied and may lag arbitrarily far. So a dead tmux pane can no longer freeze the cursor, one dead sink cannot stall another, and a sink attached tomorrow starts with an empty ledger and replays from `inbox.log` (bounded, and it reports what it skipped).
|
|
452
460
|
|
|
453
461
|
**Pull beats push.** Every push sink's liveness is a precondition for hearing anything — if the pane dies, "no wake arrived" is indistinguishable from "no mail arrived". A pull check run *by* the cell lives one layer above tmux and cannot die with it. Designed to drop straight into a SessionStart hook:
|
|
@@ -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.18"
|
|
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" }
|
|
@@ -22,6 +22,9 @@ Ships these backends:
|
|
|
22
22
|
- :class:`RuleBackend` — in-process ``rule:<module:callable>``. No credentials.
|
|
23
23
|
- :class:`TypedHttpBackend` — POST to a Jev-compatible ``/v1/systemone``
|
|
24
24
|
(laya-serve). No credentials; per-item latency is the HTTP round trip.
|
|
25
|
+
- :class:`DecisionsBackend` — POST ``/v1/decisions``. Translates a pack's
|
|
26
|
+
``{state, questions}`` body into the Decisions shape. It does not send
|
|
27
|
+
the pack body unchanged.
|
|
25
28
|
"""
|
|
26
29
|
from __future__ import annotations
|
|
27
30
|
|
|
@@ -675,3 +678,341 @@ class HttpBackend:
|
|
|
675
678
|
latency_s=round(time.perf_counter() - t0, 6),
|
|
676
679
|
estimated=tin is None or tout is None,
|
|
677
680
|
)
|
|
681
|
+
|
|
682
|
+
|
|
683
|
+
# The Decisions endpoint documents one model. ``provider:openai`` puts the
|
|
684
|
+
# registry name in model_id, so the model is not taken from that string.
|
|
685
|
+
_DECISIONS_MODEL = "gpt-6-luna"
|
|
686
|
+
_PREDICATE_TYPES = {"noul", "yes", "yes-no", "predicate"}
|
|
687
|
+
|
|
688
|
+
|
|
689
|
+
def decisions_shape_error(body: Any) -> Optional[str]:
|
|
690
|
+
"""Why a POST /v1/decisions body is not the documented shape, or None.
|
|
691
|
+
|
|
692
|
+
A double more permissive than the real API hid live failures before
|
|
693
|
+
(#1077 / #1078). This is the check that double applies.
|
|
694
|
+
"""
|
|
695
|
+
if not isinstance(body, dict):
|
|
696
|
+
return "body is not an object"
|
|
697
|
+
questions = body.get("questions")
|
|
698
|
+
if not isinstance(questions, list):
|
|
699
|
+
return "questions is not a list"
|
|
700
|
+
for i, question in enumerate(questions):
|
|
701
|
+
if not isinstance(question, dict):
|
|
702
|
+
return f"questions[{i}] is not an object"
|
|
703
|
+
for field in ("name", "type", "instructions"):
|
|
704
|
+
value = question.get(field)
|
|
705
|
+
if not isinstance(value, str) or not value.strip():
|
|
706
|
+
return f"questions[{i}] lacks {field}"
|
|
707
|
+
qtype = question.get("type")
|
|
708
|
+
if qtype == "predicate" and ("choices" in question or "levels" in question):
|
|
709
|
+
return f"questions[{i}] is a predicate carrying choices or levels"
|
|
710
|
+
if qtype == "score":
|
|
711
|
+
levels = question.get("levels")
|
|
712
|
+
if not isinstance(levels, list) or not levels:
|
|
713
|
+
return f"questions[{i}] is a score without levels"
|
|
714
|
+
for j, level in enumerate(levels):
|
|
715
|
+
if (not isinstance(level, dict)
|
|
716
|
+
or not isinstance(level.get("label"), str)
|
|
717
|
+
or not level["label"]
|
|
718
|
+
or not isinstance(level.get("description"), str)):
|
|
719
|
+
return (
|
|
720
|
+
f"questions[{i}].levels[{j}] is not "
|
|
721
|
+
"{label, description}")
|
|
722
|
+
continue
|
|
723
|
+
if qtype != "choice":
|
|
724
|
+
continue
|
|
725
|
+
choices = question.get("choices")
|
|
726
|
+
if not isinstance(choices, list) or not choices:
|
|
727
|
+
return f"questions[{i}] is a choice without choices"
|
|
728
|
+
for j, choice in enumerate(choices):
|
|
729
|
+
if (not isinstance(choice, dict)
|
|
730
|
+
or not isinstance(choice.get("value"), str)
|
|
731
|
+
or not choice["value"]
|
|
732
|
+
or not isinstance(choice.get("description"), str)):
|
|
733
|
+
return (
|
|
734
|
+
f"questions[{i}].choices[{j}] is not "
|
|
735
|
+
"{value, description}")
|
|
736
|
+
return None
|
|
737
|
+
|
|
738
|
+
|
|
739
|
+
class StrictDecisionsDouble:
|
|
740
|
+
"""Fake POST /v1/decisions. A body that fails the documented shape is 400.
|
|
741
|
+
|
|
742
|
+
``response`` is the JSON object returned when the body is accepted.
|
|
743
|
+
"""
|
|
744
|
+
|
|
745
|
+
def __init__(self, response: Optional[dict] = None):
|
|
746
|
+
self.response = response if response is not None else {"answers": []}
|
|
747
|
+
self.bodies: list = []
|
|
748
|
+
|
|
749
|
+
def __call__(self, endpoint, headers, raw_body):
|
|
750
|
+
try:
|
|
751
|
+
body = json.loads(raw_body.decode() or "null")
|
|
752
|
+
except (UnicodeDecodeError, json.JSONDecodeError):
|
|
753
|
+
return 400, "Bad Request", b'{"error": "body is not JSON"}'
|
|
754
|
+
self.bodies.append(body)
|
|
755
|
+
reason = decisions_shape_error(body)
|
|
756
|
+
if reason:
|
|
757
|
+
return 400, "Bad Request", json.dumps({"error": reason}).encode()
|
|
758
|
+
return 200, "OK", json.dumps(self.response).encode()
|
|
759
|
+
|
|
760
|
+
|
|
761
|
+
def _pack_prompt(prompt: str) -> dict:
|
|
762
|
+
try:
|
|
763
|
+
parsed = json.loads(prompt) if isinstance(prompt, str) else None
|
|
764
|
+
except json.JSONDecodeError:
|
|
765
|
+
parsed = None
|
|
766
|
+
if (not isinstance(parsed, dict) or "state" not in parsed
|
|
767
|
+
or not isinstance(parsed.get("questions"), dict)
|
|
768
|
+
or not parsed["questions"]):
|
|
769
|
+
raise ValueError(
|
|
770
|
+
"decisions prompt must be JSON with state and questions")
|
|
771
|
+
return parsed
|
|
772
|
+
|
|
773
|
+
|
|
774
|
+
def _answer_block(parsed: dict) -> tuple[str, dict]:
|
|
775
|
+
questions = parsed["questions"]
|
|
776
|
+
block = questions.get("answer")
|
|
777
|
+
if isinstance(block, dict):
|
|
778
|
+
return "answer", block
|
|
779
|
+
name, block = next(iter(questions.items()))
|
|
780
|
+
if not isinstance(block, dict):
|
|
781
|
+
raise ValueError("decisions question is not an object")
|
|
782
|
+
return str(name), block
|
|
783
|
+
|
|
784
|
+
|
|
785
|
+
def _translate_question(name: str, block: dict) -> dict:
|
|
786
|
+
raw_type = block.get("type")
|
|
787
|
+
if raw_type in _PREDICATE_TYPES:
|
|
788
|
+
out_type = "predicate"
|
|
789
|
+
else:
|
|
790
|
+
out_type = raw_type
|
|
791
|
+
question: dict = {"name": name, "type": out_type}
|
|
792
|
+
instructions = block.get("instructions")
|
|
793
|
+
if isinstance(instructions, str):
|
|
794
|
+
question["instructions"] = instructions
|
|
795
|
+
if out_type == "choice" and isinstance(block.get("criteria"), dict):
|
|
796
|
+
question["choices"] = [
|
|
797
|
+
{
|
|
798
|
+
"value": key if isinstance(key, str) else str(key),
|
|
799
|
+
"description": val if isinstance(val, str) else str(val),
|
|
800
|
+
}
|
|
801
|
+
for key, val in block["criteria"].items()
|
|
802
|
+
]
|
|
803
|
+
if out_type == "score" and isinstance(block.get("levels"), list):
|
|
804
|
+
question["levels"] = block["levels"]
|
|
805
|
+
return question
|
|
806
|
+
|
|
807
|
+
|
|
808
|
+
def _carries_image(node: Any) -> bool:
|
|
809
|
+
"""True when a pack value is an image part. Packs are text only."""
|
|
810
|
+
if isinstance(node, dict):
|
|
811
|
+
kind = node.get("type")
|
|
812
|
+
if kind in {"input_image", "image", "image_url"}:
|
|
813
|
+
return True
|
|
814
|
+
if "image_url" in node or "input_image" in node:
|
|
815
|
+
return True
|
|
816
|
+
return any(_carries_image(value) for value in node.values())
|
|
817
|
+
if isinstance(node, list):
|
|
818
|
+
return any(_carries_image(value) for value in node)
|
|
819
|
+
return False
|
|
820
|
+
|
|
821
|
+
|
|
822
|
+
def decisions_request(prompt: str) -> tuple[dict, dict]:
|
|
823
|
+
"""Pack prompt -> (original pack object, POST /v1/decisions body)."""
|
|
824
|
+
parsed = _pack_prompt(prompt)
|
|
825
|
+
if _carries_image(parsed.get("state")) or _carries_image(parsed.get("questions")):
|
|
826
|
+
raise ValueError(
|
|
827
|
+
"decisions input carries an image part; packs are text only")
|
|
828
|
+
name, block = _answer_block(parsed)
|
|
829
|
+
return parsed, {
|
|
830
|
+
"model": _DECISIONS_MODEL,
|
|
831
|
+
"input": json.dumps(parsed["state"]),
|
|
832
|
+
"questions": [_translate_question(name, block)],
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
|
|
836
|
+
def _decision_block(payload: dict) -> dict:
|
|
837
|
+
answers = payload.get("answers")
|
|
838
|
+
if not isinstance(answers, list) or not answers:
|
|
839
|
+
raise ValueError("decisions response has no answers")
|
|
840
|
+
named = next(
|
|
841
|
+
(a for a in answers if isinstance(a, dict) and a.get("name") == "answer"),
|
|
842
|
+
None)
|
|
843
|
+
block = named if named is not None else next(
|
|
844
|
+
(a for a in answers if isinstance(a, dict)), None)
|
|
845
|
+
if not isinstance(block, dict):
|
|
846
|
+
raise ValueError("decisions answer is not an object")
|
|
847
|
+
return block
|
|
848
|
+
|
|
849
|
+
|
|
850
|
+
def _predicate_label(probability: float, question: Optional[dict],
|
|
851
|
+
threshold: Optional[float]) -> str:
|
|
852
|
+
pos, neg = _noul_labels(question)
|
|
853
|
+
cut = _NOUL_THRESHOLD if threshold is None else threshold
|
|
854
|
+
return pos if probability >= cut else neg
|
|
855
|
+
|
|
856
|
+
|
|
857
|
+
class DecisionsBackend:
|
|
858
|
+
"""POST a typed pack item to OpenAI ``/v1/decisions``.
|
|
859
|
+
|
|
860
|
+
The pack body is ``{state, questions: {answer: {type, instructions,
|
|
861
|
+
criteria}}}``. This translates it. ``noul`` and yes-no become a
|
|
862
|
+
predicate. Choice ``criteria`` become ``choices`` of ``{value,
|
|
863
|
+
description}``. The model is ``gpt-6-luna``.
|
|
864
|
+
|
|
865
|
+
``KIND`` stays ``typed`` so a typed pack is not a kind mismatch. The
|
|
866
|
+
registry kind that selects this class is ``decisions``.
|
|
867
|
+
|
|
868
|
+
A predicate probability is P(true), mapped with the same 0.50 cut and
|
|
869
|
+
the same template labels as the laya noul path. A refusal is not a
|
|
870
|
+
label. When the response has no usage object, tokens are an estimate
|
|
871
|
+
of the input and ``estimated`` is true.
|
|
872
|
+
"""
|
|
873
|
+
|
|
874
|
+
KIND = "typed"
|
|
875
|
+
|
|
876
|
+
def __init__(self, *, name: str, base_url: str, path: str,
|
|
877
|
+
auth: Optional[dict] = None, usage: Optional[dict] = None,
|
|
878
|
+
price: Optional[dict] = None, egress: str = "external",
|
|
879
|
+
noul_threshold: Optional[float] = None, transport=None):
|
|
880
|
+
self.name = name
|
|
881
|
+
self.base_url = base_url.rstrip("/")
|
|
882
|
+
self.path = path
|
|
883
|
+
self.auth = auth
|
|
884
|
+
self.usage = usage or {}
|
|
885
|
+
self.price = price
|
|
886
|
+
self.egress = egress
|
|
887
|
+
self.noul_threshold = noul_threshold
|
|
888
|
+
self.transport = transport
|
|
889
|
+
self.calls = 0
|
|
890
|
+
|
|
891
|
+
def missing_creds(self) -> list[str]:
|
|
892
|
+
if not self.auth:
|
|
893
|
+
return []
|
|
894
|
+
env = self.auth.get("env") or ""
|
|
895
|
+
if not os.environ.get(env):
|
|
896
|
+
return [f"{env} (provider {self.name})"]
|
|
897
|
+
return []
|
|
898
|
+
|
|
899
|
+
def secrets(self) -> list[tuple[str, str, str]]:
|
|
900
|
+
if not self.auth:
|
|
901
|
+
return []
|
|
902
|
+
env = self.auth.get("env") or ""
|
|
903
|
+
value = os.environ.get(env) or ""
|
|
904
|
+
if not value:
|
|
905
|
+
return []
|
|
906
|
+
return [(value, env, self.auth.get("scheme") or "")]
|
|
907
|
+
|
|
908
|
+
def generate(self, model_id: str, prompt: str, system: str = "") -> BackendResult:
|
|
909
|
+
t0 = time.perf_counter()
|
|
910
|
+
secrets = self.secrets()
|
|
911
|
+
try:
|
|
912
|
+
parsed, body = decisions_request(prompt)
|
|
913
|
+
input_text = body["input"]
|
|
914
|
+
endpoint = self.base_url + self.path
|
|
915
|
+
headers = {"Content-Type": "application/json"}
|
|
916
|
+
if self.auth:
|
|
917
|
+
env = self.auth["env"]
|
|
918
|
+
value = os.environ.get(env) or ""
|
|
919
|
+
scheme = self.auth.get("scheme") or ""
|
|
920
|
+
token = f"{scheme} {value}".strip() if scheme else value
|
|
921
|
+
headers[self.auth["header"]] = token
|
|
922
|
+
raw_body = json.dumps(body).encode()
|
|
923
|
+
self.calls += 1
|
|
924
|
+
if self.transport is not None:
|
|
925
|
+
status, reason, raw = self.transport(endpoint, headers, raw_body)
|
|
926
|
+
else:
|
|
927
|
+
req = urllib.request.Request(
|
|
928
|
+
endpoint, data=raw_body, headers=headers, method="POST")
|
|
929
|
+
try:
|
|
930
|
+
with urllib.request.urlopen(req, timeout=60) as resp:
|
|
931
|
+
status, reason, raw = resp.status, resp.reason, resp.read()
|
|
932
|
+
except urllib.error.HTTPError as exc:
|
|
933
|
+
status, reason, raw = exc.code, exc.reason, exc.read()
|
|
934
|
+
if status < 200 or status >= 300:
|
|
935
|
+
return BackendResult(
|
|
936
|
+
text="", tokens_in=0, tokens_thought=0, tokens_out=0,
|
|
937
|
+
latency_s=round(time.perf_counter() - t0, 6),
|
|
938
|
+
estimated=False,
|
|
939
|
+
error=_redact(f"HTTP {status} {reason}", secrets))
|
|
940
|
+
payload = json.loads(raw.decode() or "{}")
|
|
941
|
+
if not isinstance(payload, dict):
|
|
942
|
+
raise ValueError("decisions response is not an object")
|
|
943
|
+
block = _decision_block(payload)
|
|
944
|
+
question = _answer_block(parsed)[1]
|
|
945
|
+
text, p_true = _render_decision(
|
|
946
|
+
block, question, self.noul_threshold)
|
|
947
|
+
usage = payload.get("usage") if isinstance(payload.get("usage"), dict) else None
|
|
948
|
+
in_path = self.usage.get("in") or "usage.input_tokens"
|
|
949
|
+
out_path = self.usage.get("out") or "usage.output_tokens"
|
|
950
|
+
tin = _dig(payload, in_path) if usage is not None else None
|
|
951
|
+
tout = _dig(payload, out_path) if usage is not None else None
|
|
952
|
+
if usage is not None and tin is not None:
|
|
953
|
+
estimated = False
|
|
954
|
+
tokens_in = int(tin)
|
|
955
|
+
tokens_out = int(tout or 0)
|
|
956
|
+
else:
|
|
957
|
+
estimated = True
|
|
958
|
+
tokens_in = estimate_tokens(input_text)
|
|
959
|
+
tokens_out = 0
|
|
960
|
+
text = _redact(text, secrets)
|
|
961
|
+
except Exception as exc:
|
|
962
|
+
return BackendResult(
|
|
963
|
+
text="", tokens_in=0, tokens_thought=0, tokens_out=0,
|
|
964
|
+
latency_s=round(time.perf_counter() - t0, 6),
|
|
965
|
+
estimated=False, error=_redact(str(exc), secrets))
|
|
966
|
+
return BackendResult(
|
|
967
|
+
text=text,
|
|
968
|
+
tokens_in=tokens_in,
|
|
969
|
+
tokens_thought=0,
|
|
970
|
+
tokens_out=tokens_out,
|
|
971
|
+
latency_s=round(time.perf_counter() - t0, 6),
|
|
972
|
+
estimated=estimated,
|
|
973
|
+
)
|
|
974
|
+
|
|
975
|
+
|
|
976
|
+
def _refusal_reason(block: dict) -> str:
|
|
977
|
+
for key in ("reason", "message", "explanation"):
|
|
978
|
+
value = block.get(key)
|
|
979
|
+
if isinstance(value, str) and value.strip():
|
|
980
|
+
return value
|
|
981
|
+
name = block.get("name")
|
|
982
|
+
if isinstance(name, str) and name.strip():
|
|
983
|
+
return name
|
|
984
|
+
return "refusal"
|
|
985
|
+
|
|
986
|
+
|
|
987
|
+
def _render_decision(block: dict, question: dict,
|
|
988
|
+
threshold: Optional[float]) -> tuple[str, Optional[float]]:
|
|
989
|
+
"""Result text, and p_true when the answer was a predicate.
|
|
990
|
+
|
|
991
|
+
A refusal has no ``answer`` key, so the runner scores it as a parse
|
|
992
|
+
failure. The reason stays in the text. Choice keeps the probabilities
|
|
993
|
+
beside a flat answer object the scorer can read.
|
|
994
|
+
"""
|
|
995
|
+
if block.get("type") == "score":
|
|
996
|
+
return json.dumps({"refusal": "unsupported answer type: score"}), None
|
|
997
|
+
if block.get("type") == "refusal":
|
|
998
|
+
return json.dumps({"refusal": _refusal_reason(block)}), None
|
|
999
|
+
if block.get("type") == "predicate" or (
|
|
1000
|
+
block.get("type") is None and "probability" in block):
|
|
1001
|
+
p_true = float(block["probability"])
|
|
1002
|
+
label = _predicate_label(p_true, question, threshold)
|
|
1003
|
+
return json.dumps({"answer": label, "p_true": p_true}), p_true
|
|
1004
|
+
if block.get("type") == "choice" or (
|
|
1005
|
+
block.get("type") is None and "choice" in block):
|
|
1006
|
+
label = block.get("choice")
|
|
1007
|
+
if not isinstance(label, str) or not label:
|
|
1008
|
+
raise ValueError("decisions choice has no label")
|
|
1009
|
+
flat = json.dumps({"answer": label})
|
|
1010
|
+
extra = {}
|
|
1011
|
+
if "probabilities" in block:
|
|
1012
|
+
extra["probabilities"] = block["probabilities"]
|
|
1013
|
+
if "confidence" in block:
|
|
1014
|
+
extra["confidence"] = block["confidence"]
|
|
1015
|
+
if extra:
|
|
1016
|
+
return flat + "\n" + json.dumps(extra), None
|
|
1017
|
+
return flat, None
|
|
1018
|
+
raise ValueError("decisions answer is not a choice, predicate, or refusal")
|
|
@@ -52,14 +52,18 @@ def _check(name: str, entry: dict) -> dict:
|
|
|
52
52
|
if unknown:
|
|
53
53
|
raise RegistryError(f"{name}: unknown keys {sorted(unknown)}")
|
|
54
54
|
kind = entry.get("kind")
|
|
55
|
-
if kind not in {"typed", "semantic"}:
|
|
56
|
-
raise RegistryError(f"{name}: kind must be typed or
|
|
55
|
+
if kind not in {"typed", "semantic", "decisions"}:
|
|
56
|
+
raise RegistryError(f"{name}: kind must be typed, semantic, or decisions")
|
|
57
57
|
base = entry.get("base_url")
|
|
58
58
|
if not isinstance(base, str) or not base.strip():
|
|
59
59
|
raise RegistryError(f"{name}: base_url is required")
|
|
60
60
|
path = entry.get("path")
|
|
61
61
|
if path is None:
|
|
62
|
-
path =
|
|
62
|
+
path = {
|
|
63
|
+
"typed": "/v1/systemone",
|
|
64
|
+
"semantic": "/v1/chat/completions",
|
|
65
|
+
"decisions": "/v1/decisions",
|
|
66
|
+
}[kind]
|
|
63
67
|
if not isinstance(path, str) or not path.startswith("/"):
|
|
64
68
|
raise RegistryError(f"{name}: path must start with /")
|
|
65
69
|
egress = entry.get("egress", "external")
|
|
@@ -32,6 +32,7 @@ from swarph_cli.bench.backends import (
|
|
|
32
32
|
SubscriptionBackend,
|
|
33
33
|
TypedHttpBackend,
|
|
34
34
|
HttpBackend,
|
|
35
|
+
DecisionsBackend,
|
|
35
36
|
)
|
|
36
37
|
from swarph_cli.bench.pack import PackError, load_pack, slugify_theme, validate_schema
|
|
37
38
|
from swarph_cli.bench.providers import RegistryError, load_registry, registry_path
|
|
@@ -221,6 +222,24 @@ def _default_packs_dir() -> Path:
|
|
|
221
222
|
return cwd_packs
|
|
222
223
|
|
|
223
224
|
|
|
225
|
+
def _provider_arm(name: str, entry: dict, noul_threshold):
|
|
226
|
+
"""The registry arm for ``provider:<name>``.
|
|
227
|
+
|
|
228
|
+
``kind = "decisions"`` is the OpenAI Decisions translator. ``typed`` and
|
|
229
|
+
``semantic`` stay on :class:`HttpBackend`, which posts a typed pack body
|
|
230
|
+
unchanged.
|
|
231
|
+
"""
|
|
232
|
+
common = dict(
|
|
233
|
+
name=name, base_url=entry["base_url"], path=entry["path"],
|
|
234
|
+
auth=entry.get("auth"), usage=entry.get("usage"),
|
|
235
|
+
price=entry.get("price"), egress=entry["egress"],
|
|
236
|
+
noul_threshold=noul_threshold,
|
|
237
|
+
)
|
|
238
|
+
if entry["kind"] == "decisions":
|
|
239
|
+
return DecisionsBackend(**common)
|
|
240
|
+
return HttpBackend(kind=entry["kind"], **common)
|
|
241
|
+
|
|
242
|
+
|
|
224
243
|
# ── dispatch ───────────────────────────────────────────────────────────────
|
|
225
244
|
|
|
226
245
|
def _cmd_run(args) -> int:
|
|
@@ -249,11 +268,8 @@ def _cmd_run(args) -> int:
|
|
|
249
268
|
entry = registry.get(spec.provider)
|
|
250
269
|
if entry is None:
|
|
251
270
|
continue
|
|
252
|
-
spec.arm =
|
|
253
|
-
|
|
254
|
-
path=entry["path"], auth=entry.get("auth"), usage=entry.get("usage"),
|
|
255
|
-
price=entry.get("price"), egress=entry["egress"],
|
|
256
|
-
noul_threshold=pack.get("noul_threshold"))
|
|
271
|
+
spec.arm = _provider_arm(
|
|
272
|
+
spec.provider, entry, pack.get("noul_threshold"))
|
|
257
273
|
allow_egress = set(args.allow_egress or [])
|
|
258
274
|
runnable, warnings = preflight(
|
|
259
275
|
specs, backends, pack=pack, allow_egress=allow_egress,
|