swarph-cli 0.69.2__tar.gz → 0.69.4__tar.gz

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