swarph-cli 0.66.0__tar.gz → 0.67.0__tar.gz

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